Scapy基础使用(一)
Scapy
基本介绍
简介
Scapy是一个基于Python的程序,是一个强大的交互式包操作程序。它能够发送或解码大量协议的数据包,用于发送、嗅探、伪造网络数据包等行为,因此可以作为探测、扫描或者网络攻击的工具。Scapy可以实现部分工具的功能:hping、arpsoof、apr-sk、arping、p0f,甚至Nmap、tcpdump等工具的部分功能。
scapy还可以很好地执行许多其他工具无法处理的其他特定任务,例如发送无效帧、注入自己的802.11帧、结合技术(VLAN跳跃+ARP缓存中毒、WEP加密通道上的VoIP解码等)
Scapy还可以跟踪路由并仅给出请求的起始TTL和应答的源IP。一种能发出整个网络信号并给出应答机列表的设备。一个执行portscan并返回一个 Latex 报告的函数。
特点
网络工具缺点
-
大部分网络工具都是作者为了特定目的构建的,因此在使用上有一些限制,例如一个ARP缓存中毒程序不允许使用双802.1q封装。因此如果有新的需求,则需要寻找或者构造一个新的工具
-
混淆解码和解释。机器擅长解码,可以帮助人类破解;而翻译是为人类保留的。有些程序试图模仿这种行为,例如说“The host is open“,而不是说”I received a SYN-ACK“,这对初学者比较友好,但是其中丢失了大量的可用信息。
-
只进行解码的程序也不能提供它们接收到的所有信息
Spacy能够克服上述局限,能够准确地构建所需的数据包,它有一个灵活的模型,试图避免这样的任意限制,可以自由地将所需的任何值放入所需的任何字段中,并按所需方式对其进行堆叠。Spacy可以根据少量的代码来构建一个简易的工具程序。
Scapy优点
-
快速分组设计
scapy的范例是提出一种特定于领域的语言(DSL),它能够对任何类型的数据包进行强大而快速的描述。使用python语法和python解释器作为DSL语法和解释器有许多优点:不需要编写单独的解释器,用户不需要再学习另一种语言,他们从一种完整、简洁和强大的语言中获益。
scapy允许用户将一个或一组数据包描述为层叠在一起的层。每个层的字段都有可以重载的有用默认值。scapy不强制用户使用预先确定的方法或模板。这减少了每次需要不同场景时编写新工具的需求。在C语言中,平均需要60行来描述一个包。使用scapy,将要发送的数据包只能在一行中描述,另一行用于打印结果。90%的网络探测工具可以用2行scapy重写。
-
少量探测反馈大量信息
网络发现是黑盒测试。当探测一个网络时,许多数据包被发送,而只有少数被应答。scapy提供所有信息,即发送的所有数据和接收的所有响应。对这些数据的检查将为用户提供所需的信息。
-
返回完整信息
报告类似 在端口80上接收到TCP重置 不受解释错误的影响。报告 端口80关闭 是一种解释,在工具作者无法想象的特定环境中,大多数时候可能是正确的,但也可能是错误的。一些扫描器在接收到无法到达的ICMP目的地数据包时,往往会报告一个经过过滤的TCP端口。这可能是正确的,但在某些情况下,这意味着数据包没有被防火墙过滤,而是没有主机将数据包转发到。
scapy避免了这种现象,返回比较全面的信息供人员查看。
安装
Scapy对python的版本要求比较严格
Scapy version | Python 2.2-2.6 | Python 2.7 | Python 3.4-3.6 | Python 3.7 | Python 3.8 |
---|---|---|---|---|---|
2.2.X | YES | YES | NO | NO | NO |
2.3.3 | YES | YES | NO | NO | NO |
2.4.0 | NO | YES | YES | NO | NO |
2.4.2 | NO | YES | YES | YES | NO |
2.4.3-2.4.4 | NO | YES | YES | YES | YES |
安装方式
#conda
conda install -c conda-forge scrapy
#pip
pip install Scrapy
Scapy在版本2.4.3开始,可以分为三类机型安装
pip install scapy
pip install --pre scapy[basic](推荐)
pip install --pre scapy[complete](包含了依赖项)
#git
git clone https://github.com/secdev/scapy.git
cd scapy
sudo python setup.py install
依赖项
-
作图: Matplotlib
p=sniff(count=50) p.plot(lambda x:len(x))
-
2D图形:PyX,要求安装 texlive (Unix) 或 MikTex (Windows)
p=IP()/ICMP() p.pdfdump("test.pdf")
-
图: Graphviz 和 ImageMagick (软件)
p=rdpcap("myfile.pcap") p.conversations(type="jpg", target="> test.jpg")
-
三维图形:VPython-Jupyter
a,u=traceroute(["www.python.org", "google.com","slashdot.org"]) a.trace3D()
-
WEP解密:cryptography
enc=rdpcap("weplab-64bit-AA-managed.pcap") enc.show() enc[0] conf.wepkey="AA\x00\x00\x00" dec=Dot11PacketList(enc).toEthernet() dec.show() dec[0]
-
PKI操作和TLS解密:cryptography
-
指纹图谱:Nmap
load_module("nmap") nmap_fp("192.168.0.1")
-
VoIP:Sox
特定平台软件要求
Linux:tcpdump
Windows:Npcap,旧版为Winpcap,新版不支持
交互式使用
scapy可以通过终端进行交互式操作。注意发送数据包需要root权限,因此需要在root下或者sudo运行。若缺少相关安装依赖,则会有部分功能不可用
INFO: Can't import PyX. Won't be able to use psdump() or pdfdump().
启动命令
# scapy
常见命令
ls() - 不带参数则查看所有可持的layer,也可以指定一个layer名称从而查询此layer的详细详细,例如ls(TCP)
lsc() - 查看当前scapy的所有功能列表
help() - 查看功能的帮助,例如help(hexdump)可以查看hexdump的作用和参数介绍
conf - 查看当前的配置信息,有些scapy功能没有指定参数时,默认值都取自这里
构造数据包
在Scapy中每一个协议就是一个类。只需要实例化一个协议类,就可以创建一个该协议的数据包。例如
a=IP(ttl=10) #设置一个IP数据包,其ttl为10
asrc # "127.0.0.1" 默认为本地回环地址
a.dst='192.168.220.1' 目的地址,可以设置为网段,如192.168.220.0/24,此时产生256个数据包,查看数据包使用[i for i in ip]
a.src # "192.168.220.154" 设置好目的地址后会更换会对应ip网段
a
<IP ttl=10 dst=192.168.220.1 |>
del(a.ttl)
a
<IP dst=192.168.220.1 |>
a.ttl
64
Scapy是根据分层来构造数据包的,大致基于TCP/IP体系;一般数据协议为Ether-IP-TCP/UDP等,根据不同的使用场景设置不同的数据包,例如IP()函数无法用来构造ARP请求和应答数据包,此时可以使用Ether(),这个函数可以设置发送方和接收方的MAC地址。如广播数据包。
Ether(dst="ff:ff:ff:ff:ff:ff")
构造HTTP数据包
IP()/TCP()/"GET/HTTP/1.0\r\n\r\n"
Scapy目前使用频率最高的类有Ether、IP、TCP和UDP。每个类都有自己的一些参数
-
Ether:源地址、目的地址和类型。
dst - 目的MAC地址 src - 源MAC地址 type - 类型 IPv4:0x0800 例如: Ether(dst='00:00:00:00:00:00',src='00:00:00:00:00:00',type=0x800)
-
IP:源地址和目的地址,还有版本、长度、协议类型、校验和等
version - 版本,4表示IPv4,6表示IPv6 proto - 协议,TCP,UDP chksum - 校验和 src - 源地址 dst - 目的地址 IP(version=4,ihl=5, tos=84, id=19450, flags=0, ttl=64, proto=1,chksum=0xf090, src='192.168.1.6',dst='192.168.1.6')
-
TCP
sport - 源端口 dport - 目标端口 seq - sequence值 ack - acknowledgement值 flags - S,A,SA,F,P window - 窗口大小 chksum - 校验和 TCP(sport=45486,dport=8888,seq=31,ack=31,window=342,flags='PA')
-
UDP
sport - 源端口 dport - 目的端口 len - 包长度 chksum - 校验和 UDP(sport=1971,dport=9527,len=10,chksum=0)
可以使用 ls() 函数来查看一个类拥有的属性。
ls(Ether())
WARNING: Mac address to reach destination not found. Using broadcast.
dst : DestMACField = 'ff:ff:ff:ff:ff:ff' (None)
src : SourceMACField = '00:0c:29:81:c8:a0' (None)
type : XShortEnumField = 36864 (36864)
/
运算符用作两层之间的合成运算符,下层可以根据上层重载一个或多个默认字段,字符串可以用作原始层,可以根据/
进行去跟
>>> IP()
<IP |>
>>> IP()/TCP()
<IP frag=0 proto=tcp |<TCP |>>
>>> Ether()/IP()/TCP()
<Ether type=IPv4 |<IP frag=0 proto=tcp |<TCP |>>>
>>> IP()/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' |>>>
>>> Ether()/IP()/UDP()
<Ether type=IPv4 |<IP frag=0 proto=udp |<UDP |>>>
>>> IP(protp=55)/TCP()
<IP frag=0 proto=55 |<TCP |>>
通过_
查看数据包的最新信息
>> IP(_)
<IP version=4 ihl=5 tos=0x0 len=20 id=1 flags= frag=0 ttl=64 proto=hopopt chksum=0x7ce7 src=127.0.0.1 dst=127.0.0.1 |>
展示数据
-
show - 例如pcap.show,或者pcap[0].show
-
show() - 例如pcap.show(),或者pcap[0].show()
-
sniff获取的数据可以列表的方式操作,每一个元素都是一条捕获的消息,show可以显示概要信息,show()则会显示详细的内容,或者格式化显示内容
如
ptk = Ether()/IP()/TCP()/'HELLO WORLD'
>>> ptk.show()
###[ Ethernet ]###
dst= ff:ff:ff:ff:ff:ff
src= 00:00:00:00:00:00
type= IPv4
###[ IP ]###
version= 4
ihl= None
tos= 0x0
len= None
id= 1
flags=
frag= 0
ttl= 64
proto= tcp
chksum= None
src= 127.0.0.1
dst= 127.0.0.1
\options\
###[ TCP ]###
sport= ftp_data
dport= http
seq= 0
ack= 0
dataofs= None
reserved= 0
flags= S
window= 8192
chksum= None
urgptr= 0
options= []
###[ Raw ]###
load= 'HELLO WORLD'
##查询相关信息
>>> ptk[TCP]
<TCP |<Raw load='HELLO WORLD' |>>
>>> ptk[TCP].show()
###[ TCP ]###
sport= ftp_data
dport= http
seq= 0
ack= 0
dataofs= None
reserved= 0
flags= S
window= 8192
chksum= None
urgptr= 0
options= []
###[ Raw ]###
load= 'HELLO WORLD'
>>> ptk[TCP].dport
80
>>>
收发数据包
在创建好数据包后,需要收发数据包,Scapy中提供了多个用来完成发送数据包的函数,主要有只发不收和既发又收两种情况
对于某种方法至于输入其名字即可获得其相关用法
send
<function scapy.sendrecv.send(x, inter=0, loop=0, count=None, verbose=None, realtime=None, return_packets=False, socket=None, iface=None, *args, **kargs)>
- 只发不收
send和sendp
send(),在第三层发包
send(IP(dst="www.baidu.com",ttl=2)/ICMP())
sendp(),在第二层发包
sendp(Ether()/IP(dst=www.baidu.com))
sendp(rdpcap("/home/jma/Desktop/scapy/scapy_test.pcap")) #直接发送捕获的数据包
iface - 指定网卡
loop - 开启循环发送
inter - 设置间隔时间
如果希望发送一个内容是随机填充的数据包,而且又要保证这个数据包的正确性,可以使用fuzz()函数
sr(IP(dst="192.168.1.107")/fuzz(())
- 既发又收
sr()、sr1()第三层
srp()第二层,与sr类似
src
sr()函数是Scapy的核心,它的返回值是两个列表,第一个列表是收到了应答的包和对应的应答,第二个列表是未收到应答的包。
>>> ans,unans=sr(IP(dst="192.168.220.2")/ICMP())
Begin emission:
Finished sending 1 packets.
.*
Received 2 packets, got 1 answers, remaining 0 packets
>>> ans
<Results: TCP:0 UDP:0 ICMP:1 Other:0>
>>> ans.summary()
IP / ICMP 192.168.220.154 > 192.168.220.2 echo-request 0 ==> IP / ICMP 192.168.220.2 > 192.168.220.154 echo-reply 0 / Padding
sr(IP(dst="192.168.220.157")/TCP(sport=666,dport=(440,443),flags="S")) #发送440到443的SYN标志包
这里使用ans和unans来保存sr()的返回值,因为发出的是一个ICMP请求数据包,而且也收到了一个应答包,所以这个发送的数据包和收到的应答包都被保存到了ans列表中,使用ans.summary()可以查看两个数据包的内容,而unans列表为空。
sr1
sr1()函数和sr()函数作用基本一样,但是值返回一个应答包。只需要使用一个列表就可以保存这个函数的返回值。因此可以使用sr1()函数来测试目标的某个端口是否开放,采用半开扫描(SYN)的办法。
>>> p = sr1(IP(dst="192.168.220.157")/TCP(dport=80,flags="A"))
Begin emission:
Finished sending 1 packets.
.*
Received 2 packets, got 1 answers, remaining 0 packets
>>> p
<IP version=4 ihl=5 tos=0x0 len=40 id=23775 flags=DF frag=0 ttl=64 proto=tcp chksum=0xa367 src=192.168.220.157 dst=192.168.220.154 |<TCP sport=http dport=ftp_data seq=0 ack=0 dataofs=5 reserved=0 flags=R window=0 chksum=0x74f3 urgptr=0 |<Padding load='\x00\x00\x00\x00\x00\x00' |>>>
可以发现目的地址回应了设置SYN标志位的数据包,说明开放了80端口
sniff()
与tcpdump比较相似,可以在自己的程序中捕获经过本机网卡的数据包,需要注意的是不可以实时回显,需要自己设置固定的输出或者终止嗅探后回显,可以使用filter进行过滤
- 过滤参数 - filter,可以wireshark支持的形式设定相关参数,例如filter='tcp port 80'
- 自定义过滤函数 - lfilter,可以指定一个函数,用来实现自己的过滤,例如lfilter=lambda p: p.haslayer(Raw),指定过滤所有带有data的包。filter和lfilter可以同时存在
- 包个数 - count,例如count=20,捕获20个包之后停止
- 接口 - iface,例如iface='eth1',多个接口可以通过list方式指定,例如iface=['eth0','eth1','lo']
- 自定义停止捕获函数 - stop_filter,可以指定一个函数,返回true则停止捕获,例如stop_filter=lambda p: p.haslayer(Raw) and 'stop flag' in p.getlayer(Raw).load,就可以指定在接收到'stop flag'字符之后停止抓包。
在eth0网卡上监听源地址或目的地址为192.168.220.157的30个ICMP数据包
sniff(filter="icmp and host 192.168.220.157",count=30,iface="eth0")
# 此时在其他终端可以发送数据包或者直接ping
<Sniffed: TCP:0 UDP:0 ICMP:30 Other:0>
>>> a = _ #获取上一步结果
>>> a.nsummary() #摘要长度为一行
0000 Ether / IP / ICMP 192.168.220.154 > 192.168.220.157 echo-request 0 / Raw
0001 Ether / IP / ICMP 192.168.220.157 > 192.168.220.154 echo-reply 0 / Raw
0002 Ether / IP / ICMP 192.168.220.154 > 192.168.220.157 echo-request 0 / Raw
0003 Ether / IP / ICMP 192.168.220.157 > 192.168.220.154 echo-reply 0 / Raw
0004 Ether / IP / ICMP 192.168.220.154 > 192.168.220.157 echo-request 0 / Raw
0005 Ether / IP / ICMP 192.168.220.157 > 192.168.220.154 echo-reply 0 / Raw
0006 Ether / IP / ICMP 192.168.220.154 > 192.168.220.157 echo-request 0 / Raw
0007 Ether / IP / ICMP 192.168.220.157 > 192.168.220.154 echo-reply 0 / Raw
0008 Ether / IP / ICMP 192.168.220.154 > 192.168.220.157 echo-request 0 / Raw
存储捕获的数据
pcap=sniff(filter='tcp port 80',count=10, iface='lo')
wrpcap('my.pcap',pcap)
读取pcap文件
pcap=rdpcap('my.pcap')
通过sniff读取离线文件
pcap=sniff(offline='/home/jma/Desktop/scapy/scapy_test.pcap')