简介
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.
>> >