Manual

do

Maker

.

com

Forense, Redes e MCUs - TCPDump com filtros e expressões

Forense, Redes e MCUs - TCPDump com filtros e expressões

Mesmo que você já tenha usado tcpdump, talvez não tenha ido além do essencial. Cito isso porque o essencial muitas vezes é o suficiente para diversas necessidades. Quando fazendo análise em tempo real, é necessário expandir ao máximo as possibilidades e filtros podem omitir informações cruciais e daí se torna ainda mais compreensível a utilização do sniffer com o mínimo possível de filtros. No caso de dados coletados para perícia, pode-se abusar desses filtros e fazer diversas análises diferentes, inclusive para auxiliar na documentação (laudo ou parecer), deixando a informação tão sucinta quanto possível.

Após alguns exemplos e talvez até dicas que você não conhecia, vamos ver um pouco de interceptação utilizando um dispositivo TAP para fazer a escuta do fluxo sem que haja manifestação na rede, então analisar o tráfego gerado entre uma comunicação com um Arduino utilizando ethernet shield e melhorar a comunicação baseando-se nessa análise. Se você ainda não conheceu o produto, poderá ver uma interceptação de um um ethernet shield com Arduino nesse post.

Trata-se de um dispositivo de escuta de rede multi-propósito e, o propósito depende da área de aplicação. Nesse post vou utilizar o mesmo sketch da interceptação do Arduino, mas vou dar uma simplificada nele para abordar alguns conceitos de rede.

Vou iniciar o assunto pela coleta pericial que é a parte menos complicada por ser a fase inicial. Vamos desde os primeiros conceitos para evitar a nulidade da Cadeia de Custódia.

Concluo afirmando que esse tutorial serve para qualquer sistema operacional que rode tcpdump, bastando mudar o parâmetro da interface, mas para os comandos bash e script recomendo a instalação do cygwin no Windows. Se tiver dúvidas quanto ao nome da interface, use o parâmetro '-D' para identificá-las. Mais abaixo coloquei um exemplo detalhado, você não terá desculpa para não usar essa ferramenta!

Prólogo - Procedimento de coleta pericial

Antecedendo "à peça", o perito normalmente tem que juntar todas as informações circunstanciais para montar uma linha do tempo em seu parecer ou laudo (dependendo se assistente técnico ou perito sob qualquer designação). Portanto nesse primeiro momento deve-se descrever tudo:

  • confirmar a data/hora do computador que fará a coleta (FUNDAMENTAL, senão, adeus perícia)
  • data/hora do inicio das operações.
  • computador que será utilizado para a coleta (sistema, foto, serial).
  • meio (fotos de cabos, conexões, TAP).
  • descrição da ferramenta empregada (isso faz-se no momento da composição do documento final).

Todo o processo deve ser gravado SEM MOSTRAR O ROSTO. Antes de iniciar qualquer passo, deve-se dizer em alto e bom som o que será executado:

  • "Estou iniciando a interconexão do dispositivo TAP à estrutura de rede"
  • "Estou interconectando esse computador ao coletor forense"

Se possível, inicie a gravação do desktop previamente ao inicio do processo:

  • "Vou digitar o comando para iniciar a gravação do desktop de forma auxiliar a essa gravação"
  • "Vou migrar a aba do terminal para digitar o comando e iniciar a coleta"

Anota-se então a hora do ENTER para o início da coleta.

Antes de seguir, quero recomendar a leitura desse post onde relato um processo bastante agradável e robusto de fazer a gravação do desktop da maneira ideal. Isso é chamado "screencast". Da forma que exemplifico nesse post, o material ficará rico, por favor, leia-o.

Apenas para concluir essa primeira etapa, gostaria de citar a possibilidade de fazer uma ata notarial, de forma que a coleta venha a ter fé pública certificada por um tabelião. Você pode chamar o tabelião de qualquer cartório para acompanhar o processo descrito e depois ele certifica a autenticidade do processo, mas os detalhes desse procedimento vou deixar para um post escrito pelo Ilmo. Perito Diego Souza.

Daqui em diante o procedimento serve para todos os demais casos.

A coleta deve ser feita em formato bruto

Conhecido tradicionalmente por "modo raw" (ou "cru", na tradução direta de "raw"), esse formato é obrigatório para que se possa apresentar qualquer dado, evitando assim que haja possibilidade de argumentação quanto a manipulação dos dados coletados, ou insinuações de tendenciosidade que levem a anular a validade dos artefatos coletados em rede.

Especificar interface de rede já seria uma forma de manipulação, mas para não coletar manifestação da interface de loopback, não há problema em excluí-la. Desse modo, o primeiro formato básico do comando tcpdump pode ser:

tcpdump host not localhost

Isso exibirá todo o conteúdo diretamente na tela (que é conhecido por stdout ou, "saída padrão" de comandos). Mas esse comando não serve para coleta de dados. Você poderia utilizá-lo previamente para verificar se há algum fluxo na rede, ou para certificar se está na máquina correta, mas esses procedimentos já não são ideais.

Para iniciar uma coleta em modo raw, voce pode utilizar o comando:

tcpdump -w "teste-`echo $(date)|sed -re 's/ /_/g; s/:/-/g'`.raw"

Utilizando a hora que estou escrevendo esse post por exemplo, resultaria em um arquivo com o seguinte formato:

teste-Wed_Jan_13_23-52-05_BRST_2016.raw

Isso certamente casará com o horário da filmagem e gravação do desktop.

Como ou quando finalizar o processo de coleta?

Você deverá ter em mente o quanto de dados basta. Se quer coletar por tamanho, por pacotes ou por tempo. Assim, vou exemplificar todas essas formas de fazê-lo.

Coleta por número de pacotes

Você pode determinar um número de pacotes na coleta simplesmente passando o parâmetro '-c':

tcpdump host not localhost -c 50

Após coletar 50 pacotes, o programa se encerra automaticamente.

Tamanho de pacote

Se quiser determinar a coleta por tamanho de pacote, um script shell pode auxiliá-lo a interromper o processo:

  • Crie o arquivo tcpdumpMon.sh
  • Adicione o seguinte código:
#!/bin/sh

LIMIT="200K"
[ $1 ] || {
    echo "Arg 1 needs to be a filename."
    exit
}
echo "Starting..."
TCPDUMP_PID=`ps ax|egrep 'tcpdump'|egrep -v 'grep'|awk '{print $1}'`
LIMIT_DEC=`echo $LIMIT|sed -re 's/[^[:digit:]]//g'`

while [ true ]; do
    SIZE=`ls -sh $1 2>/dev/null`
    [ $LIMIT_DEC -lt `echo $SIZE|sed -re 's/[^[:digit:]]//g'` ] && {
        kill -9 $TCPDUMP_PID
        echo "Finished."
    }
    sleep 2
done

  • mude as permissões:
chmod 700 tcpdumpMon.sh

Inicie o tcpdump e esse script em seguida, passando o nome do arquivo como parâmetro. Se a captura de pacotes for muito curta, você pode iniciar o script primeiro colocando um sleep na primeira linha e então:

tcpdumpMon.sh meuArquivo &
tcpdump host not localhost -w meuArquivo

Se quiser limitar o tamanho do arquivo para que não exceda ao tamanho pretendido, você pode utilizar o parâmetro '-C', assim qualquer valor excedente será escrito para um novo arquivo, que você poderá descartar.

Esse parâmetro tem o propósito de separar em arquivos de tamanho específico, para facilitar a leitura posterior no caso de a captura ser muito grande, o modo de utilização sugerido é paliativo em relação a um limitador de tamanho para um único pacote de dados pretendido.

A libpcap é a responsável pelos recursos contidos nos programas TCPDump e Wireshark. Essa lib oferece uma quantidade realmente considerável de recursos, não vai dar pra exemplificar tudo em um post, mas vou citar os que considero mais importante.

Ctrl+C

O Ctrl+C no shell interrompe o programa imediatamente. Se quiser interromper o programa da maneira mais simples, aí está.

Interrupção por tempo

Suponhamos que queira capturar X segundos ou minutos de dados. Uma maneira simples de fazê-lo é digitar isso em um segundo shell após iniciar o tcpdump (com exemplo de 10 segundos):

sleep 10 && kill -9 $(ps ax|grep tcpdump|grep -v grep|awk '{print $1}')

Interfaces disponíveis

Logo pensa-se em interfaces ethernet, mas a libpcap oferece captura de dados de outros tipos também. Para ver as disponíveis no sistema, utilize o parâmetro '-D'. Esse tutorial está sendo composto utilizando Linux como plataforma base, mas serve para qualquer outro sistema, bastando mudar o parâmetro de interface, cujo nome você identifica com esse parâmetro.

Como você pode ver, dá pra capturar pacotes inclusive do bluetooth. Utilizando o parâmetro '-i' você pode especificar a interface a ser escutada:

tcpdump -i eth0

Utilizando o parâmetro '-e', você coloca no topo da mensagem o MAC do dispositivo, o que pode ser bastante útil para diversos propósitos. Se estiver querendo monitorar WiFi, é fundamental, mas a vez que salvou minha pele em rede ethernet foi em um CPD em 2006 mais ou menos. Aquela época estava tendo uma epidemia de hardwares utilizando o mesmo MAC e aconteceu justamente de um servidor em uma das redes estar com MAC repetido. Foi a condição mais inesperada, foi necessário análise da camada 7 do modelo OSI e ir descendo até desconfiar de spoofing, mas o caso não podia ser realmente chamado de spoofing, era porquice do fornecedor mesmo. O que acontecia é que os IPs eram diferentes por se tratar de servidores diferentes, mas quando os switches renovavam a tabela ARP a manifestação mais recente entrava na lista e então o outro servidor se tornava indisponível. Analisar pelo MAC ajudou a identificar os IPs que disputavam um espaço na tabela ARP dos switches.

Ver o que está sendo capturado

Não serve para captura forense, mas se estiver coletando dados para qualquer outro tipo de análise que não seja para aplicação judicial, você pode gravar em arquivo enquanto captura com o parâmetro -l:

tcpdump -l |tee dat

Selecionar a direção do fluxo

Agora vamos começar a entrar em filtros. Você pode selecionar a direção a escutar utilizando o parâmetro '-P'. Mas isso tem mais de uma maneira de fazer. Suponhamos que queira escutar o fluxo de saída do host 172.0.0.15:

tcpdump -i any host 172.0.0.15 -P out

Se é fluxo de saída, então quer dizer que a origem é 172.0.0.15. Esse filtro poderia ser aplicado da seguinte maneira:

tcpdump -i any src host 172.0.0.15

O mesmo se aplica para '-P in' e 'dst host'. Também é possível fazer a negativa de uma condição. Olhe que interessante, parece programação em Python:

tcpdump -i any src host not 172.0.0.15

A expressão é bastante clara, nao é coisa de se decorar, apenas basta entender e não sairá mais de sua memória.

Ler dados gravados do tcpdump

O resultado da gravação em arquivo não é texto plano, portanto para lê-lo você também precisará do tcpdump, mas dessa vez utilizando o parâmetro '-r'. O bom de ler a partir de pacotes de dados gravados é que você pode aplicar diferentes tipos de filtros para analisar diferentes situações. Em uma leitura do tráfego em tempo real você fica limitado ao filtro aplicado portanto, sempre que possível, grave os dados em arquivo e faça a análise posteriormente.

Nível de verbosidade

Quanto mais verboso, mais informação se torna disponível. No caso do tcpdump você pode utilizar 3 níveis de verbosidade; de '-v' à '-vvv'.

Exibir dados dos pacotes

Para exibir os dados dos pacotes, você tem diversas combinações possíveis (man tcpdump). Eu uso sempre um modo que exibe o valor em hexa e em ASCII. Quando surge alguma dúvida no formato ASCII (por exemplo, um espaço após o último byte legível), eu confiro o valor pelo hexa (à esquerda). Adicionalmente, defino o tamanho da mensagem a ser exibida no pacote. Inicio com um valor cabalístico de 4192 bytes, mas você pode variar para mais ou para menos, a gosto. Para exibir os dados, utilizo '-XX' e para definir o tamanho, utilizo '-s4192'.

Expressões

Já mostrei mais acima alguma coisa com expressão. Dá pra ir muito, muito além. Por exemplo, um servidor se comunica com vários outros em um CPD e você está interceptando esse servidor a partir do switch dos servers. O servidor se chama BANANA e você deseja analisar apenas a comunicação dele com PERA e UVA. Você pode compor de várias maneiras, mas vou exemplificar apenas a que parece mais simples:

tcpdump -i any host BANANA and host ( PERA or UVA )

Perceba que é fácil errar no filtro e o melhor é utilizá-lo a partir da leitura de um arquivo de captura. Isso porque invés de escrever o filtro como acima, você poderia escrever:

tcpdump -i any host BANANA and host PERA or host UVA

Mas nesse caso casariam duas condições diferentes. Uma seria a comunicaçao de BANANA com PERA __ou__ a segunda, apenas tráfego de UVA.

Diversos protocolos diferentes podem estar trafegando e você pode querer apenas tratar protocolo IP. Então basta informar ao tcpdump que o que lhe interessa é apenas pacotes IP:

tcpdump -i any ip host 172.0.0.15 -vvv -XX -s4192 

Se deseja interpretar apenas tráfego através do gateway (supondo final .1), voce pode fazê-lo especificando o gateway e se desejar, as portas pretendidas:

tcpdump -i any 'gateway 172.0.0.1 and (port ftp or http or 8888)' -vvv -XX -s4192

Você pode desejar exibir o tráfego externo, excluindo qualquer comunicação que esteja ocorrendo na rede local. Para tanto:

tcpdump -i any ip and not net 172.0.0.0

Ou pode omitir redes paralelas se comunicando através de firewalls, basta utilizar o parâmetro 'not net' seguido da rede que deseja excluir da escuta.

Filtrar flags TCP

Você pode querer filtrar por exemplo o início e fim de de uma comunicação (flags SYN e FIN). O comando é um pouco mais elaborado nesse caso, mas não deixa de ser claro:

tcpdump 'tcp[tcpflags] & (tcp-syn|tcp-fin) != 0'

Ou seja, você informa que quer as flags TCP da escuta e dessas flags o tcpdump deve exibir SYN ou FIN cujo valor da flag seja diferente de 0.

Especificar protocolo TCP ou UDP

Você pode ter hosts diferentes utilizando a mesma porta mas um utiliza TCP e outro UDP. Pra piorar, esses hosts passam por um gateway que faz mascaramento NAT, portanto ambos chegam à escuta com o mesmo IP. Sabendo que seu desejo é escutar a porta 8888 UDP, o comando seria algo como:

 tcpdump -i any host 172.0.0.1  and 'udp port 8888' -vvv -XX -s4192

Capturar pacotes determinando um tamanho mínimo ou máximo de bytes

Suponhamos que você tenha uma comunicação que envolva diversos pacotes no handshake, mas te interessa especificamente o pacote maior (200 bytes por exemplo), que carrega toda a transação de um determinado evento. Você pode capturar apenas esse tipo de pacote definindo o tamanho pretendido, da seguinte maneira:

tcpdump -i any host not 127.0.0.1 and host 172.0.0.15 and 'tcp port 8888 and ip[2:2] > 199' -vvv -XX -s300 

A porção 'ip[2:2]' se refere ao header do protocolo IP:

Trata-se do Total Lenght, que é o tamanho total do pacote, incluindo o header do protocolo IP.

Exibir comunicação ICMP que não seja do tipo 0 e 8

O comando ping (echo request) utiliza o ICMP tipo 8. Existem diversos tipos no protocolo ICMP e voce pode querer filtrar qualquer comunicação ICMP que não seja ping. Para tal:

 tcpdump 'icmp[icmptype] != icmp-echo and icmp[icmptype] != icmp-echoreply'

Só pra deixar um pouco mais claro, caso você não seja programador; icmp[icmptype] é o valor do tipo ICMP que está trafegando e '!= icmp-echo and icmp[icmptype] != icmp-echoreply' significa que esse valor deve ser diferente do tipo 0 e 8 (echo reply e echo request).

Os 8 bits do header TCP

Você não precisa ser especialista em redes de computadores se não for sua área, mas não é nada mal ao menos conhecer o header do protocolo TCP. Esses 8 bits representam as flags da comunicação

CWR | ECE | URG | ACK | PSH | RST | SYN | FIN

Quando você lê os dados de uma linha da escuta, lá estará a respectiva flag:

Como você pode ver nessa mensagem, o inicio mostra o timestamp e indica TTL (Time To Live), a flag DF (Don't Fragment), diz que é um protocolo TCP (6) e seu tamanho. Ou seja, no começo da linha já existem muitas informações importantes para identificar o comportamento da comunicação.

Já na segunda linha você vê a comunicação com origem 192.168.1.230 (com porta dinâmica estabelecedo comunicação HTTP com o host 186.202.183.65). A flag é [P.], ou, confirmação de PUSH, que significa dados sendo empurrados na comunicação. Veja a sequência, que vai de 1 a 1305, com ACK 1, ou seja, um pacote reconhecido como original. No final da linha o lenght indica o tamanho do pacote transferido: 1305 bytes, ou seja, a sequência inteira.

Depois disso está a mensagem, que inicia em 0x0000 e então o resto é interpretação.

Header TCP

Não ache que esse conhecimento é exclusivo de administradores de rede; Peritos necessitam dessa informação para análise e desenvolvedores conseguem desenvolver uma aplicativo de rede melhor se souberem o comportamento da comunicação. Do que discorri até esse ponto, todos os ítens podem ser encontrados no header TCP. Preferi pegar o modelo especificado no manual do TCPDump, mas você acha mais riqueza de detalhes no google, se lhe interessar:

Esse é o header de um pacote TCP IPv4 (32 bits).

Ler os campos do header TCP

Pode parecer que estamos entrando no primeiro nível da loucura fazendo um filtro assim:

tcpdump -i any dst host 192.168.1.230 and port 80 and 'tcp[13] == 2' -vvv -XX -s4192

Mas se você der uma olhada a seguir, entenderá a linha. Todos os conceitos foram vistos anteriormente e apenas os relacionados ao acesso dos campos do header através de expressões não foram explicados. Vamos agora aos conceitos.

No 13º octeto do header tem um octeto relacionado às flags. Aqui é bom que você saiba binário, mas vou dar uma explicada rápido de qualquer modo.

Como você pode ver na imagem, estão dispostos os 8 bits relacionadas às flags TCP. Supondo que o filtro comtemple apenas a captura de pacotes que contenham a flag SYN, então deveriamos casar o segundo campo da direita para a esquerda. O valor desse campo portanto é 2¹, que é o valor binário 00000010, que representa o valor decimal 2. Por isso o exemplo 'tcp[13] == 2'; dizemos aqui para casar o segundo bit do 13º octeto do header TCP.

Você pode especificar pelo nome da flag pretendida também. Para [P] (push) você poderia casar:

tcpdump -i any 'tcp[tcpflags] & tcp-push != 0' -vvv -XX -s4192

Nesse simples caso, o filtro sugere que apenas pacotes cuja flag PUSH esteja assinalada sejam capturados. Pode ser bastante útil se você deseja ignorar qualquer coisa que não sejam dados empurrado de um lado a outro, pois todas as demais flags são ignoradas. Obviamente esse comando tem o propósito de ler a mensagem.

Essas dicas são muito mais do que utilizo nesse video, onde mostro a mudança do comportamento TCP mudando código do sketch do Arduino sem mexer no conteúdo da mensagem, ou seja, vou mandar exatamente a mesma mensagem, modificando o modo que o programa o faz. Acredite, vale muito a pena assistir esse vídeo.

Como você pode ver no video, a redução foi de 45 para 13 pacotes sem modificações na mensagem, uma economia considerável de thoughput e tempo. Se somarmos apenas os bytes do CABEÇALHO IP e devolvessemos os outros 4 sensores do sketch original, teriamos 45*20*5 ou, ~4.5KB a cada 5 segundos do refresh ou, ~54KB por minuto.

Suponhamos que você esteja oferecendo serviço de broker e você tem apenas 100 clientes mandando 10 informes por casa. Nesse caso o resultado seria 54*10*100!

O que acha de trafegar 54MB de dados por minuto? Será que precisa de muita banda? Ah! Precisa sim, muita, mas muita banda, porque se você reparar nesse exemplo hipotético, eu fiz as contas apenas com os bytes do cabeçalho IP; não considerei o cabeçalho TCP, não considerei os dados e não considerei as retransmissões (comum no protocolo TCP). Por isso, se for planejar um produto, tem que planejar a escalabilidade dele senão é fácil colapsar.

Pra finalizar, não seria trivial analisar o fluxo de dados sem uma ferramenta específica e o DWOS TAP realmente facilita essa tarefa sem necessitar de configurações especificas.

Siga-nos no Manual do Maker no Facebook para ter informações de outros artigos conforme publicados.

Inscreva-se no nosso canal Manual do Maker Brasil no YouTube.

Próximo post a caminho!

Nome do Autor

Djames Suhanko

Autor do blog "Do bit Ao Byte / Manual do Maker".

Viciado em embarcados desde 2006.
LinuxUser 158.760, desde 1997.