Назад | Перейти на главную страницу

Захватить первый пакет установленного TCP-соединения с помощью iptables?

Я ищу способ изучить только первый пакет вновь установленного TCP-соединения (то есть первого пакета с фактической полезной нагрузкой). Есть ли способ сделать это с помощью iptables? Соответствующие пакеты ESTABLISHED будут соответствовать всем пакетам соединения после рукопожатия, верно?

Вы можете достичь своей цели, используя (злоупотребляя) iptables, чтобы быть более конкретным: connbytes матч и NFQUEUE цель. connbytes позволяет сопоставить N-й пакет в соединении и NFQUEUE это механизм для передачи пакетов, соответствующих правилу iptables, в программу пользовательского пространства. Более того: вам придется использовать какую-то программу, которая будет получать соответствующие пакеты от ядра и обрабатывать их.

iptables

Я предполагаю, что вы заинтересованы в захвате пакетов на стороне сервера (это можно изменить, если вы заинтересованы в захвате на стороне клиента). В этом случае нам нужно захватить 3-й входящий пакет для каждого соединения (т.е. первый входящий пакет после трехстороннего рукопожатия) и помещает пакет в очередь netfilter (в данном случае очередь # 1).

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

Как только пакет соответствует этому правилу, он будет передан программе пользовательского пространства, привязанной к очереди №1. Затем программа может проверить пакет и впоследствии решить принять его или отбросить.

Программа

Вам понадобится программа, которая будет получать пакеты в пользовательском пространстве, используя libnetfilter_queue библиотека. Привязки для библиотеки доступны на разных языках. Ниже приводится пример программы, написанной на 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

Программа предполагает, что пакеты в очереди будут пакетами IPv4 TCP, и печатает исходную пару ip: порт и полезную нагрузку TCP пакета.

Предостережения

  1. Вы никогда не можете быть уверены, что первый пакет данных будет иметь полное приветствие клиента TLS - TCP может фрагментировать поток по своему усмотрению, и возможно получение одного байта в первом пакете данных.
  2. TCP Fast Open нарушит логику этого подхода. Если он включен, первоначальное трехстороннее рукопожатие может уже передавать данные. Но TFO пока отключен по умолчанию почти на всех устройствах.
  3. Следует соблюдать осторожность при использовании NFQUEUE target: если программа пользовательского пространства, привязанная к очереди, зависает, дает сбой или медленно обрабатывает пакеты, они будут отброшены / зависнут, а служба, привязанная к указанному порту, станет недоступной. Вы можете пройти --queue-bypass вариант для NFQUEUE нацеливаться на ACCEPT совпадающие пакеты, если никакая программа пользовательского пространства не привязана к указанной очереди: это должно помочь с проблемой сбоя программы, но не поможет с зависшей или медленной программой.

Вы не можете делать то, что ищете, на складе iptables. Вам нужно будет написать контрольный код уровня 7.

Альтернативой, если вы готовы смириться с некоторой пост-обработкой, было бы захват трафика с помощью tcpdump в файлы PCAP, проанализируйте их на наличие пакетов, которые вы ищете, и выбросите остальные. Я знаю, что диссектор SSL и фильтр отображения Wireshark могут получить то, что вы ищете.

Если бы вы не возражали, что ваши снимки будут немного отставать от реального времени, вы могли бы легко использовать tcpdump для захвата трафика во вращающиеся файлы PCAP. Вы можете запустить сценарий, который отслеживает завершенные файлы PCAP и анализирует их с помощью tshark и фильтр отображения, позволяющий записывать в новые файлы PCAP только то, что вы ищете.

Соответствующие пакеты ESTABLISHED будут соответствовать всем пакетам соединения после рукопожатия, верно?

Правильно !

Не уверен, что вы имеете в виду под «захватом». iptables это вещь, захват сетевых пакетов (tcpdump) другое дело.

Предполагая, что я понимаю «регистрировать только новые соединения», вам нужно будет регистрировать только правила, соответствующие NEW штат.

Давайте воспользуемся примером, когда мы хотим регистрировать только новые соединения для запроса 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

Здесь мы просто LOG когда NEW соединение установлено. Таким образом, мы можем отправить более 1000 ping (с того же исходного хоста), только первый пакет будет зарегистрирован:

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

Если вы положите LOG_ACCEPT в 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

тогда каждый запрос ping будет регистрироваться. Таким образом, 1000+ ping (с того же исходного хоста) сгенерируют более 1000 записей в журнале:

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

Надеюсь, я понял вопрос!