不知道为何,网上有些把scapy跟scrapy搞混的。scapy是一个操作网络数据包的工具(Packet crafting),scrapy是一个屏幕抓取和web抓取框架(A Fast and Powerful Scraping and Web Crawling)...
scapy的基本用法还是参照scapy官网上的来做吧。翻译对我来说是一件困难的事,幸亏有人已经做过类似的工作,如这篇博客Scapy使用文档中文版。那对于一些我不确定的翻译,我就直接使用这篇博客的。我的文章也可以看作是对于Scapy使用文档中文版的转载;不过我会根据自己的理解,以及结合不同网络协议的学习,加一些注解。测试平台是linux centos7。
安装
python3 -m pip install scapy
或者尝试
python3
pip3 install scapy
开始使用scapy
scapy的交互式shell在终端会话中,发送数据包需要root权限,所以普通用户需要使用sudo命令
sudo ./scapy
我这root用户,直接使用scapy命令就好
配置终端显示颜色
将conf.color_theme设置为下列样式中的一种:
DefaultTheme, BrightTheme, RastaTheme, ColorOnBlackTheme, BlackAndWhite, HTMLTheme, LatexTheme
如:
conf.color_theme = BrightTheme()
交互式教程
新建一个包(packet),测一测以下代码
此处IP创建了一个默认的数据包,使用ls(IP())可以查看IP数据包的参数
叠层(Stacking layers)
“/”操作符在两个层之间起到一个组合的作用。当使用该操作符时,下层可以根据其上层,使它的一个或多个默认字段被重载。(您仍可以赋予您想要的值)一个字符串也可以被用作原始层。
每一个数据包都可以被建立或分解(注意:在Python中“_”(下划线)是上一条语句执行的结果):
被拆分的包保留了所有字段,如果这样显得太啰嗦,可以通过hide_defaults()方法删除重复的字段
读取PCAP文件
a=rdpcap("/spare/captures/isakmp.cap")
图形转储(PDF,PS)
如果你安装了PyX,你可以将一个包或者一系列包图形转储为PostScript/PDF文件(下面是比较丑的PNG图片,如果是PostScript/PDF格式的话质量要更好)
未安装pyx提示:ImportError: PyX and its dependencies must be installed
a[423].pdfdump(layer_shift=1)
a[423].psdump("/tmp/isakmp_pkt.eps",layer_shift=1)
命令 | 作用 |
raw(pkt) | 组装数据包 |
hexdumo(pkt) | 转换成16进制 |
ls(pkt) | 列出字段值 |
pkt.summary() | 查看数据包的一行摘要 |
pkt.show() | 包的开发视图 |
pkt.show2() | 和show一样,但是用于组装包上(例如,计算校验和(checksum)) |
pkt.sprintf() | 用包字段值填充格式化字符串 |
pkt.decode_payload_as() | 改变有效载荷的解码方式 |
pkt.psdump() | 绘制一个解释说明的PostScript图表 |
pkt.pdfdump() | 绘制一个解释说明的pdf |
ptk.command | 返回一个可以生成包的scapy命令 |
生成数据包的集合/生成一组包
目前我们只是生成一个数据包。让我们看看如何轻易地定制一组数据包。整个数据包的每一个字段(甚至是网络层次)都可以是一组。在这里隐含地定义了一组数据包的概念,意思是在所有字段中使用笛卡尔积来生成一组数据包。
某些操作(如修改一个数据包中的字符串)无法对一组数据包使用。在这些情况下,如果您忘记展开您的数据包集合,只有您忘记生成的列表中的第一个元素会被用于组装数据包。
(这里其实就是利用python的list生成一组数据包sets of packets)
命令 | 集合 |
summary() | 显示每个包的摘要列表 |
nsummary() | 跟前面的一样,并且带有包数量 |
conversations() | 显示会话的图标 |
show() | displays the preferred representation(通常用nsummary()) |
filter() | 返回一个由lambda函数过滤的包列表 |
hexdump() | 返回所有包的一个hexdump数据 |
haxraw() | 返回所有包的Rawlayer的一个hexdump数据 |
padding() | 返回一个带有填充的包的hexdump |
nzpadding() | 返回一个非0填充的包的hexdump |
plot() | 绘制应用于数据包列表的lambda函数 |
make table() | 根据lambda函数显示一个表格 |
发送包
我们已经知道如何去修改包,现在让我们看看怎么发送它们。send()函数在第3层(网络层?)上发送包。这就是说,它将为你处理第2层数据和路由。sendp()函数工作在第2层(数据链路层?)。选择正确的接口和链接层协议(link layer protocol)完全取决于你。如果return_packets=True,则send()和sendp()将返回已发送的包列表。
ttl,即Time To Live的缩写,该字段指定IP包被路由器丢弃之前允许通过的最大网段数量,TTL是IPv4报头的一个8bit字段。虽然TTL从字面上翻译,是可以存活的时间,但实际上TTL是IP数据包在计算机网络中可以转发的最大跳数。TTL字段由IP数据包的发送者设置,在IP数据包从源到目的的整个转发路径上,每经过一个路由器,路由器都会修改这个TTL字段值,具体的做法是把该TTL的值减1,然后再将IP包转发出去。如果在IP包到达目的IP之前,TTL减少为0,路由器将会丢弃收到的TTL=0的IP包并向IP包的发送者发送 ICMP time exceeded消息。(百度百科)
错误提示:WARNING: Mac address to reach destination not found. Using broadcast.这个错误是因为应该用sendp发送而用了send
关于返回值的测试
Fuzzing
fuzz()函数可以通过一个具有随机值、数据类型合适的对象,来改变任何默认值,但该值是不能被计算的(像校验和那样)。这使得可以快速建立循环模糊化测试模板。在下面的例子中,IP层是正常的,UDP层和NTP层被fuzz。UDP的校验和是正确的,UDP的目的端口被NTP重载为123,而且NTP的版本被更变为4.其他所有的端口将被随机分组:
备注:Fuzzing是模糊测试,通过函数随机生成大量测试用例来测试程序。
发送并且接收包
现在,我们来做一些有趣的事,src()函数用来发送包并且接收响应。这个函数返回包及其响应,以及没有响应的包。函数sr1()是一个变体,它只返回一个响应包(不管是发送一个包还是一组包)。这些包必须是第三层的包(IP包,ARP包等)。函数srp()做类似的事,不过它是发送第二层包(如以太包,802.3等),如果没有应答,或者超时,会返回一个空值(比如,构建包字段填写错误,返回空值)。
一个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<IP version=4L ihl=5L tos=0x0 len=78 id=0 flags=DF frag=0L ttl=64 proto=UDP chksum=0xaf38src=192.168.5.1 dst=192.168.5.21 options='' |<UDP sport=53 dport=53 len=58 chksum=0xd55d|<DNS id=0 qr=1L opcode=QUERY aa=0L tc=0L rd=1L ra=1L z=0L rcode=ok qdcount=1 ancount=1nscount=0 arcount=0 qd=<DNSQR qname='www.slashdot.org.' qtype=A qclass=IN |>an=<DNSRR rrname='www.slashdot.org.' type=A rclass=IN ttl=3560L rdata='66.35.250.151' |>ns=0 ar=0 |<Padding load='\xc6\x94\xc7\xeb' |>>>>
发送和接受函数族(function family)是scapy的核心。它们返回两个列表。第一个就是发送的数据包及其应答组成的列表,第二个是无应答数据包组成的列表。为了更好地呈现它们,它们被封装成一个对象,并且提供了一些便于操作的方法:
>>>sr(IP(dst="192.168.8.1")/TCP(dport=[21,22,23]))Received 6 packets, got 3 answers, remaining 0 packets(<Results: UDP:0 TCP:3 ICMP:0 Other:0>, <Unanswered: UDP:0 TCP:0 ICMP:0 Other:0>)>>>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 / PaddingIP / 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 / PaddingIP / 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”参数等待最后一个数据包已发送的时间。
SYN Scans
经典的SYN扫描可以通过执行下面的提示符命令初始化
上面向Google的80端口(我这里改成能ping通的其他主机地址了)发送单个SYN包,在接受单个响应时退出。
从上面的输出可以看出,目标主机返回一个“SA”或者SYN-ACK标志标明端口是开放的。
使用其他标志位扫描一下系统的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 > 192.168.1.1:440 S ======> IP / TCP 192.168.1.1:440 > 192.168.1.100:ftp-data RA / PaddingIP / TCP 192.168.1.100:ftp-data > 192.168.1.1:441 S ======> IP / TCP 192.168.1.1:441 > 192.168.1.100:ftp-data RA / PaddingIP / TCP 192.168.1.100:ftp-data > 192.168.1.1:442 S ======> IP / TCP 192.168.1.1:442 > 192.168.1.100:ftp-data RA / PaddingIP / TCP 192.168.1.100:ftp-data > 192.168.1.1:https S ======> IP / TCP 192.168.1.1:https > 192.168.1.100:ftp-data SA / Padding
进度......20%