Scapy模块
- 简介
- 基本用法
- Scapy的基本操作
- Scapy中的函数
- Scapy常用简单实例
简介
- scapy可以实现对网络数据包的发送、监听和解析等。
基本用法
- 在kali中启动Scapy:
scapy
Scapy的基本操作
- 在Scapy中,每一个协议就是一个类,只需要实例化一个协议类,就可以创建一个该协议的数据包。
- 创建一个IP类型的数据包(IP数据包最重要的就是源地址和目标地址,这两个属性可以使用sec和dst来设置):
>>> ip = IP(dst= "192.168.1.255")
>>> ip
<IP dst=192.168.1.255 |>
>>> target = "192.168.1.0/24"
>>> ip = IP(dst = target)
>>> ip
<IP dst=Net('192.168.1.0/24') |>
>>> [p for p in ip]
- Scapy采用分层形式来构造数据包,通常最下面的一个协议为Ether,然后是IP,再之后是TCP或UDP。IP()函数无法用来构造ARP请求和应答数据包,此时可以使用Ether(),这个函数可以设置发送方和接收方的MAC得地址。例如:
>>> Ether(dst = "ff:ff:ff:ff:ff:ff")
<Ether dst=ff:ff:ff:ff:ff:ff |>
- Scapy中的分层通过符合“ / ”,实现,一个数据包是由多层次协议组成,按照协议自下而上的顺序从左向右排列。
>>> 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类的属性有源地址、目的地址、版本、长度、协议类型、校验和等;TCP类有源端口和目的端口。可以使用ls()函数来查看一个类的属性,例如:
>>> ls(IP())
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 = 0 (0)
chksum : XShortField = None (None)
src : SourceIPField = '127.0.0.1' (None)
dst : DestIPField = '127.0.0.1' (None)
options : PacketListField = [] ([])
IP(src = "192.168.1.10",dst = "192.168.1.125", ttl = 32)
Scapy中的函数
- 发送报文但不处理回应包的函数(刚刚使用的IP()作用是产生一个IP数据包,并未将其发送),send()是用来发送IP的数据包(第三层),sendp()是用来发送Ether()数据包的(第二层)。如:
>>> send(IP(dst = "192.168.1.10")/ICMP())
.
Sent 1 packets.
>>> sendp(Ether(dst = "ff:ff:ff:ff:ff:ff"))
.
Sent 1 packets.
- 发送随机填充数据包,并保证这个数据包的正确性:函数fuzz(),如
>>> IP(dst = "192.168.0.10")/fuzz(TCP())
<IP frag=0 proto=tcp dst=192.168.0.10 |<TCP |>>
- 发送和接收数据包的函数,sr()和sr1()主要作用于第三层,例如IP和ARP等,srp()用于第二层。
>>> sr(IP(dst = "192.168.1.10")/ICMP())
Begin emission:
.Finished sending 1 packets.
*
Received 2 packets, got 1 answers, remaining 0 packets
(<Results: TCP:0 UDP:0 ICMP:1 Other:0>, <Unanswered: TCP:0 UDP:0 ICMP:0 Other:0>)
sr()
函数是Scapy的核心,返回值是两个列表,第一个列表是收到了应答的包和对应的应答,第二个列表是未收到应答的包。
>>> ans,unans = sr(IP(dst = "192.168.1.10")/ICMP())
Begin emission:
.Finished sending 1 packets.
*
Received 2 packets, got 1 answers, remaining 0 packets
- ans 保存发送的数据包和收到的应答包,使用 ans.summary()可以查看两个包的内容,unans列表为空。
>>> ans.summary()
IP / ICMP 192.168.1.125 > 192.168.1.10 echo-request 0 ==> IP / ICMP 192.168.1.10 > 192.168.1.125 echo-reply 0 / Padding
- sr1()函数跟sr()函数作用基本相同,但只返回一个应答包,只需要一个列表。例如:
>>> ans = sr1(IP(dst = "192.168.1.10")/ICMP())
Begin emission:
.Finished sending 1 packets.
*
Received 2 packets, got 1 answers, remaining 0 packets
>>> ans
<IP version=4 ihl=5 tos=0x0 len=28 id=8154 flags= frag=0 ttl=128 proto=icmp chksum=0x97aa src=192.168.1.10 dst=192.168.1.125 |<ICMP type=echo-reply code=0 chksum=0xffff id=0x0 seq=0x0 |<Padding load='\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' |>>>
- 可以利用sr1()函数来探测目标的某个端口是否开放,采用半开扫描(SYN)的办法。
>>> sr1(IP(dst = "192.168.1.10")/TCP(dport = 80 ,flags = "S"))
Begin emission:
.Finished sending 1 packets.
..............................
- 另一重要函数
sniff()
,可以在自己的程序中捕获经过本机网卡的数据包。使用sniff()开始监听,捕获的数据包不会即时显示,只有使用Ctrl+C组合键停止监听时才会显示捕获的数据包。例如:
>>> sniff()
^C<Sniffed: TCP:3 UDP:6 ICMP:0 Other:2>
- 这个函数最强大的地方在于可以使用
参数 filter
对数据包进行过滤。例如:
>>> sniff(filter = "host 192.168.1.10")
^C<Sniffed: TCP:2 UDP:0 ICMP:0 Other:0>
>>> sniff(filter = "icmp")
^C<Sniffed: TCP:0 UDP:0 ICMP:0 Other:0>
>>> sniff(filter ="host 192.168.1.10 and icmp")
^C<Sniffed: TCP:0 UDP:0 ICMP:19 Other:0>
- 另外两个重要
参数iface、count
。
- iface可以指定所要进行监听的网卡,例如:
>>> sniff(iface = "eth1")
^C<Sniffed: TCP:0 UDP:2 ICMP:0 Other:0>
- count可以用来指定监听到的数据包的数量,达到指定的数量就会停止监听,例如:
>>> sniff(count = 5)
<Sniffed: TCP:2 UDP:0 ICMP:0 Other:3>
- 设计一个综合性的监听器,在网卡eth0上监听源地址或者目的地址为192.168.1.6的icmp数据包,接收3个这样的数据包就停止监听。(正常情况下是不会去往或来自192.168.1.10的icmp数据包,需要ping)
>>> sniff(filter = "host 192.168.1.10 and icmp", count = 3 , iface = "eth0")
<Sniffed: TCP:0 UDP:0 ICMP:3 Other:0>
>>> a = _
>>> a.nsummary()
0000 Ether / IP / ICMP 192.168.1.125 > 192.168.1.10 echo-request 0 / Raw
0001 Ether / IP / ICMP 192.168.1.10 > 192.168.1.125 echo-reply 0 / Raw
0002 Ether / IP / ICMP 192.168.1.125 > 192.168.1.10 echo-request 0 / Raw
>>> a.summary()
>>> p=IP(dst="192.168.1.10")
>>> p.summary()
'192.168.1.125 > 192.168.1.10 hopopt'
Scapy常用简单实例
- 一次ACK类型的端口扫描,例如:对192.168.1.6的 21、23、80、135、445、3389端口是否被屏蔽(屏蔽不是关闭)进行ACK扫描。
>>> ans,unnas = sr(IP(dst = "192.168.1.6")/TCP(dport = [21,23,80,135,445,3389],flags='A'))
Begin emission:
..*****Finished sending 6 packets.
*
Received 8 packets, got 6 answers, remaining 0 packets
- 正常情况下,一个开放的端口会回应ack数据包,而关闭的端口会回应rst数据包。在网络中,一些不安全的端口(22,135,139,445,3389等)会被过滤,这些端口的状态不是开放或关闭,而是被屏蔽。
- 向目标发送5个标志位置为’A’的TCP数据包,按照TCP三次握手规则,如果端口目标为被过滤,发送的数据包就会得到回应,否则没有回应。
- 查看未被过滤的端口:
>>> for s,r in ans:
... if s[TCP].dport == r[TCP].sport:
... print str(s[TCP].dport) + " is unfiltered."
...
21 is unfiltered.
23 is unfiltered.
80 is unfiltered.
135 is unfiltered.
445 is unfiltered.
3389 is unfiltered.
>>> for s in unnas:
... print str(s[TCP].dport) + " is filtered."
...
- 在python中编写程序来实现查看端口是否被屏蔽的简单程序。
>>> from scapy.all import IP,TCP,sr
>>> ans,unnas = sr(IP(dst = "192.168.1.6")/TCP(dport = [21,23,80,135,445,3839],flags='A'))
Begin emission:
.Finished sending 6 packets
...............................
Received 32 packets, got 0 answers, remaining 6 packets
- 使用Scapy强大的包处理能力来设计一个端口是否开放的扫描器
- 注意,与前例的区别 : 如果端口屏蔽,不会产生任何响应报文;端口开放,在收到syn数据包之后回应一个rst数据包。反之,端口关闭,在收到syn数据包之后回应一个rst数据包。
>>> from scapy.all import fuzz,TCP,IP,sr
>>> ans,unnas = sr(IP(dst="192.168.1.6")/fuzz(TCP(dport=80,flags='S')))
Begin emission:
.Finished sending 1 packets.
.*
Received 3 packets, got 1 answers, remaining 0 packets
>>> for s,r in ans:
... if r[TCP].flags == 18:
... print "This port is Open."
... if r[TCP].flags == 20:
... print "This port is Closed."
...
This port is closed.
>>>