web-dev-qa-db-ja.com

確立されたTCP iptablesとの接続の最初のパケットをキャプチャしますか?

新しく確立されたTCP接続(つまり、実際のペイロードを持つ最初のパケット)の最初のパケットのみを調べる方法を探しています。 iptablesでこれを行う方法は?ESTABLISHEDパケットを一致させると、ハンドシェイク後の接続のすべてのパケットが一致しますよね?

2
N.A.

iptablesを使用して(乱用して)目標を達成できます。具体的には、connbytes一致とNFQUEUEターゲットです。 connbytesを使用すると、接続内のN番目のパケットを照合できます。NFQUEUEは、iptablesルールに一致するパケットをユーザースペースプログラムに渡すためのメカニズムです。さらに、カーネルから関連するパケットを受信して​​処理するプログラムを使用する必要があります。

iptables

ここでは、サーバー側でパケットをキャプチャすることに関心があると想定しています(クライアント側でのキャプチャに関心がある場合は変更できます)。その場合、接続ごとに3番目の着信パケット(つまり、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 library を使用してユーザースペースでパケットを受信するプログラムが必要になります。ライブラリのバインディングは、さまざまな言語で利用できます。以下は、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:portペアとパケットのTCPペイロードを出力します。

警告

  1. 最初のデータパケットに完全なTLSクライアントが含まれるかどうかを確認することはできませんhello-TCPはストリームを好きなようにフラグメント化でき、最初のデータパケットで1バイトを受信することは不可能ではありません。
  2. TCP Fast Open は、このアプローチのロジックを壊します。有効になっている場合、最初の3ウェイハンドシェイクですでにデータが転送されている可能性があります。ただし、現時点では、ほとんどすべてのデバイスでTFOがデフォルトで無効になっています。
  3. NFQUEUEターゲットを使用する場合は注意が必要です。キューにバインドされたユーザースペースプログラムがハングしたり、クラッシュしたり、パケットの処理が遅い場合、パケットはドロップ/スタックし、指定されたポートにバインドされたサービスに到達できなくなります。指定されたキューにユーザースペースプログラムがバインドされていない場合は、--queue-bypassオプションをNFQUEUEターゲットに渡して一致したパケットをACCEPTに渡すことができます。これは、プログラムがクラッシュする問題に役立つはずです。しかし、ハングしたプログラムや遅いプログラムには役立ちません。
4
skarap

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

私が質問を理解したことを願っています!

2
krisFR

在庫iptablesでは探していることはできません。レイヤー7の検査コードを書く必要があります。

別の方法として、後処理に我慢したい場合は、tcpdumpを使用してトラフィックをPCAPファイルにキャプチャし、探しているパケットを解析して、残りを破棄することもできます。 WiresharkのSSLディセクタとディスプレイフィルタがあなたが探しているものを手に入れることができることを私は知っています。

キャプチャがリアルタイムより少し遅れることを気にしない場合は、tcpdumpを使用してローテーションするPCAPファイルへのトラフィックを簡単にキャプチャできます。完了したPCAPファイルを監視し、それらをtsharkと表示フィルターで解析して、探しているものだけを新しいPCAPファイルに書き出すスクリプトを実行できます。

2
Evan Anderson