Scapy
知乎:https://zhuanlan.zhihu.com/p/51002301
官方文档:https://scapy.readthedocs.io/en/latest/introduction.html
中文文档:https://www.cntofu.com/book/33/1.md
Scapy is a Python program that enables the user to send, sniff and dissect and forge network packets. This capability allows construction of tools that can probe, scan or attack networks.
In other words, Scapy is a powerful interactive packet manipulation program. It is able to forge or decode packets of a wide number of protocols, send them on the wire, capture them, match requests and replies, and much more. Scapy can easily handle most classical tasks like scanning, tracerouting, probing, unit tests, attacks or network discovery. It can replace hping, arpspoof, arp-sk, arping, p0f and even some parts of Nmap, tcpdump, and tshark).
Scapy also performs very well on a lot of other specific tasks that most other tools can’t handle, like sending invalid frames, injecting your own 802.11 frames, combining techniques (VLAN hopping+ARP cache poisoning, VOIP decoding on WEP encrypted channel, …), etc.
Scapy是一个强大的,用Python编写的交互式数据包处理程序,它能让用户发送、嗅探、解析,以及伪造网络报文,从而用来侦测、扫描和向网络发动攻击。Scapy可以轻松地处理扫描(scanning)、路由跟踪(tracerouting)、探测(probing)、单元测试(unit tests)、攻击(attacks)和发现网络(network discorvery)之类的传统任务。它可以代替hping
,arpspoof
,arp-sk
,arping
,p0f
甚至是部分的Nmap
,tcpdump
和tshark
的功能。
Scapy
在大多数其它工具无法完成的特定任务中也表现优异,比如发送无效帧、添加自定义的802.11的侦、多技术的结合(跳跃攻击(VLAN hopping)+ARP缓存中毒(ARP cache poisoning)、在WEP加密信道(WEP encrypted channel)上的VOIP解码(VOIP decoding))等等。
Scapy
的主要功能是:发送数据包、接收响应数据(sending packets and receiving answers)。
一、linux下安装
官网:https://scapy.readthedocs.io/en/latest/installation.html
pip3 install scapy
注意:
- 数据包无法发送到 localhost(或你自己的主机上的本机 IP 地址)
- 可能无法在 Windows 上捕获 WLAN 流量,原因:wireshark可以尝试关闭混合模式
conf.sniff_promisc = False
二、使用
# 进入scapy
sudo scapy
scapy
进入scapy后,可以用ls()
函数来查看scapy支持的网络协议
除了ls()
外,还可以用lsc()
函数来查看scapy的指令集(函数)。比较常用的函数包括arpcachepoison(用于arp毒化攻击,也叫arp欺骗攻击),arping(用于构造一个ARP的who-has包) ,send(用于发3层报文),sendp(用于发2层报文), sniff(用于网络嗅探,类似Wireshark和tcpdump), sr(发送+接收3层报文),srp(发送+接收2层报文)等等
还可以用使用ls()的携带参数模式
,比如ls(IP)
来查看IP包的各种默认参数
(https://img2020.cnblogs.com/blog/1461466/202006/1461466-20200602205510241-1955828266.png)
" style="zoom: 80%;" />
(一)构造数据包
1、一层数据包
>>> a=IP(ttl=1)
>>> a
<IP ttl=1 |>
>>> a.src
'127.0.0.1'
>>> a.dst='10.1.1.10'
>>> a
<IP ttl=1 dst=10.1.1.10 |>
>>> ls(a)
version : BitField (4 bits) = 4 (4)
ihl : BitField (4 bits) = None (None)
tos : XByteField = 0 (0)
len : ShortField = None (None)
id : ShortField = 1 (1)
flags : FlagsField (3 bits) = <Flag 0 ()> (<Flag 0 ()>)
frag : BitField (13 bits) = 0 (0)
ttl : ByteField = 1 (64)
proto : ByteEnumField = 0 (0)
chksum : XShortField = None (None)
src : SourceIPField = '10.0.2.58' (None)
dst : DestIPField = '10.1.1.10' (None)
options : PacketListField = [] ([])
2、多层数据包
/
操作符在两层之间起到一个组合的作用
>>> a=IP(dst='192.168.10.1')/TCP(sport=RandShort(),dport=80)
>>> a
<IP frag=0 proto=tcp dst=192.168.10.1 |<TCP sport=<RandShort> dport=http |>>
>>> ls(a)
version : BitField (4 bits) = 4 (4)
ihl : BitField (4 bits) = None (None)
tos : XByteField = 0 (0)
len : ShortField = None (None)
id : ShortField = 1 (1)
flags : FlagsField (3 bits) = <Flag 0 ()> (<Flag 0 ()>)
frag : BitField (13 bits) = 0 (0)
ttl : ByteField = 64 (64)
proto : ByteEnumField = 6 (0)
chksum : XShortField = None (None)
src : SourceIPField = '10.0.2.58' (None)
dst : DestIPField = '192.168.10.1' (None)
options : PacketListField = [] ([])
--
sport : ShortEnumField = <RandShort> (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) = <Flag 2 (S)> (<Flag 2 (S)>)
window : ShortField = 8192 (8192)
chksum : XShortField = None (None)
urgptr : ShortField = 0 (0)
options : TCPOptionsField = [] (b'')
>>> IP()
<IP |>
>>> IP()/TCP()
<IP frag=0 proto=TCP |<TCP |>>
>>> Ether()/IP()/TCP()
<Ether type=0x800 |<IP frag=0 proto=TCP |<TCP |>>>
>>> IP()/TCP()/"GET / HTTP/1.0
"
<IP frag=0 proto=TCP |<TCP |<Raw load='GET / HTTP/1.0
' |>>>
>>> Ether()/IP()/IP()/UDP()
<Ether type=0x800 |<IP frag=0 proto=IP |<IP frag=0 proto=UDP |<UDP |>>>>
>>> IP(proto=55)/TCP()
<IP frag=0 proto=55 |<TCP |>>
命令 | 效果 |
---|---|
str(pkt) | 组装数据包 |
hexdump(pkt) | 十六进制转储 |
ls(pkt) | 显示出字段值的列表 |
pkt.summary() | 一行摘要 |
pkt.show() | 针对数据包的展开视图 |
pkt.show2() | 显示聚合的数据包(例如,计算好了校验和) |
pkt.sprintf() | 用数据包字段填充格式字符串 |
pkt.decode_payload_as() | 改变payload的decode方式 |
pkt.psdump() | 绘制一个解释说明的PostScript图表 |
pkt.pdfdump() | 绘制一个解释说明的PDF |
pkt.command() | 返回可以生成数据包的Scapy命令 |
3、一组数据包
传入的是数据段,就会生成一组数据包
>>> a=IP(dst="www.slashdot.org/30") # 该网段中的子网的ip,4个
>>> [i for i in a]
[<IP dst=216.105.38.12 |>, <IP dst=216.105.38.13 |>, <IP dst=216.105.38.14 |>, <IP dst=216.105.38.15 |>]
>>> b=TCP(dport=[3,(600,610)]) # (a, b)表示从a到b范围内所有数据
>>> [i for i in b]
[<TCP dport=3 |>, <TCP dport=600 |>, <TCP dport=601 |>, <TCP dport=602 |>, <TCP dport=603 |>, <TCP dport=604 |>, <TCP dport=605 |>, <TCP dport=606 |>, <TCP dport=nqs |>, <TCP dport=608 |>, <TCP dport=609 |>, <TCP dport=npmp_local |>]
>>> c= IP(dst="www.slashdot.org/30")/TCP(dport=[3,(600,603)])
>>> [i for i in c]
[<IP frag=0 proto=tcp dst=216.105.38.12 |<TCP dport=3 |>>, <IP frag=0 proto=tcp dst=216.105.38.12 |<TCP dport=600 |>>, <IP frag=0 proto=tcp dst=216.105.38.12 |<TCP dport=601 |>>, <IP frag=0 proto=tcp dst=216.105.38.12 |<TCP dport=602 |>>, <IP frag=0 proto=tcp dst=216.105.38.12 |<TCP dport=603 |>>, <IP frag=0 proto=tcp dst=216.105.38.13 |<TCP dport=3 |>>, <IP frag=0 proto=tcp dst=216.105.38.13 |<TCP dport=600 |>>, <IP frag=0 proto=tcp dst=216.105.38.13 |<TCP dport=601 |>>, <IP frag=0 proto=tcp dst=216.105.38.13 |<TCP dport=602 |>>, <IP frag=0 proto=tcp dst=216.105.38.13 |<TCP dport=603 |>>, <IP frag=0 proto=tcp dst=216.105.38.14 |<TCP dport=3 |>>, <IP frag=0 proto=tcp dst=216.105.38.14 |<TCP dport=600 |>>, <IP frag=0 proto=tcp dst=216.105.38.14 |<TCP dport=601 |>>, <IP frag=0 proto=tcp dst=216.105.38.14 |<TCP dport=602 |>>, <IP frag=0 proto=tcp dst=216.105.38.14 |<TCP dport=603 |>>, <IP frag=0 proto=tcp dst=216.105.38.15 |<TCP dport=3 |>>, <IP frag=0 proto=tcp dst=216.105.38.15 |<TCP dport=600 |>>, <IP frag=0 proto=tcp dst=216.105.38.15 |<TCP dport=601 |>>, <IP frag=0 proto=tcp dst=216.105.38.15 |<TCP dport=602 |>>, <IP frag=0 proto=tcp dst=216.105.38.15 |<TCP dport=603 |>>] #len()=4*5=20
命令 | 效果 |
---|---|
summary() | 显示一个关于每个数据包的摘要列表 |
nsummary() | 同上,但规定了数据包数量 |
conversations() | 显示一个会话图表 |
show() | 显示首选表示(通常用nsummary()) |
filter() | 返回一个lambda过滤后的数据包列表 |
hexdump() | 返回所有数据包的一个hexdump |
hexraw() | 返回所以数据包Raw layer的hexdump |
padding() | 返回一个带填充的数据包的hexdump |
nzpadding() | 返回一个具有非零填充的数据包的hexdump |
plot() | 规划一个应用到数据包列表的lambda函数 |
make table() | 根据lambda函数来显示表格 |
(二)发送数据包
1、send——发送IP报文
构造IP报文
ip = IP(dst='120.78.120.251')
查看IP报文
ls(ip) # ip的src变为本机,dst变为120.78.120.251
.png)
发送报文
send(ip,iface=80)
2、sendp——发送ARP报文
send(),sendp()函数,两者的区别是前者是发送三层报文,后者则是发送二层报文
用sendp()配合Ether()和arp()函数来构造一个ARP报文
sendp(Ether(dst='ff:ff:ff:ff:ff:ff') / ARP(hwsrc = '00:0c:29:72:b2:b5', psrc = '192.168.2.1', hwdst = 'ff:ff:ff:ff:ff:ff', pdst = '120.78.120.251') / 'abc', iface='80')
# 这里我们构造了一个源MAC地址为00:0c:29:72:b2:b5, 源IP地址为192.168.2.1, 目标MAC地址为ff:ff:ff:ff:ff:ff,目标IP地址为120.78.120.251,payload为abc的ARP报文
3、sr——发送接收3层报文
-
send()和sendp()函数只能发送报文,而不能接收返回的报文
-
查看返回的3层报文,需要用到
sr()
函数
用sr()向S1发一个ICMP包,可以看到返回的结果是一个tuple(元组),该元组里的元素是两个列表,其中一个列表叫Results(响应),另一个叫Unanswered(未响应)。
sr(IP(dst = '192.168.2.11') / ICMP())
-
如果向一个不存在的IP,由于没有响应,所以你能看到Unanswered后面的ICMP:显示了1
-
使用两个变量接收返回的元组
ans, unans = sr(IP(dst = '192.168.2.11') / ICMP())
for sent,received in ans:
if received.haslayer(TCP) and str(received[TCP].flags) == "SA":
pass
- 用show(), summary(), nsummary()等方法来查看ans的内容
ans.show()
ans.summary()
ans.nsummary()
- 查看更多ans信息,
ans
实为一个SndRcvList
列表
ans[0] # (send数据包, recv数据包)
- 可以看到ans[0]本身又是一个包含了两个元素的元组,我们可以继续用ans[0][0]和ans[0][1]查看这两个元素
ans[0][0]
ans[0][1] # sr1的返回值,recv数据包
4、sr1——发送接收3层报文
sr1
是sr
的一种变体,返回的是应答(ans[0][1]
)数据包,即sr
返回值中接收的数据包
srcport = RandShort()
received = sr1(IP(dst = target)/TCP(sport=srcport, dport = port, flags = "S"), timeout=1) # Send SYN and recieve RST-ACK or SYN-ACK
# received ==> ans[0][1]
if str(received.getlayer(TCP).flags)=="SA":
pass
5、srp——发送接收2层报文
6、tcp——发送接收4层TCP SYN报文
- 在scapy上使用ip()和tcp()函数来构造一个目的地IP为192.168.2.11,源端口(sport)为30,目的端口(dport)为80的TCP SYN报文
ans, unans = sr(IP(dst = "192.168.2.11") / TCP(sport = 30, dport = 80, flags = "S"))
-
TCP端口号除了手动指定外,还可以使用
RandShort()
,RandNum()
和Fuzz()
这几个函数来自动生成一个随机的端口号-
随机生成一个1-65535之间的端口号,
ans, unans = sr(IP(dst = "192.168.2.11") / TCP(sport = RandShort(), dport = 80, flags = "S"))
-
在指定范围内自动生成一个端口号,
RandNum(100-50000)
ans, unans = sr(IP(dst = "192.168.2.11") / TCP(sport = RandNum(100-50000), dport = 80, flags = "S"))
-
Fuzz()
检查sport
是否漏写,漏写会自动生成一个sportans, unans = sr(IP(dst = "192.168.2.11") / fuzz(TCP(dport = 80, flags = "S"))
-
三、TCP SYN扫描
当发送端的TCP SYN包发出后,大致会有下面四种情况发生:
- 目的端口在接收端打开,收到SYN包的接收端回复SYN/ACK包给发送端,收到SYN/ACK包的发送端此时知道目的端口是打开的(open)。
- 目的端口在接收端被关闭,收到SYN包的接收端回复RST包给发送端,告知发送端该目的端口已经被关闭了(closed)。
- 如果发送端和接收端之间有防火墙或者使用ACL进行包过滤的路由器,那么SYN包在到达接收端之前就被防火墙或路由器拦截下来,此时防火墙或路由器会回复一个类型3(Unreachable,不可达)的ICMP包(注意不再是TCP包)给发送端告知该目的端口已经被过滤了(filtered)。
- 如果ICMP在防火墙或路由器上被关闭了,这时SYN包会被防火墙、路由器"静悄悄"地丢弃,路由器和防火墙不会发送类型3的ICMP包告知发送端。此时发送端收不到任何回应(no response),这里我们同样可以判断该目的端口已经被过滤了(filtered)。
四、Sniff
[wireshark BPF过滤规则](/home/up/文档/markdown文档/wireshark BPF过滤规则.md)
sniff(filter="tcp port 80 and src host 192.168.88.3",iface="any", prn=function, count=N)
# filter
指定scapy嗅探的数据包,一个 BPF(wireshark类型)的过滤器,空表示嗅探所有数据包
# iface
设置所需要嗅探的网卡,留空嗅探所有网卡
# prn
指定嗅探到符合过滤器条件的数据包时所调用的回调函数,这个回调函数以接受到的数据包对象作为唯一的参数。
# count
指定嗅探的数据包的个数,留空则默认为嗅探无限个
>>> lsc()
IPID_count : Identify IP id values classes in a list of packets
arpcachepoison : Poison target's cache with (your MAC,victim's IP) couple
arping : Send ARP who-has requests to determine which hosts are up
arpleak : Exploit ARP leak flaws, like NetBSD-SA2017-002.
bind_layers : Bind 2 layers on some specific fields' values.
bridge_and_sniff : Forward traffic between interfaces if1 and if2, sniff and return
chexdump : Build a per byte hexadecimal representation
computeNIGroupAddr : Compute the NI group Address. Can take a FQDN as input parameter
corrupt_bits : Flip a given percentage or number of bits from a string
corrupt_bytes : Corrupt a given percentage or number of bytes from a string
defrag : defrag(plist) -> ([not fragmented], [defragmented],
defragment : defragment(plist) -> plist defragmented as much as possible
dhcp_request : Send a DHCP discover request and return the answer
dyndns_add : Send a DNS add message to a nameserver for "name" to have a new "rdata"
dyndns_del : Send a DNS delete message to a nameserver for "name"
etherleak : Exploit Etherleak flaw
explore : Function used to discover the Scapy layers and protocols.
fletcher16_checkbytes: Calculates the Fletcher-16 checkbytes returned as 2 byte binary-string.
fletcher16_checksum : Calculates Fletcher-16 checksum of the given buffer.
fragleak : --
fragleak2 : --
fragment : Fragment a big IP datagram
fuzz :
getmacbyip : Return MAC address corresponding to a given IP address
getmacbyip6 : Returns the MAC address corresponding to an IPv6 address
hexdiff : Show differences between 2 binary strings
hexdump : Build a tcpdump like hexadecimal view
hexedit : Run hexedit on a list of packets, then return the edited packets.
hexstr : Build a fancy tcpdump like hex from bytes.
import_hexcap : Imports a tcpdump like hexadecimal view
is_promisc : Try to guess if target is in Promisc mode. The target is provided by its ip.
linehexdump : Build an equivalent view of hexdump() on a single line
ls : List available layers, or infos on a given layer class or name.
neighsol : Sends and receive an ICMPv6 Neighbor Solicitation message
overlap_frag : Build overlapping fragments to bypass NIPS
promiscping : Send ARP who-has requests to determine which hosts are in promiscuous mode
rdpcap : Read a pcap or pcapng file and return a packet list
report_ports : portscan a target and output a LaTeX table
restart : Restarts scapy
send : Send packets at layer 3
sendp : Send packets at layer 2
sendpfast : Send packets at layer 2 using tcpreplay for performance
sniff :
split_layers : Split 2 layers previously bound.
sr : Send and receive packets at layer 3
sr1 : Send packets at layer 3 and return only the first answer
sr1flood : Flood and receive packets at layer 3 and return only the first answer
srbt : send and receive using a bluetooth socket
srbt1 : send and receive 1 packet using a bluetooth socket
srflood : Flood and receive packets at layer 3
srloop : Send a packet at layer 3 in loop and print the answer each time
srp : Send and receive packets at layer 2
srp1 : Send and receive packets at layer 2 and return only the first answer
srp1flood : Flood and receive packets at layer 2 and return only the first answer
srpflood : Flood and receive packets at layer 2
srploop : Send a packet at layer 2 in loop and print the answer each time
tcpdump : Run tcpdump or tshark on a list of packets.
tdecode :
traceroute : Instant TCP traceroute
traceroute6 : Instant TCP traceroute using IPv6
traceroute_map : Util function to call traceroute on multiple targets, then
tshark : Sniff packets and print them calling pkt.summary().
wireshark :
wrpcap : Write a list of packets to a pcap file