Ferramentas de Sniffing - Criação, Captura e Análise de Pacotes
Uma ferramenta de sniffing (ou analisador de pacotes) funciona como um "grampo" digital, interceptando e registrando o tráfego que passa por uma interface de rede. Elas são essenciais para o diagnóstico de problemas de conexão, auditoria de segurança e estudo de protocolos, pois permitem visualizar exatamente o que está sendo transmitido entre dois pontos. Normalmente, essas ferramentas operam colocando a placa de rede em modo promíscuo, o que permite capturar pacotes mesmo que o destino final não seja a máquina que está monitorando, sendo frequentemente desenvolvidas em linguagens como C (pela performance de baixo nível) ou Python (pela flexibilidade na análise de dados).
Ferramentas de Snifing
TCPDUMP
O tcpdump é a ferramenta de linha de comando para captura de pacotes. Em ambientes de servidor ou containers (onde não há interface gráfica), ele é o padrão para capturar tudo o que acontece na rede e o salva em um arquivo de extensão .pcap (Packet Capture).
tcpdump -i any -w /app/laboratorio.pcap
- -i any: Escuta em todas as interfaces de rede do roteador.
- -w: Escreve (salva) o resultado em um arquivo para análise posterior em outras ferramentas.
Além de gravar o tráfego total, o tcpdump permite aplicar filtros em tempo real para capturar apenas pacotes específicos. Isso é essencial em redes com muito tráfego. Por exemplo, filtrar por Porta Específica. Útil para monitorar apenas um serviço (ex: apenas tráfego Web ou apenas Banco de Dados).
# Escuta apenas o tráfego na porta 80 (HTTP)
tcpdump -i any port 80
Filtrar por Endereço IP. Ideal para isolar o que um cliente específico está fazendo ou o que está chegando em um servidor.
# Captura apenas pacotes que tenham o IP 10.0.2.2 como origem ou destino
tcpdump -i any host 10.0.2.2
Filtrar por Origem e Destino (Fluxo Direcionado). Para ser ainda mais específico e ver apenas o que sai de um ponto A para um ponto B.
# Captura pacotes que saem do Cliente (src) para o Servidor (dst)
tcpdump -i any src 10.0.2.2 and dst 10.0.1.2
NGREP
O ngrep (Network Grep) aplica a lógica de busca de texto do comando grep diretamente nos pacotes que estão trafegando na placa de rede em tempo real. Ele combina a capacidade de captura do tcpdump com a facilidade de busca de texto do comando grep. Ele permite que você escute a rede e filtre apenas os pacotes que contêm palavras-chave específicas.
Comando Principal (Monitoramento de Telnet).
ngrep -q -W byline port 23
- -q (Quiet): Oculta os caracteres de controle e metadados irrelevantes, focando apenas no conteúdo útil.
- -W byline: Formata a saída quebrando as linhas de forma legível, simulando como o texto aparece na tela do usuário.
- port 23: Filtra o tráfego do protocolo Telnet.
Perceba que é possível visualizar o tráfego e captrar o login e senha de usuário.
Captura de Padrões Específicos (Filtro de Conteúdo).
# Busca apenas requisições GET na porta 80
ngrep -q -W byline "^GET" port 80
# Busca qualquer pacote que contenha a palavra "aluno" ou "lab123"
ngrep -i "aluno|lab123" any
- "^GET": Usa expressões regulares para identificar o início de uma requisição web.
- -i: Ignora a diferença entre maiúsculas e minúsculas (case-insensitive).
Wireshark
O Wireshark é a ferramenta da análise de redes com interface gráfica. Enquanto o tcpdump e o ngrep nos dão recortes rápidos no terminal, o Wireshark nos oferece o "Raio-X" completo de toda a comunicação. No nosso laboratório, como os containers não possuem interface gráfica, utilizamos a estratégia de capturar o tráfego no roteador e exportar o arquivo .pcap para análise na nossa máquina real.
Ao iniciar o Wireshark, a primeira coisa que fazemos é carregar o arquivo que gravamos anteriormente. Diferente de um editor de texto, o Wireshark entende a estrutura binária dos pacotes e já os organiza cronologicamente.
A força do Wireshark está na sua organização em três seções principais, que permitem navegar do geral para o específico:
-
Lista de Pacotes (Topo): Cada linha é um pacote. As cores ajudam a identificar o protocolo (ex: azul para DNS, verde para HTTP, roxo para TCP).
-
Detalhes do Pacote (Meio): O Wireshark decodifica as camadas (Ethernet, IP, TCP) e permite que você abra cada uma para ver os campos, como endereços e portas.
-
Bytes do Pacote (Base): Mostra o dado bruto em hexadecimal e ASCII. É a prova final do que realmente passou pelo fio.
Como o TCP envia dados em muitos pacotes pequenos, ler um por um é difícil. A função Follow TCP Stream recria a conversa inteira em uma única janela, como se fosse um chat.
Para fazer isso, clicamos com o botão direito em um pacote MariaDB e selecionamos Follow -> TCP Stream.
Ao abrir o fluxo TCP do MariaDB, a vulnerabilidade do protocolo fica exposta. O Wireshark exibe em cores diferentes o que o cliente enviou e o que o servidor respondeu. Conseguimos ver as queries envidas do cliente para servidor
Scapy
!pip install scapy
Scapy é uma ferramenta poderosa para análise e manipulação de pacotes de rede. Através dela conseguimos criar as ferramentas sniffers como wireshark e similares.
Normalmente, quando um pacote chega na placa de rede, o Kernel do Linux faz todo o trabalho. Ele abre a camada Ethernet, depois a IP, depois a TCP e entrega apenas o "conteúdo" (o dado) para o aplicativo.
O Scapy utiliza Raw Sockets. Isso permite que ele ignore o processamento padrão do Kernel e pegue o pacote "cru".
- Modo Promíscuo: O Scapy coloca a placa de rede em um estado onde ela não descarta nada, mesmo pacotes que não são para aquele computador.
- Cópia de Fluxo: O pacote continua indo para o destino original, mas o Scapy faz uma cópia binária exata para análise.
from scapy.all import (
# Camadas de protocolo
Ether, IP, IPv6, TCP, UDP, ICMP, Raw,
# Funções de envio
send, sendp, sr1, srp1, sr, srp,
# Funções de captura
sniff, rdpcap, wrpcap,
# Utilitários
ls, conf, hexdump, get_if_list, get_if_addr,
# Tipos especiais
DNS, DNSQR,
)
import time
import sys
No Scapy, um pacote não é uma string de texto, mas um objeto composto por fatias.
A estrutura visual que o Scapy usa é:
Ether() / IP() / TCP() / Raw()
- Ether(): Camada 2 (Endereços MAC).
- IP(): Camada 3 (Endereços IP, TTL, etc).
- TCP() / UDP(): Camada 4 (Portas, Flags, Sequência).
- Raw(): Onde os dados reais (payload) residem.
Usando o CMD do Scapy
basta digitar scapy no terminal. Isso abre um shell Python onde você pode interagir com pacotes em tempo real:
- ls(): Lista todos os protocolos suportados (são centenas!).
- ls(IP): Mostra todos os campos que existem dentro da camada IP.
- p = IP(dst="10.0.1.2")/TCP(dport=80): Cria um pacote manualmente (Forge).
- p.show(): Mostra a estrutura detalhada e "mastigada" do pacote.
Vamos agora criar um sniffer que tenta capturar um pacote de um serviço rodando no ambiente. Para isso você precisa iniciar o ambiente do aluno do laboratório 2.
ls(IP)
version : BitField (4 bits) = ('4') ihl : BitField (4 bits) = ('None') tos : XByteField = ('0') len : ShortField = ('None') id : ShortField = ('1') flags : FlagsField = ('<Flag 0 ()>') frag : BitField (13 bits) = ('0') ttl : ByteField = ('64') proto : ByteEnumField = ('0') chksum : XShortField = ('None') src : SourceIPField = ('None') dst : DestIPField = ('None') options : PacketListField = ('[]')ls(TCP)
sport : ShortEnumField = ('20') dport : ShortEnumField = ('80') seq : IntField = ('0') ack : IntField = ('0') dataofs : BitField (4 bits) = ('None') reserved : BitField (3 bits) = ('0') flags : FlagsField = ('<Flag 2 (S)>') window : ShortField = ('8192') chksum : XShortField = ('None') urgptr : ShortField = ('0') options : TCPOptionsField = ("b''")Listar Interfaces Disponíveis
for iface in get_if_list():
print(f" → {iface}")
Criando Pacotes com Scapy
Pacote IP Simples
pkt_ip = IP(dst="8.8.8.8")
print(f" Destino: {pkt_ip.dst}")
print(f" TTL padrão: {pkt_ip.ttl}")
print(f" Protocolo: {pkt_ip.proto}")
Pacote ICMP (Ping)
Pacote UDP com payloadpkt_ping = IP(dst="8.8.8.8", ttl=64) / ICMP() pkt_ping.show()
pkt_udp = IP(src="192.168.1.10", dst="192.168.1.20") \
/ UDP(sport=5000, dport=9999) \
/ Raw(load="Olá, Scapy!")
pkt_udp.show()
Acessando campos individualmente
print(f" IP src : {pkt_udp[IP].src}") print(f" IP dst : {pkt_udp[IP].dst}") print(f" UDP sport: {pkt_udp[UDP].sport}") print(f" UDP dport: {pkt_udp[UDP].dport}") print(f" Payload : {pkt_udp[Raw].load}")Modificando Uma Cópia dos Pacotes
pkt_mod = pkt_udp.copy() pkt_mod[IP].dst = "10.0.0.1" pkt_mod[UDP].dport = 8080 pkt_mod[Raw].load = b"Mensagem modificada" print(f" Novo dst: {pkt_mod[IP].dst}, nova porta: {pkt_mod[UDP].dport}") print(f" Payload: {pkt_mod[Raw].load}")
Visualizando Pacotes
show()
pkt_udp.show()
###[ IP ]###
version = 4
ihl = None
tos = 0x0
len = None
id = 1
flags =
frag = 0
ttl = 64
proto = udp
chksum = None
src = 192.168.1.10
dst = 192.168.1.20
\options \
###[ UDP ]###
sport = commplex_main
dport = distinct
len = None
chksum = None
###[ Raw ]###
load = b'Ol\xc3\xa1, Scapy!'
show2() - visualização com campos preenchidos
pkt_udp.show2()
###[ IP ]###
version = 4
ihl = 5
tos = 0x0
len = 40
id = 1
flags =
frag = 0
ttl = 64
proto = udp
chksum = 0xf755
src = 192.168.1.10
dst = 192.168.1.20
\options \
###[ UDP ]###
sport = commplex_main
dport = distinct
len = 20
chksum = 0xd49c
###[ Raw ]###
load = b'Ol\xc3\xa1, Scapy!'
Summary - Resumo do pacote em uma linha
pkt_udp.summary()
'IP / UDP 192.168.1.10:commplex_main > 192.168.1.20:distinct / Raw'
hexdump() — bytes do pacote em hexa + ASCII
hexdump(pkt_udp)
0000 45 00 00 28 00 01 00 00 40 11 F7 55 C0 A8 01 0A E..(....@..U....
0010 C0 A8 01 14 13 88 27 0F 00 14 D4 9C 4F 6C C3 A1 ......'.....Ol..
0020 2C 20 53 63 61 70 79 21 , Scapy!
Acessando cabeçalhos pelo índice ou por nome
print(f" pkt[IP] → src={pkt_udp[IP].src}, dst={pkt_udp[IP].dst}")
print(f" pkt[UDP] → sport={pkt_udp[UDP].sport}, dport={pkt_udp[UDP].dport}")
print(f" pkt[Raw] → load={pkt_udp[Raw].load}")
Verificar se uma camada existe no pacote
print(f"\n Tem camada TCP? {TCP in pkt_udp}") print(f" Tem camada UDP? {UDP in pkt_udp}") print(f" Tem camada Raw? {Raw in pkt_udp}")
Enviando Pacotes
send() — envia pacote sem aguardar resposta
dst_ip = "127.0.0.1" pkt_ping = IP(dst=dst_ip) / ICMP() print(f"Enviando ping para {dst_ip}...") send(pkt_ping, verbose=0)envia e aguarda resposta (ping)
dst_ip = "127.0.0.1"
pkt_ping = IP(dst=dst_ip) / ICMP()
print(f"Enviando ping para {dst_ip}...")
resp = sr1(pkt_ping, timeout=2, verbose=0)
resp.summary()
'IP / ICMP 127.0.0.1 > 127.0.0.1 echo-reply 0'
Capturando Pacotes - Sniff
Parâmetros sniff()
- count=N → captura exatamente N pacotes (0 = infinito)
- timeout=T → para após T segundos
- iface='eth0' → captura apenas nessa interface
- filter='...' → filtro BPF (mesma sintaxe do tcpdump)
- prn=função → callback chamado para cada pacote capturado
- store=False → não armazena na memória (use com prn)
- lfilter=lambda → filtro Python (mais flexível que BPF)
Exemplos de Filtros - BPF:
- 'tcp' → apenas TCP
- 'udp port 9999' → UDP na porta 9999
- 'host 192.168.1.10' → apenas host específico
- 'tcp and dst port 80' → TCP indo para porta 80
- 'icmp' → apenas ICMP
- 'not arp' → ignora ARP
- 'tcp port 80 or tcp port 443' → HTTP e HTTPS
Digite o comando abaixo. Ele coloca o Scapy em modo de espera escutando a porta 23:
pacote_unico = sniff(filter="tcp port 23", count=1)
O Scapy não retorna o pacote diretamente, mas sim um objeto do tipo PacketList.
> pacote
<Sniffed: TCP:1 UDP:0 ICMP:0 Other:0>
Você pode ver uma representação simplificada da "pilha" de protocolos com o seguinte comando.
>>> pacote.summary()
Ether / IP / TCP 10.0.2.2:43688 > 10.0.1.2:telnet PA / Raw
Esse script, é basicamente o tcpdump.
from scapy.all import sniff, IP, TCP
def analisar_pacote(pacote):
if pacote.haslayer(IP):
ip_origem = pacote[IP].src
ip_destino = pactoe[IP].dst
if pacote.haslayer(TCP):
porta_origem = pacote[TCP].sport
porta_destino = pacote[TCP].dport
print(f"[TCP] {ip_origem}:{porta_origem} ---> {ip_destino}:{porta_destino}")
print("Iniciando o Sniffer do Roteador...")
print("Pressione Ctrl+C para interromper.\n")
# prn=analisar_pacote: Diz ao sniff para jogar cada pacote dentro da nossa função.
# store=0: Extremamente importante! Diz ao Scapy para NÃO guardar os pacotes na RAM,
# caso contrário seu roteador travaria por falta de memória em poucos minutos.
sniff(prn=analisar_pacote, store=0)
Salvando e Lendo Arquivos .pcap
Criando pacotes de exemplo e salvando em PCAP
pacotes_para_salvar = [ IP(src="10.0.0.1", dst="10.0.0.2") / UDP(sport=1001, dport=9999) / Raw(load="Mensagem 1"), IP(src="10.0.0.2", dst="10.0.0.1") / UDP(sport=9999, dport=1001) / Raw(load="Resposta 1"), IP(src="10.0.0.1", dst="10.0.0.2") / TCP(sport=2001, dport=80, flags="S"), IP(src="10.0.0.2", dst="10.0.0.1") / TCP(sport=80, dport=2001, flags="SA"), IP(src="10.0.0.1", dst="10.0.0.2") / ICMP() / Raw(load="ping"), ] arquivo_pcap = "captura_lab.pcap" wrpcap(arquivo_pcap, pacotes_para_salvar) print(f" {len(pacotes_para_salvar)} pacotes salvos em '{arquivo_pcap}'")Lendo arquivos .pcap
pkts_lidos = rdpcap(arquivo_pcap) print(f" Total de pacotes lidos: {len(pkts_lidos)}") print("\n Sumário de cada pacote:") for i, pkt in enumerate(pkts_lidos): print(f" [{i+1}] {pkt.summary()}")
Total de pacotes lidos: 5
Sumário de cada pacote:
[1] IP / UDP 10.0.0.1:1001 > 10.0.0.2:9999 / Raw
[2] IP / UDP 10.0.0.2:9999 > 10.0.0.1:1001 / Raw
[3] IP / TCP 10.0.0.1:2001 > 10.0.0.2:http S
[4] IP / TCP 10.0.0.2:http > 10.0.0.1:2001 SA
[5] IP / ICMP 10.0.0.1 > 10.0.0.2 echo-request 0 / Raw
Praticando ...
EXERCÍCIO 1 — Criação de pacotes:
- a) Crie um pacote ICMP Echo Request para o endereço "1.1.1.1" com TTL=128 e exiba com show2().
- b) Crie um pacote UDP com src="192.168.0.5", dst="192.168.0.10", porta origem 6000, porta destino 6001 e payload "Exercicio1".
- c) Crie um pacote TCP SYN+FIN para a porta 443 e observe os flags.
EXERCÍCIO 2 — Manipulação de campos:
- a) A partir do pacote do exercício 1b, modifique apenas o payload para "Alterado" sem recriar o pacote.
- b) Exiba o hexdump antes e depois da modificação.
EXERCÍCIO 3 — Captura e análise:
- a) Capture 10 pacotes IP com sniff() e liste as 3 IPs de origem mais frequentes.
- b) Use um filtro BPF para capturar apenas tráfego na porta 80 (HTTP) por 15 segundos e conte quantos pacotes têm payload.
EXERCÍCIO 4 — PCAP:
- a) Capture 20 pacotes com sniff() e salve em "minha_captura.pcap".
- b) Reabra o arquivo e separe os pacotes por protocolo (TCP/UDP/ICMP).
- c) Calcule o tamanho médio dos pacotes TCP capturados.
EXERCÍCIO 5 — Chat Scapy:
- a) Execute o servidor (chat_server_scapy.py) em um terminal.
- b) Execute o cliente (chat_client_scapy.py) em outro terminal.
- c) Troque mensagens e observe os pacotes capturados.
- d) Capture a sessão de chat e analise o arquivo PCAP no Wireshark.