Captura primeiro pacote de conexão TCP estabelecida com o iptables?

2

Estou procurando uma maneira de examinar o primeiro pacote somente de uma conexão TCP recém-estabelecida (o primeiro pacote com carga útil real, isto é). Existe uma maneira de fazer isso com o iptables? A correspondência de pacotes ESTABLISHED corresponderia a todos os pacotes de uma conexão após o handshake, certo?

    
por N.A. 04.04.2014 / 20:35

3 respostas

4

Você pode atingir sua meta usando (abusando) iptables , para ser mais específico: connbytes match e NFQUEUE target. connbytes permite que você corresponda ao N-ésimo pacote na conexão e NFQUEUE é um mecanismo para passar pacotes correspondentes a uma regra iptables para o programa userspace. Além disso: você terá que usar algum programa que esteja recebendo pacotes relevantes do kernel e processando-os.

iptables

Estou assumindo aqui que você está interessado em capturar o lado do servidor de pacotes (que pode ser alterado se você estiver interessado na captura do lado do cliente). Nesse caso, precisaremos capturar o pacote 3-incoming para cada conexão (ou seja, o primeiro pacote de entrada após o handshake de três vias) e colocar o pacote em uma fila do netfilter (fila # 1 neste caso).

iptables -I INPUT -p tcp -m tcp --dport 12345 -m connbytes --connbytes-mode packets --connbytes-dir original --connbytes 3:3 -j NFQUEUE --queue-num 1

Assim que um pacote corresponder a essa regra, ele será transmitido para o programa userspace ligado à fila # 1. O programa pode então examinar o pacote e depois decidir aceitá-lo ou soltá-lo.

O programa

Você precisará de um programa que receberá os pacotes no espaço do usuário usando a libnetfilter_queue library . As ligações para a biblioteca estão disponíveis em diferentes idiomas. A seguir, um exemplo de programa escrito em python:

import struct

from netfilterqueue import NetfilterQueue

def ip_to_string(ip):
        return ".".join(map(lambda n: str(ip>>n & 0xff), [24,16,8,0]))

def print_and_accept(pkt):
        pl = pkt.get_payload()
        src_ip = struct.unpack('>I', pl[12:16])[0]
        tcp_offset = (struct.unpack('>B', pl[0:1])[0] & 0xf) * 4
        tmp = struct.unpack('>B', pl[tcp_offset+12:tcp_offset+13])[0]
        data_offset = ((tmp & 0xf0) >> 4) * 4
        src_port = struct.unpack('>H', pl[tcp_offset+0:tcp_offset+2])[0]
        data = pl[tcp_offset + data_offset:]
        print 'from {}:{}, "{}"'.format(ip_to_string(src_ip), src_port, data)
        pkt.accept()

nfqueue = NetfilterQueue()
nfqueue.bind(1, print_and_accept)
try:
        nfqueue.run()
except KeyboardInterrupt:
        print

O programa assume que os pacotes enfileirados serão pacotes TCP IPv4 e imprime o par de origem ip: porta e a carga TCP do pacote.

Advertências

  1. Você nunca pode ter certeza de que o primeiro pacote de dados terá o cliente TLS completo como hello - o TCP pode fragmentar o fluxo da forma que desejar e não é impossível receber um único byte no primeiro pacote de dados.
  2. O TCP Fast Open irá quebrar a lógica desta abordagem. Se estiver habilitado, o handshake triplo inicial pode já transferir dados. Mas o TFO está desativado por padrão em quase todos os dispositivos por enquanto.
  3. Deve-se tomar cuidado ao usar NFQUEUE target: se o programa userspace ligado à fila travar, travar ou for lento para processar pacotes, eles serão descartados / travados e o serviço ligado à porta especificada ficará inacessível. Você pode passar a opção --queue-bypass para o NFQUEUE target para ACCEPT dos pacotes correspondentes se nenhum programa de espaço do usuário estiver ligado à fila especificada: isso deve ajudar com o problema do travamento do programa, mas não ajudará com um travamento ou programa lento.
por 05.04.2014 / 13:35
2

Você não pode fazer o que procura com o estoque iptables . Você precisaria escrever algum código de inspeção da camada 7.

Uma alternativa, se você estiver disposto a aceitar um pós-processamento, seria capturar o tráfego com tcpdump nos arquivos PCAP, analisá-los para os pacotes que está procurando e descartar o resto. Eu sei que o dissector SSL da Wireshark e um filtro de exibição podem obter o que você está procurando.

Se você não se importasse que suas capturas ficariam um pouco atrasadas, você poderia facilmente usar tcpdump para capturar o tráfego para girar arquivos PCAP. Você pode executar um script que monitore os arquivos PCAP concluídos e analise-os com tshark e um filtro de exibição para gravar apenas o que você está procurando em novos arquivos PCAP.

    
por 05.04.2014 / 01:39
2

Matching ESTABLISHED packets would match all packets of a connection after handshake, right?

Certo!

Não tenho certeza do que você quer dizer com "captura". iptables é uma coisa, a captura de pacotes de rede ( tcpdump ) é outra coisa.

Supondo que o meu entendimento seja "registrar somente novas conexões", você precisará registrar apenas as regras que correspondam ao estado NEW .

Vamos usar um caso de amostra em que queremos registrar apenas novas conexões para uma solicitação icmp (ping):

# Define LOG settings
iptables -N LOG_ACCEPT
iptables -A LOG_ACCEPT -j LOG --log-prefix '[IPTABLES ACCEPT] :'
iptables -A LOG_ACCEPT -j ACCEPT

iptables -A INPUT -p icmp -d 192.168.0.47 -m state --state NEW -j LOG_ACCEPT
iptables -A INPUT -p icmp -d 192.168.0.47 -m state --state ESTABLISHED,RELATED -j ACCEPT

Aqui, apenas LOG quando a NEW connection é estabelecida. Assim, podemos enviar mais de 1000 ping (do mesmo host de origem), apenas o primeiro pacote será registrado:

Apr  4 21:28:17 UBN-1 kernel: [78512.613705] [IPTABLES ACCEPT] :IN=eth0 OUT= MAC=00:0c:29:e4:d1:06:10:60:4b:69:7e:fd:08:00 SRC=192.168.0.39 DST=192.168.0.47 LEN=60 TOS=0x00 PREC=0x00 TTL=128 ID=32571 PROTO=ICMP TYPE=8 CODE=0 ID=2 SEQ=47088

Se você colocar LOG_ACCEPT na regra ESTABLISHED,RELATED :

iptables -A INPUT -p icmp -d 192.168.0.47 -m state --state NEW -j LOG_ACCEPT
iptables -A INPUT -p icmp -d 192.168.0.47 -m state --state ESTABLISHED,RELATED -j LOG_ACCEPT

então cada pedido de ping será registrado. Então 1000+ ping (do mesmo host de origem) gerará mais de 1.000 entradas no log:

Apr  4 21:33:49 UBN-1 kernel: [78844.130615] [IPTABLES ACCEPT] :IN=eth0 OUT= MAC=00:0c:29:e4:d1:06:10:60:4b:69:7e:fd:08:00 SRC=192.168.0.39 DST=192.168.0.47 LEN=60 TOS=0x00 PREC=0x00 TTL=128 ID=244 PROTO=ICMP TYPE=8 CODE=0 ID=2 SEQ=47092
Apr  4 21:33:50 UBN-1 kernel: [78845.130551] [IPTABLES ACCEPT] :IN=eth0 OUT= MAC=00:0c:29:e4:d1:06:10:60:4b:69:7e:fd:08:00 SRC=192.168.0.39 DST=192.168.0.47 LEN=60 TOS=0x00 PREC=0x00 TTL=128 ID=247 PROTO=ICMP TYPE=8 CODE=0 ID=2 SEQ=47093
Apr  4 21:33:51 UBN-1 kernel: [78846.131295] [IPTABLES ACCEPT] :IN=eth0 OUT= MAC=00:0c:29:e4:d1:06:10:60:4b:69:7e:fd:08:00 SRC=192.168.0.39 DST=192.168.0.47 LEN=60 TOS=0x00 PREC=0x00 TTL=128 ID=250 PROTO=ICMP TYPE=8 CODE=0 ID=2 SEQ=47094
Apr  4 21:33:52 UBN-1 kernel: [78847.132160] [IPTABLES ACCEPT] :IN=eth0 OUT= MAC=00:0c:29:e4:d1:06:10:60:4b:69:7e:fd:08:00 SRC=192.168.0.39 DST=192.168.0.47 LEN=60 TOS=0x00 PREC=0x00 TTL=128 ID=252 PROTO=ICMP TYPE=8 CODE=0 ID=2 SEQ=47095
......
......

Espero ter entendido a pergunta!

    
por 04.04.2014 / 21:38