0x00 前言
scapy是一个强大的交互式(interactive)的包操作程序,用python写的,有一个python的命令行解释器界面,可直接运行,当然也可以作为第三库,导入到我们的python程序中去使用它的类和方法。
关于scapy作者的一个简介:
Philippe Biondi is a research engineer at EADS Innovation Works(欧洲宇航防务集团创新中心). He works in the IT security lab, and is the creator of many programs like Scapy or ShellForge. Philippe authored chapter 6 of Security Power Tools, in which Scapy takes a good place, and also chapter 11.
scapy可以仿造和解析大量协议类型的包,可以把包发到网上,或者捕捉包,可以match请求和回复。更强大的是它可以扫描路由器,扫描协议端口,攻击网络等等等等吧,非常之多,非常之强大。有人称它为可编程的wireshark,它的强大我现在也没能亲身体会,但我感觉它应该比wireshark强大。
0x01 起航Scapy
Scapy的交互shell是运行在一个终端会话当中。因为需要root权限才能发送数据包,所以我们在这里使用“sudo”。$ sudo scapy
Welcome to Scapy (2.0.1-dev)
>>>
在Windows当中,请打开命令提示符(“cmd.exe”),并确保您拥有管理员权限:C:\>scapy
INFO: No IPv6 support in kernel
WARNING: No route found for IPv6 destination :: (no default route?)
Welcome to Scapy (2.0.1-dev)
>>>
如果您没有安装所有的可选包,Scapy将会告诉你有些功能不可用:INFO: Can't import python gnuplot wrapper . Won't be able to plot.
INFO: Can't import PyX. Won't be able to use psdump() or pdfdump().
虽然没有安装,但发送和接收数据包的基本功能仍然有效。
0x02 互动教程
本节将会告诉您一些Scapy的功能。让我们按上文所述打开Scapy,亲自尝试些例子吧。
1. 第一步
让我们来建立一个数据包试一试。>>> a=IP(ttl=10)
>>> a
< IP ttl=10 |>
>>> a.src
’127.0.0.1’
>>> a.dst="192.168.1.1"
>>> a
< IP ttl=10 dst=192.168.1.1 |>
>>> a.src
’192.168.8.14’
>>> del(a.ttl)
>>> a
< IP dst=192.168.1.1 |>
>>> a.ttl
64
2. 层结构(OSI参考模型)
“/”操作符在两个层之间起到一个组合的作用。当使用该操作符时,下层可以根据其上层,使它的一个或多个默认字段被重载。(您仍可以赋予您想要的值)一个字符串也可以被用作原始层。>>> IP()
>>> IP()/TCP()
>
>>> Ether()/IP()/TCP()
>>
>>> IP()/TCP()/"GET / HTTP/1.0\r\n\r\n"
>>
>>> Ether()/IP()/IP()/UDP()
>>>
>>> IP(proto=55)/TCP()
>
每一个数据包都可以被建立或分解(注意:在Python中“_”(下划线)是上一条语句执行的结果):>>> str(IP())
'E\x00\x00\x14\x00\x01\x00\x00@\x00|\xe7\x7f\x00\x00\x01\x7f\x00\x00\x01'
>>> IP(_)
chksum=0x7ce7 src=127.0.0.1 dst=127.0.0.1 |>
>>> a=Ether()/IP(dst="www.slashdot.org")/TCP()/"GET /index.html HTTP/1.0 \n\n"
>>> hexdump(a)
00 02 15 37 A2 44 00 AE F3 52 AA D1 08 00 45 00 ...7.D...R....E.
00 43 00 01 00 00 40 06 78 3C C0 A8 05 15 42 23 [email protected]<....b>
FA 97 00 14 00 50 00 00 00 00 00 00 00 00 50 02 .....P........P.
20 00 BB 39 00 00 47 45 54 20 2F 69 6E 64 65 78 ..9..GET /index
2E 68 74 6D 6C 20 48 54 54 50 2F 31 2E 30 20 0A .html HTTP/1.0 .
0A .
>>> b=str(a)
>>> b
'\x00\x02\x157\xa2D\x00\xae\xf3R\xaa\xd1\x08\x00E\x00\x00C\x00\x01\x00\x00@\x06x
\xa8\x05\x15B#\xfa\x97\x00\x14\x00P\x00\x00\x00\x00\x00\x00\x00\x00P\x02 \x00
\xbb9\x00\x00GET /index.html HTTP/1.0 \n\n'
>>> c=Ether(b)
>>> c
ihl=5L tos=0x0 len=67 id=1 flags= frag=0L ttl=64 proto=TCP chksum=0x783c
src=192.168.5.21 dst=66.35.250.151 options='' |
ack=0L dataofs=5L reserved=0L flags=S window=8192 chksum=0xbb39 urgptr=0
options=[] |>>>
我们看到一个分解的数据包将其所有的字段填充。那是因为,我认为每个字段值由原始字符串附加。如果这太冗长,“hide_defaults()”方法将会删除具有默认值的字段:>>> c.hide_defaults()
>>> c
frag=0 proto=TCP chksum=0x783c src=192.168.5.21 dst=66.35.250.151 |
chksum=0xbb39 options=[] |>>>
3. 读取PCAP文件
你可以从PCAP文件中读取数据包,并将其写入到一个PCAP文件中。>>> a=rdpcap("/spare/captures/isakmp.cap")
>>> a
4. 图形转储(PDF,PS)
如果您已经安装PyX,你可以做一个数据包的图形转储PostScript/PDF或数据包的列表(见下面丑陋的PNG图像,PostScript/PDF则具有更好的质量...)>>> a[423].pdfdump(layer_shift=1)
>>> a[423].psdump("/tmp/isakmp_pkt.eps",layer_shift=1)
5. 生成一组数据包
目前我们只是生成一个数据包。让我们看看如何轻易地定制一组数据包。整个数据包的每一个字段(甚至是网络层次)都可以是一组。在这里隐含地定义了一组数据包的概念,意思是在所有字段中使用笛卡尔积来生成一组数据包。>>> a=IP(dst="www.slashdot.org/30")
>>> a
>>> [p for p in a]
[, ,
, ]
>>> b=IP(ttl=[1,2,(5,9)])
>>> b
>>> [p for p in b]
[, , , ,
, , ]
>>> c=TCP(dport=[80,443])
>>> [p for p in a/c]
[>,
>,
>,
>,
>,
>,
>,
>]
某些操作(如修改一个数据包中的字符串)无法对一组数据包使用。在这些情况下,如果您忘记展开您的数据包集合,只有您忘记生成的列表中的第一个元素会被用于组装数据包。
6. 发送数据包
现在我们知道了如何处理数据包。让我们来看看如何发送它们。“send()”函数将会在第3层发送数据包。也就是说它会为你处理路由和第2层的数据。“send()”函数将会工作在第2层。选择合适的接口和正确的链路层协议都取决于你。>>> send(IP(dst="1.2.3.4")/ICMP())
.
Sent 1 packets.
>>> sendp(Ether()/IP(dst="1.2.3.4",ttl=(1,4)), iface="eth1")
....
Sent 4 packets.
>>> sendp("I'm travelling on Ethernet", iface="eth1", loop=1, inter=0.2)
................^C
Sent 16 packets.
>>> sendp(rdpcap("/tmp/pcapfile")) # tcpreplay
...........
Sent 11 packets.
7. Fuzzing
“fuzz()”函数可以通过一个具有随机值、数据类型合适的对象,来改变任何默认值,但该值是不能被计算的(像校验和那样)。这使得可以快速建立循环模糊化测试模板。在下面的例子中,IP层是正常的,UDP层和NTP层被fuzz。UDP的校验和是正确的,UDP的目的端口被NTP重载为123,而且NTP的版本被更变为4.其他所有的端口将被随机分组:>>> send(IP(dst="target")/fuzz(UDP()/NTP(version=4)),loop=1)
................^C
Sent 16 packets.
8. 发送和接收数据包(“sr”)
现在让我们做一些有趣的事情。“sr()”函数是用来发送数据包和接收应答。该函数返回一对数据包及其应答,还有无应答的数据包。“sr1()”函数是一种变体,用来返回一个应答数据包。发送的数据包必须是第3层报文(IP,ARP等)。“srp()”则是使用第2层报文(以太网,802.3等)。>>> p=sr1(IP(dst="www.slashdot.org")/ICMP()/"XXXXXXXXXXX")
Begin emission:
...Finished to send 1 packets.
.*
Received 5 packets, got 1 answers, remaining 0 packets
>>> p
chksum=0x51dd src=66.35.250.151 dst=192.168.5.21 options='' |
code=0 chksum=0xee45 id=0x0 seq=0x0 |
|>>>
>>> p.show()
---[ IP ]---
version = 4L
ihl = 5L
tos = 0x0
len = 39
id = 15489
flags =
frag = 0L
ttl = 42
proto = ICMP
chksum = 0x51dd
src = 66.35.250.151
dst = 192.168.5.21
options = ''
---[ ICMP ]---
type = echo-reply
code = 0
chksum = 0xee45
id = 0x0
seq = 0x0
---[ Raw ]---
load = 'XXXXXXXXXXX'
---[ Padding ]---
load = '\x00\x00\x00\x00'
DNS查询(“rd” = recursion desired)。主机192.168.5.1是我的DNS服务器。注意从我Linksys来的非空填充具有Etherleak缺陷:>>> sr1(IP(dst="192.168.5.1")/UDP()/DNS(rd=1,qd=DNSQR(qname="www.slashdot.org")))
Begin emission:
Finished to send 1 packets.
..*
Received 3 packets, got 1 answers, remaining 0 packets
src=192.168.5.1 dst=192.168.5.21 options='' |
|
nscount=0 arcount=0 qd=
an=
ns=0 ar=0 |>>>
发送和接收函数族是scapy中的核心部分。它们返回一对两个列表。第一个就是发送的数据包及其应答组成的列表,第二个是无应答数据包组成的列表。为了更好地呈现它们,它们被封装成一个对象,并且提供了一些便于操作的方法:>>> sr(IP(dst="192.168.8.1")/TCP(dport=[21,22,23]))
Received 6 packets, got 3 answers, remaining 0 packets
(, )
>>> ans,unans=_
>>> ans.summary()
IP / TCP 192.168.8.14:20 > 192.168.8.1:21 S ==> Ether / IP / TCP 192.168.8.1:21 > 192.168.8.14:20 RA / Padding
IP / TCP 192.168.8.14:20 > 192.168.8.1:22 S ==> Ether / IP / TCP 192.168.8.1:22 > 192.168.8.14:20 RA / Padding
IP / TCP 192.168.8.14:20 > 192.168.8.1:23 S ==> Ether / IP / TCP 192.168.8.1:23 > 192.168.8.14:20 RA / Padding
如果对于应答数据包有速度限制,你可以通过“inter”参数来设置两个数据包之间等待的时间间隔。如果有些数据包丢失了,或者设置时间间隔不足以满足要求,你可以重新发送所有无应答数据包。你可以简单地对无应答数据包列表再调用一遍函数,或者去设置“retry”参数。如果retry设置为3,scapy会对无应答的数据包重复发送三次。如果retry设为-3,scapy则会一直发送无应答的数据包,直到“timeout”参数等待最后一个数据包已发送的时间。
9. SYN Scans
在Scapy提示符中执行以下命令,可以对经典的SYN Scan初始化:>>> sr1(IP(dst="72.14.207.99")/TCP(dport=80,flags="S"))
以上向Google的80端口发送了一个SYN数据包,会在接收到一个应答后退出:Begin emission:
.Finished to send 1 packets.
*
Received 2 packets, got 1 answers, remaining 0 packets
proto=TCP chksum=0x6a34 src=72.14.207.99 dst=192.168.1.100 options=// |
flags=SA window=8190 chksum=0xcdc7 urgptr=0 options=[('MSS', 536)] |
>>
从以上的输出中可以看出,Google返回了一个SA(SYN-ACK)标志位,表示80端口是开放的。
使用其他标志位扫描一下系统的440到443端口:>>> sr(IP(dst="192.168.1.1")/TCP(sport=666,dport=(440,443),flags="S"))
或者>>> sr(IP(dst="192.168.1.1")/TCP(sport=RandShort(),dport=[440,441,442,443],flags="S"))
可以对收集的数据包进行摘要(summary),来快速地浏览响应:>>> ans,unans = _
>>> ans.summary()
IP / TCP 192.168.1.100:ftp-data &