ネットワーク機器などの検証のために通常は生成されない不正なパケットが必要なケースがあります。
そんな不正なパケットを生成するときに非常に重宝するpythonのライブラリがscapyです。IPやTCPで不正な長さの値の入ったパケットなどが簡単に作れます。
今回はTCPオプションの長さが不正なパケットを作成しようと試みましたが、オプションの長さの値を自動的に計算してパケットを作成するようになっていました。
TCPオプションフィールドは type-length-value の形式になっています。
しかしscapyの指定するオプションは以下の通りで type とvalueしか指定できません。
pkt=IP()/TCP(dport=80, flags="S", options=[("MSS",1460)])
オプションは TCPOptionsField というフィールドになっていて、値に何をいれても長さを挿入してきます。
ls(TCP())
sport : ShortEnumField = 20 (20)
dport : ShortEnumField = 80 (80)
seq : IntField = 0 (0)
ack : IntField = 0 (0)
dataofs : BitField (4 bits) = None (None)
reserved : BitField (3 bits) = 0 (0)
flags : FlagsField (9 bits) = 2 (2)
window : ShortField = 8192 (8192)
chksum : XShortField = None (None)
urgptr : ShortField = 0 (0)
options : TCPOptionsField = {} ({})
ここでもちろんTCPヘッダをバイナリで書いていく方法もあるのですが、TCPのチェックサムの計算はIPヘッダも入るので面倒になり、scapyを使うメリットがなくなってしまいます。
そこでTCPのクラスを継承したクラスを作成して上記のoptionsを上書してみました。
TCPのクラスはlayersの下のinet.pyにありました。
class TCP(Packet):
name = "TCP"
fields_desc = [ ShortEnumField("sport", 20, TCP_SERVICES),
ShortEnumField("dport", 80, TCP_SERVICES),
IntField("seq", 0),
IntField("ack", 0),
BitField("dataofs", None, 4),
BitField("reserved", 0, 3),
FlagsField("flags", 0x2, 9, "FSRPAUECN"),
ShortField("window", 8192),
XShortField("chksum", None),
ShortField("urgptr", 0),
TCPOptionsField("options", []) ]
上記のTCPOptionsFieldをべつの自由にバイナリデータが書けるやつに書き換えてしまえばいけるかもしれません。他のレイヤをみていたらStrFixedLenFieldというのがあり、これを試してみることにしました。
MYTCPを定義してTCPクラスを継承、optionsを上書きします。
from scapy.all import *
class MYTCP(TCP):
fields_desc = [ ShortEnumField("sport", 20, TCP_SERVICES),
ShortEnumField("dport", 80, TCP_SERVICES),
IntField("seq", 0),
IntField("ack", 0),
BitField("dataofs", None, 4),
BitField("reserved", 0, 3),
FlagsField("flags", 0x2, 9, "FSRPAUECN"),
ShortField("window", 8192),
XShortField("chksum", None),
ShortField("urgptr", 0),
StrFixedLenField("options", "\x00\x00\x00\x00",4) ]
実際にパケットをつくってみます。
pkt=IP(dst="3.3.3.3")/MYTCP(dport=80,flags="S",options="\x99\x98\x97\x96")/"AAA"
次にパケットが正常に作成できているかみてみます。
checksum 0xaddbの次がurgptr で\x00\x00
その次がoptionで今回指定した\x99\x98\x97\x96となっていて不正なフィールドを作成できました。
pkt.show2()
### [ IP ]###
version = 4
ihl = 5
tos = 0x0
len = 47
id = 1
flags =
frag = 0
ttl = 64
proto = tcp
chksum = 0x5c9d
src = 10.255.13.39
dst = 3.3.3.3
\options \
### [ TCP ]###
sport = ftp_data
dport = http
seq = 0
ack = 0
dataofs = 6
reserved = 0
flags = S
window = 8192
chksum = 0xaddb
urgptr = 0
options = [(153, b'\x97\x96')]
### [ Raw ]###
load = 'AAA'
print(bytes(pkt))
b"E\x00\x00/\x00\x01\x00\x00@\x06\\\x9d\n\xff\r'\x03\x03\x03\x03\x00\x14\x00P\x00\x00\x00\x00\x00\x00\x00\x00`\x02 \x00\xad\xdb\x00\x00\x99\x98\x97\x96AAA
パケットを送ると不正なパケットがきちんと送信されました。
send(pkt)