交互式数据包处理程序 Scapy 入门指南
Posted Q-WHai
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了交互式数据包处理程序 Scapy 入门指南相关的知识,希望对你有一定的参考价值。
概述
Scapy 是一个强大的交互式数据包处理程序(使用python编写)。它能够伪造或者解码大量的网络协议数据包,能够发送、捕捉、匹配请求和回复包等等。它可以很容易地处理一些典型操作,比如端口扫描,tracerouting,探测,单元测试,攻击或网络发现(可替代hping,NMAP,arpspoof,ARP-SK,arping,tcpdump,tethereal,P0F等)。最重要的他还有很多更优秀的特性——发送无效数据帧、注入修改的802.11数据帧、在WEP上解码加密通道(VOIP)、ARP缓存攻击(VLAN)等,这也是其他工具无法处理完成的。下面就先通过一些实例来学习一下 Scapy 这个强大的工具吧。
版权说明
著作权归作者所有。
商业转载请联系作者获得授权,非商业转载请注明出处。
本文作者:Coding-Naga
发表日期: 2016年4月13日
本文链接:http://blog.csdn.net/lemon_tree12138/article/details/51141440
来源:CSDN
更多内容:分类 >> 黑客的隐形衣
实验环境
- Centos 6.5
- Python 2.6.6
- setuptools-20.7.0
- prettytable-0.7.2
- Scapy (2.3.2)
功能列表
基本测试
登入 Scapy 环境
$ scapy
框架包的引入
>>> from scapy.all import *
这里要注意的是不要使用右边的这种方式导入包:*from scapy import **
查看配置信息
conf 变量保存了配置信息
>>> conf
ASN1_default_codec = <ASN1Codec BER[1]>
AS_resolver = <scapy.as_resolvers.AS_resolver_multi instance at 0x13c1dd0>
( 此处省略 N 行 )
verb = 2
version = '2.3.2'
warning_threshold = 5
wepkey = ''
查看 scapy 支持的指令集
>>> lsc()
arpcachepoison : Poison target's cache with (your MAC,victim's IP) couple
arping : Send ARP who-has requests to determine which hosts are up
bind_layers : Bind 2 layers on some specific fields' values
corrupt_bits : Flip a given percentage or number of bits from a string
( 此处省略 N 行 )
wireshark : Run wireshark on a list of packets
wrpcap : Write a list of packets to a pcap file
比如这里的 arping ,我们就可以这样来使用:
arping
得到所在局域网内所有可用的ip与mac的对应关系
>>> arping("172.16.2.79/80")
Begin emission:
Finished to send 1 packets.
*
Received 1 packets, got 1 answers, remaining 0 packets
30:5a:3a:45:1a:28 172.16.2.79
(<ARPing: TCP:0 UDP:0 ICMP:0 Other:1>, <Unanswered: TCP:0 UDP:0 ICMP:0 Other:0>)
查看 scapy 中已实现的网络协议
缺省参数模式
>>> ls()
AH : AH
ARP : ARP
ASN1_Packet : None
ATT_Error_Response : Error Response
ATT_Exchange_MTU_Request : Exchange MTU Request
( 此处省略 N 行 )
_IPv6ExtHdr : Abstract IPV6 Option Header
_MobilityHeader : Dummy IPv6 Mobility Header
携带参数模式
>>> ls(UDP)
sport : ShortEnumField = (53)
dport : ShortEnumField = (53)
len : ShortField = (None)
chksum : XShortField = (None)
ls() 中携带的参数可以是任何的一个具体的包,常用的有ARP、Ether、ICMP、IP、UDP、TCP,也支持SNMP、DHCP、STP等。
IP 模块的使用
我们可以像在 python 中一样实例化一个 IP 对象。
>>> data = IP()
>>> data
<IP |>
也可以传入需要自定义的参数
>>> data = IP(dst="172.16.2.79")
>>> data
<IP dst=172.16.2.79 |>
查看 IP 模块对象的所有信息
>>> data = IP()
>>> data.show()
###[ IP ]###
version= 4
ihl= None
tos= 0x0
len= None
id= 1
flags=
frag= 0
ttl= 64
proto= ip
chksum= None
src= 127.0.0.1
dst= 127.0.0.1
\\options\\
>>> data = IP(dst="172.16.2.79")
>>> data.show()
###[ IP ]###
version= 4
ihl= None
tos= 0x0
len= None
id= 1
flags=
frag= 0
ttl= 64
proto= ip
chksum= None
src= 172.16.2.91
dst= 172.16.2.79
\\options\\
发送报文
可以将上面的 IP 对象封装成一个数据包发送出去。
>>> send(data, iface="eth0")
.
Sent 1 packets.
接收报文
这边我所做的测试是,在本地开启两个 Putty 客户端,远程连接 172.16.2.91 虚拟机。 putty-1 用于 scapy 监听接收数据包, putty-2 用于向 172.16.2.91 发送指令。这里我发送的测试指令是 ls.
测试的结果如下:
>>> receive = sniff(filter="tcp and host 172.16.2.79", count=2)
>>> receive
<Sniffed: TCP:2 UDP:0 ICMP:0 Other:0>
>>> receive.show()
0000 Ether / IP / TCP 172.16.2.79:58081 > 172.16.2.91:ssh A / Padding
0001 Ether / IP / TCP 172.16.2.79:61186 > 172.16.2.91:ssh PA / Raw
>>> receive[0]
<Ether dst=08:00:27:24:b8:a3 src=30:5a:3a:45:1a:28 type=0x800 |<IP version=4L ihl=5L tos=0x0 len=40 id=20835 flags=DF frag=0L ttl=64 proto=tcp chksum=0x8ca2 src=172.16.2.79 dst=172.16.2.91 options=[] |<TCP sport=58081 dport=ssh seq=2977133173 ack=3389913828 dataofs=5L reserved=0L flags=A window=16249 chksum=0x47bd urgptr=0 |<Padding load='\\x00\\x00\\x00\\x00\\x00\\x00' |>>>>
>>> receive[1]
<Ether dst=08:00:27:24:b8:a3 src=30:5a:3a:45:1a:28 type=0x800 |<IP version=4L ihl=5L tos=0x0 len=104 id=20838 flags=DF frag=0L ttl=64 proto=tcp chksum=0x8c5f src=172.16.2.79 dst=172.16.2.91 options=[] |<TCP sport=61186 dport=ssh seq=3311241534 ack=3819002209 dataofs=5L reserved=0L flags=PA window=16277 chksum=0x4540 urgptr=0 options=[] |<Raw load='\\xc0\\xday\\xa2X\\xe5\\'=QS\\xf9\\x1e\\xe2|\\xa0\\xb4\\xb5Y\\r\\xb0e\\x86\\x02\\x13x\\x19E[\\x94\\x0c\\xff\\xec#\\x1c?;W\\xab\\x18\\xf6"\\x90\\'\\xd9\\x94\\x01G\\xb0\\xc6\\x07\\x08\\xc3\\'\\r\\x7f\\xa9jo\\xa1\\x04\\xc1\\\\\\x13y' |>>>>
更多详细过程,请参见下面的数据嗅探及过滤章节。
细说数据发送
在上面的基本测试阶段,对发送数据进行一些常规测试。本节会是对发送数据的一个全面解析。
send
在第三层发送数据包,但没有接收功能。
>>> send(IP(dst="www.baidu.com",ttl=1)/ICMP())
.
Sent 1 packets.
>>> send(IP(dst="192.168.115.188")/ICMP())
.
Sent 1 packets.
>>> send(IP(dst="www.baidu.com")/UDP()/NTP(version=4), loop=2)
>>> send(IP(dst="www.baidu.com")/fuzz(UDP()/NTP(version=4)), loop=2)
fuzz函数的作用:可以更改一些默认的不可以被计算的值(比如校验和checksums),更改的值是随机的,但是类型是符合字段的值的。
向某一个 IP 发送一个数据包
data_sender.py
import struct
from scapy.all import *
# data = struct.pack('=BHI', 0x12, 20, 1000)
data = struct.pack('=BHI', 0x12, 0x4EF3, 0x76)
pkt = IP(src='172.16.2.91', dst='172.16.2.79')/UDP(sport=12345,dport=5555)/data
send(pkt, inter=1, count=5)
上面的 python 代码的功能是从 172.16.2.91/12345 每隔 1 秒就向 172.16.2.79/5555 发送 5 个相同的数据包,这个数据包中包含了三个数据,分别为:0x12, 0x4EF3, 0x76。如果在 172.16.2.79 上使用抓包工具,也是可以捕获这个数据包的。
sendp
在第二层发送数据包,同样没有接收功能。
>>> sendp(Ether()/IP(dst="www.baidu.com",ttl=1)/ICMP())
.
Sent 1 packets.
>>> sendp(Ether()/IP(dst="192.168.115.188",ttl=(1,4)),iface="eth0")
....
Sent 4 packets.
>>> sendp("hello ,i am walfred ",iface="eth0",loop=1,inter=0.2)
sr
在第三层发送数据包,有接收功能。
>>> sr(IP(dst="www.baidu.com")/TCP(dport=[21, 22, 23]))
Begin emission:
.Finished to send 3 packets.
.......^C
Received 8 packets, got 0 answers, remaining 3 packets
(<Results: TCP:0 UDP:0 ICMP:0 Other:0>, <Unanswered: TCP:3 UDP:0 ICMP:0 Other:0>)
>>> p = sr(IP(dst="www.baidu.com",ttl=1)/ICMP())
Begin emission:
....Finished to send 1 packets.
...*
Received 8 packets, got 1 answers, remaining 0 packets
>>> p
(<Results: TCP:0 UDP:0 ICMP:1 Other:0>, <Unanswered: TCP:0 UDP:0 ICMP:0 Other:0>)
>>> p[0]
<Results: TCP:0 UDP:0 ICMP:1 Other:0>
>>> p[0].show()
0000 IP / ICMP 172.16.2.91 > 180.97.33.108 echo-request 0 ==> IP / ICMP 172.16.2.20 > 172.16.2.91 time-exceeded ttl-zero-during-transit / IPerror / ICMPerror
连续发送ttl=1,2,3,4四个包的情况
>>> p=sr(IP(dst="www.baidu.com",ttl=(1,4))/ICMP())
Begin emission:
....*.*Finished to send 4 packets.
.*.*
Received 11 packets, got 4 answers, remaining 0 packets
>>> p
(<Results: TCP:0 UDP:0 ICMP:4 Other:0>, <Unanswered: TCP:0 UDP:0 ICMP:0 Other:0>)
>>> p[0].show()
0000 IP / ICMP 172.16.2.91 > 180.97.33.107 echo-request 0 ==> IP / ICMP 172.16.2.20 > 172.16.2.91 time-exceeded ttl-zero-during-transit / IPerror / ICMPerror
0001 IP / ICMP 172.16.2.91 > 180.97.33.107 echo-request 0 ==> IP / ICMP 121.225.2.1 > 172.16.2.91 time-exceeded ttl-zero-during-transit / IPerror / ICMPerror
0002 IP / ICMP 172.16.2.91 > 180.97.33.107 echo-request 0 ==> IP / ICMP 218.2.151.29 > 172.16.2.91 time-exceeded ttl-zero-during-transit / IPerror / ICMPerror / Padding
0003 IP / ICMP 172.16.2.91 > 180.97.33.107 echo-request 0 ==> IP / ICMP 202.102.69.70 > 172.16.2.91 time-exceeded ttl-zero-during-transit / IPerror / ICMPerror / Padding
sr1
在第三层发送数据包,有接收功能,但只接收第一个包。以上面的发送四个包为例
>>> q=sr1(IP(dst="www.baidu.com",ttl=(1,4))/ICMP())
Begin emission:
.....*.**Finished to send 4 packets.
..*
Received 12 packets, got 4 answers, remaining 0 packets
>>> q
<IP version=4L ihl=5L tos=0xc0 len=56 id=30311 flags= frag=0L ttl=128 proto=icmp chksum=0x670e src=172.16.2.20 dst=172.16.2.91 options=[] |<ICMP type=time-exceeded code=ttl-zero-during-transit chksum=0xf4ff reserved=0 length=0 unused=None |<IPerror version=4L ihl=5L tos=0x0 len=28 id=1 flags= frag=0L ttl=1 proto=icmp chksum=0x35a8 src=172.16.2.91 dst=180.97.33.108 options=[] |<ICMPerror type=echo-request code=0 chksum=0xf7ff id=0x0 seq=0x0 |>>>>
>>> q.show();
###[ IP ]###
version= 4L
ihl= 5L
tos= 0xc0
len= 56
id= 30311
flags=
frag= 0L
ttl= 128
proto= icmp
chksum= 0x670e
src= 172.16.2.20
dst= 172.16.2.91
\\options\\
###[ ICMP ]###
type= time-exceeded
code= ttl-zero-during-transit
chksum= 0xf4ff
reserved= 0
length= 0
unused= None
###[ IP in ICMP ]###
version= 4L
ihl= 5L
tos= 0x0
len= 28
id= 1
flags=
frag= 0L
ttl= 1
proto= icmp
chksum= 0x35a8
src= 172.16.2.91
dst= 180.97.33.108
\\options\\
###[ ICMP in ICMP ]###
type= echo-request
code= 0
chksum= 0xf7ff
id= 0x0
seq= 0x0
srloop
在第三层工作
>>> p=srloop(IP(dst="www.baidu.com",ttl=1)/ICMP())
RECV 1: IP / ICMP 172.16.2.20 > 172.16.2.91 time-exceeded ttl-zero-during-transit / IPerror / ICMPerror
RECV 1: IP / ICMP 172.16.2.20 > 172.16.2.91 time-exceeded ttl-zero-during-transit / IPerror / ICMPerror
RECV 1: IP / ICMP 172.16.2.20 > 172.16.2.91 time-exceeded ttl-zero-during-transit / IPerror / ICMPerror
^C
Sent 3 packets, received 3 packets. 100.0% hits.
>>> p=srloop(IP(dst="www.baidu.com",ttl=1)/ICMP(),inter=3,count=2)
RECV 1: IP / ICMP 172.16.2.20 > 172.16.2.91 time-exceeded ttl-zero-during-transit / IPerror / ICMPerror
RECV 1: IP / ICMP 172.16.2.20 > 172.16.2.91 time-exceeded ttl-zero-during-transit / IPerror / ICMPerror
Sent 2 packets, received 2 packets. 100.0% hits.
这里第一条语句在执行时,将会不停的ping百度,第二条执行时每隔3秒ping一次,一共执行两次。inter表示间隔,count记录次数。
srp
在第二层发送数据包,有接收功能。
>>> srp(IP(dst="192.168.115.1")/TCP(dport=[21,22,23]))
>>> p=srp(IP(dst="www.baidu.com",ttl=1)/ICMP())
srp1
在第二层发送数据包,有接收功能,但只接收第一个包。以上面的发送四个包为例
>>> q=srp1(IP(dst="www.baidu.com",ttl=(1,4))/ICMP())
srploop
在第三层工作
>>> p = srploop(IP(dst="172.16.2.79", ttl=4)/TCP())
fail 1: IP / TCP 172.16.2.91:ftp_data > 172.16.2.79:http S
fail 1: IP / TCP 172.16.2.91:ftp_data > 172.16.2.79:http S
fail 1: IP / TCP 172.16.2.91:ftp_data > 172.16.2.79:http S
send...
Sent 3 packets, received 0 packets. 0.0% hits.
>>> p = srploop(IP(dst="www.baidu.com", ttl=4)/ICMP())
fail 1: IP / ICMP 172.16.2.91 > 180.97.33.107 echo-request 0
fail 1: IP / ICMP 172.16.2.91 > 180.97.33.107 echo-request 0
send...
Sent 2 packets, received 0 packets. 0.0% hits.
端口扫描
TCP 连接扫描
客户端与服务器建立 TCP 连接要进行一次三次握手,如果进行了一次成功的三次握手,则说明端口开放。
# encoding=utf-8
import logging
logging.getLogger("scapy.runtime").setLevel(logging.ERROR)
from scapy.all import *
dst_ip = "180.97.33.107" # 百度 IP
src_port = RandShort()
dst_port = 80
tcp_connect_scan_resp = sr1(IP(dst=dst_ip)/TCP(sport=src_port, dport=dst_port, flags="S"), timeout=10)
if str(type(tcp_connect_scan_resp)) == "":
print("Closed")
elif tcp_connect_scan_resp.haslayer(TCP):
if tcp_connect_scan_resp.getlayer(TCP).flags == 0x12:
send_rst = sr(IP(dst=dst_ip)/TCP(sport=src_port, dport=dst_port, flags="AR"), timeout=10)
print("Open")
elif tcp_connect_scan_resp.getlayer(TCP).flags == 0x14:
print("Closed")
Begin emission:
...Finished to send 1 packets.
.*
Received 5 packets, got 1 answers, remaining 0 packets
Begin emission:
.Finished to send 1 packets.
.................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................
Received 498 packets, got 0 answers, remaining 1 packets
Open
TCP SYN 扫描(也称为半开放扫描或stealth扫描)
同 TCP 连接扫描非常相似。同样是客户端向服务器发送一个带有 SYN 标识和端口号的数据包,如果目标端口开发,则会返回带有 SYN 和 ACK 标识的 TCP 数据包。但是,这时客户端不会返回 RST+ACK 而是返回一个只带有 RST 标识的数据包。这种技术主要用于躲避防火墙的检测。
# encoding=utf-8
import logging
logging.getLogger("scapy.runtime").setLevel(logging.ERROR)
from scapy.all import *
dst_ip = "180.97.33.107" # 百度 IP
src_port = RandShort()
dst_port = 80
stealth_scan_resp = sr1(IP(dst=dst_ip)/TCP(sport=src_port, dport=dst_port, flags="S"), timeout=10)
if str(type(stealth_scan_resp)) == "":
print("Filtered")
elif stealth_scan_resp.haslayer(TCP):
if stealth_scan_resp.getlayer(TCP).flags == 0x12:
send_rst = sr(IP(dst=dst_ip)/TCP(sport=src_port, dport=dst_port, flags="R"), timeout=10)
print("Open")
elif stealth_scan_resp.getlayer(TCP).flags == 0x14:
print("Closed")
elif stealth_scan_resp.haslayer(ICMP):
if int(stealth_scan_resp.getlayer(ICMP).type) == 3 and int(stealth_scan_resp.getlayer(ICMP).code) in [1,2,3,9,10,13]:
print("Filtered")
Begin emission:
...Finished to send 1 packets.
..*
Received 6 packets, got 1 answers, remaining 0 packets
Begin emission:
Finished to send 1 packets.
..........................................................................................................................................................................................................................................
Received 234 packets, got 0 answers, remaining 1 packets
Open
TCP 圣诞树(Xmas Tree)扫描
在圣诞树扫描中,客户端会向服务器发送带有 PSH,FIN,URG 标识和端口号的数据包给服务器。如果目标端口是开放的,那么不会有任何来自服务器的回应。如果服务器返回了一个带有 RST 标识的 TCP 数据包,那么说明端口处于关闭状态。但如果服务器返回了一个 ICMP 数据包,其中包含 ICMP 目标不可达错误类型3以及 ICMP 状态码为1,2,3,9,10或13,则说明目标端口被过滤了无法确定是否处于开放状态。
# encoding=utf-8
import logging
logging.getLogger("scapy.runtime").setLevel(logging.ERROR)
from scapy.all import *
dst_ip = "180.97.33.107" # 百度 IP
src_port = RandShort()
dst_port = 80
xmas_scan_resp = sr1(IP(dst=dst_ip)/TCP(dport=dst_port, flags="FPU"), timeout=10)
if str(type(xmas_scan_resp)) == "":
print("Open|Filtered")
elif xmas_scan_resp.haslayer(TCP):
if xmas_scan_resp.getlayer(TCP).flags == 0x14:
print("Closed")
elif xmas_scan_resp.haslayer(ICMP):
if int(xmas_scan_resp.getlayer(ICMP).type) == 3 and int(xmas_scan_resp.getlayer(ICMP).code) in [1,2,3,9,10,13]:
print("Filtered")
Begin emission:
...Finished to send 1 packets.
.....................................................................................................................................................................................................................................................................................................................................................................................................................................................................
Received 456 packets, got 0 answers, remaining 1 packets
Traceback (most recent call last):
File "scan.py", line 12, in <module>
elif(xmas_scan_resp.haslayer(TCP)):
AttributeError: 'NoneType' object has no attribute 'haslayer'
这里扫描百度(外网)出现异常状况,于是将百度的 IP 地址替换成了本地的主机 IP 地址。再次执行结果如下:
Begin emission:
...Finished to send 1 packets.
.*
Received 5 packets, got 1 answers, remaining 0 packets
Closed
TCP FIN 扫描
FIN 扫描会向服务器发送带有 FIN 标识和端口号的 TCP 数据包。如果没有服务器端回应则说明端口开放。如果服务器返回一个 RST 数据包,则说明目标端口是关闭的。如果服务器返回了一个 ICMP 数据包,其中包含 ICMP 目标不可达错误类型3以及 ICMP 代码为1,2,3,9,10或13,则说明目标端口被过滤了无法确定端口状态。
# encoding=utf-8
import logging
logging.getLogger("scapy.runtime").setLevel(logging.ERROR)
from scapy.all import *
dst_ip = "180.97.33.107" # 百度 IP
src_port = RandShort()
dst_port = 80
fin_scan_resp = sr1(IP(dst=dst_ip)/TCP(dport=dst_port, flags="F"), timeout=10)
if str(type(fin_scan_resp)) == "":
print("Open|Filtered")
elif fin_scan_resp.haslayer(TCP):
if fin_scan_resp.getlayer(TCP).flags == 0x14:
print("Closed")
elif fin_scan_resp.haslayer(ICMP):
if int(fin_scan_resp.getlayer(ICMP).type) == 3 and int(fin_scan_resp.getlayer(ICMP).code) in [1,2,3,9,10,13]:
print("Filtered")
Begin emission:
...Finished to send 1 packets.
..........................................................................................................................................................................................................
Received 205 packets, got 0 answers, remaining 1 packets
Traceback (most recent call last):
File "scan.py", line 12, in <module>
elif(fin_scan_resp.haslayer(TCP)):
AttributeError: 'NoneType' object has no attribute 'haslayer'
与 Xmas Tree 扫描类似,这里同样切换成 172.16.2.79 的本地地址。再次测试,结果如下:
Begin emission:
...Finished to send 1 packets.
.*
Received 5 packets, got 1 answers, remaining 0 packets
Closed
TCP 空扫描(Null)
# encoding=utf-8
import logging
logging.getLogger("scapy.runtime").setLevel(logging.ERROR)
from scapy.all import *
dst_ip = "180.97.33.107" # 百度 IP
src_port = RandShort()
dst_port = 80
null_scan_resp = sr1(IP(dst=dst_ip)/TCP(dport=dst_port, flags=""), timeout=10)
if str(type(null_scan_resp)) == "":
print("Open|Filtered")
elif null_scan_resp.haslayer(TCP):
if null_scan_resp.getlayer(TCP).flags == 0x14:
print("Closed")
elif null_scan_resp.haslayer(ICMP):
if int(null_scan_resp.getlayer(ICMP以上是关于交互式数据包处理程序 Scapy 入门指南的主要内容,如果未能解决你的问题,请参考以下文章