1. 理解数据包嗅探和伪造的原理
2. 学习scapy库函数,编写简单的嗅探器和欺骗程序
嗅探是指窃听网络中流经的数据包。在局域网中,数据包会以广播的形式广播到局域网内所有主机的网卡,网卡会检查数据帧头部的目的地址是否与本主机的MAC地址相匹配,若匹配,就会接收,否则只是进行简单的丢弃。那么我们的嗅探程序并不会监听到数据包。实际上,网卡有一种特殊的模式----‘混杂模式’,在该模式下,网卡会将从网络中接收到的每个数据帧,即使MAC与目标MAC并不匹配;在混杂模式下,嗅探程序便可正常工作,但是需要将网卡设置成混杂模式需要较高的权限------root权限。
数据包的伪造一般由数据包的构造和发送数据包两个步骤构成。
一般伪造会与嗅探结合使用,首先进行数据包的嗅探,然后根据捕获的数据包内容来伪造响应数据包,达到欺骗的目的。
Scapy是一个强大的交互式数据包处理程序,能够伪造或解码大量的网络协议数据包,能够发送、捕捉、匹配请求和回复包等,需要在root权限下运行。
(1)创建数据包
IP()、TCP()、UDP()、ICMP()等等,同时也可以设置一些参数;用‘/’符号表示两个协议层的组合。
(2)发送和接收数据包:
Send()在第三层发送数据包,但是没有接收响应的功能。
Sr()在第三层发送数据包,并返回响应和未响应的数据包。
(3)抓包
sniff函数:最重要的是filter过滤,可以抓取我们想要类型的数据包,基于BPF过滤规则;prn可以调用回调函数,执行相应的操作;iface可以指定抓包的接口;count可以指定抓包的数量等等。
具体根据实验指导书以及自己的虚拟机来进行设置。
虚拟机镜像 |
抓包工具 |
Python版本 |
Ubuntu 16.04(32bits) |
Wireshark |
Python 3.5.2 |
虚拟机一 |
虚拟机二 |
虚拟机三 |
|
IP地址 |
10.0.2.13 |
10.0.2.14 |
10.0.2.15 |
MAC地址 |
08:00:27:82:f7:f2 |
08:00:27:a2:eb:b9 |
08:00:27:58:26:e2 |
Mask掩码 |
255.255.255.0 |
255.255.255.0 |
255.255.255.0 |
作用 |
运行嗅探程序 |
测试 |
综合利用中ping |
网络前缀 |
10.0.2.0/24 |
1. scapy包的安装
seed@VM:~$ sudo pip install scapy
2. python 程序的运行
(1).py文件的运行
仅仅是展示一下在terminal中如何执行python程序
seed@VM:~$ sudo python3 hello.py
(2)以命令行的形式运行
3. 简单测试scapy的使用
首先以sudo(root权限)运行python3,scapy模块需要root权限
然后导入scapy模块,IP()建立一个IP对象,并用show方法显示IP对象的一些基本信息。
此任务的主要目的是学习如何使用scapy进行数据包的嗅探。
在桌面建立一个python文件,并命名为sniffing.py。(以示例代码为例)
#!usr/bin/env python3
from scapy.all import *
def print_pkt(pkt):
pkt.show()
pkt = sniff(filter='icmp',prn=print_pkt)
这就是一个简单的使用scapy嗅探数据包的程序。
根据filter=‘icmp’可知我们嗅探的数据包的类型为icmp数据包,prn是对我们捕捉到的数据包要执行的回调函数,根据定义,我们知道此回调函数是将我们捕捉到的数据包的一些信息打印出来。
Task 1.1A:运行嗅探程序
我们在Terminal中使用root权限运行该嗅探程序,然后我们在另一个Terminal中执行ping命令,下图为我们嗅探程序捕获到的数据包:
我们会发现已经捕捉到了ICMP数据包,但是会一直捕捉。
因此我们可以在ping命令或者sniff中指定数量。
Ping命令指定发出的ICMP的数量:
seed@VM:~$ ping -c 2 www.baidu.com
Sniff中通过count参数指定嗅探数据包的数量:
pkt = sniff(filter = 'icmp',prn = print_pkt,count = 2)
然后使用普通权限运行上述程序,结果如下:
我们可以发现PermissionError:Operation not permitted,提示我们操作不被允许。
Explanation:之所以报错,是因为使用scapy库函数需要root权限,而且只有在root权限下网卡才会设置成为‘混杂模式’;所以在当前普通用户的权限下该嗅探程序不能运行。
Task 1.1B:使用BPF设置filter
对Sniff函数中的filter参数进行设置,以便我们只抓取我们想要分析的数据包。
捕获ICMP数据包
在Task 1.1中我们已经通过运行一个嗅探程序,并在同一虚拟机上使用ping命令,抓取到了ICMP数据包,接下来我们可以使用另一台虚拟机执行ping命令并ping此虚拟机,来使得嗅探程序进行捕获。
如图,src为我们执行ping命令的虚拟机,dst为我们执行嗅探程序的虚拟机。
捕获来自特定IP且目标端口号为23的任意TCP数据包
Telnet的默认端口号为23,而Telnet是TCP/IP协议族中的一员,是Internet远程登录服务的标准协议和主要方式。
为了捕获到特定IP且目标端口号为23的TCP数据包,我们可以使用主机②执行telnet命令远程登录主机①,此时主机①就相当于一个服务器,在23端口号等待其他主机请求服务,嗅探程序根据过滤规则就可以捕获到来自主机②的远程登录请求且目标端口为23的TCP数据包。 执行telnet命令的特定IP为:10.0.2.14。
首先更改我们的嗅探程序(仅仅修改过滤规则即可):
# 嗅探程序
#!usr/bin/env python3
from scapy.all import *
def print_pkt(pkt):
pkt.show()
pkt = sniff(filter='src host 10.0.2.14 and tcp and (dst port 23)',prn=print_pkt)
方法一:
运行嗅探程序,并在特定IP的主机上执行telnet命令
telnet:远程登录,默认目的端口为23
方法二:
编写一个发送TCP数据包的python文件,在python文件中,我们需要设置IP以及目的端口号并用嗅探程序嗅探。发送TCP数据包的python如下:
#!/usr/bin/python3
from scapy.all import *
a = IP()
a.src = '10.0.2.14'
a.dst = '10.0.2.13'
b = TCP()
b.dport = 23
send(a/b)
a/b表示将TCP数据包封装在IP数据包中,我们设置了该IP数据包的源地址和目的地址(即我们在实验中用到的两台虚拟机),并设置了目的端口号23。
嗅探结果如下:
在这里,我们也能够发现两种方法捕获到的数据包的一些信息并不相同。
捕获另一子网的数据包
编写一个发包程序,在此程序中,我们将发送两个包,第一个发送到特定子网,第二个从特定子网发出数据包,修改IP数据包的源IP与目的IP,我们发包时指定特定子网中的一台主机,否则会对特定子网中的所有主机进行一个广播,发送的包太多;我们使用的特定子网就是实验指导书中提到的128.230.0.0/16。
发包程序:
# 发包程序
#!usr/bin/env python3
from scapy.all import *
a = IP()
a.src = "10.0.2.13"
a.dst = "128.230.0.1"
b = ICMP()
send(a/b)
a.src = "128.230.0.1"
a.dst = "10.0.2.13"
send(a/b)
相应地修改嗅探程序,只需要将filter设置子网号
在这里我们修改了回调函数中的展示方法,summary只展示一行摘要,方便我们观察:
# 嗅探程序
#!usr/bin/env python3
from scapy.all import *
def print_pkt(pkt):
pkt.summary()
pkt = sniff(filter='net 128.230.0.0/16',prn=print_pkt)
运行嗅探程序与发包程序,我们可以捕获到发送到特定子网的数据包和从特定子网上发出的数据包:
Task 1.2:Spoofifing ICMP Packets
1.2.1 示例演示
IP()和ICMP()创建IP对象和ICMP对象,a/b则是将b封装到a中,在示例中我们通过a.dst=’10.0.2.14’的方法对数据包进行了修改,并以send方法发送出去。
1.2.2 ICMP包的欺骗:用任意的源IP地址欺骗ICMP请求包
首先,虚拟机①编写一个send.py文件,修改IP数据包的源IP地址进行ICMP请求数据包的伪造,并发送到虚拟机②。
'''Task 1.2 伪造 ICMP'''
#!/usr/bin/python3
from scapy.all import *
a = IP()
a.arc = '196.128.1.8'
a.dst = '10.0.2.14'
b = ICMP()
send(a/b)
在虚拟机①上利用wireshark观察ICMP数据包的交互。
虚拟机①的原IP为:10.0.2.13,这里我们看到已经用196.128.1.8伪造成功,并收到了回复包。
1.3.1 示例演示
1.3.2 估计两个IP之间的路由器数目
本实验的目的是使用Scapy来估计虚拟机到另一特定IP之间的路由器距离,我们可以设置IP数据包的TTL字段,中间途径的路由器会发送一个ICMP包,以收到的ICMP数据包来获得途径的路由器IP以及路由器的数量。
示例中只是一次发包的过程,但是send()方法只是发送数据包,并不会接收数据包,我们可以使用scapy中的sr方法发送数据包,会返回两个列表,一个是应答包,一个是未应答包,我们可以遍历应答包,从而输出应答包中的源IP地址(即中间路由器的IP地址),以及ttl字段。
编写发包程序,选择1.2.3.4为目的IP:
'''Task 1.3 Traceroute'''
#!/usr/bin/python3
from scapy.all import *
reply,Nreply = sr(IP(dst='1.2.3.4',ttl=(1,20))/ICMP())
for i,j in reply:
print(i.ttl,j.src)
发现经过20个路由器仍没有到达目的IP,继续增大ttl,发现经过很长时间只能收到部分回复包,不能够估计中间路由器的数量。
于是选择172.25.167.65(宿主机的IP)为目的IP:
发现中间只经过一个路由器,且IP为10.0.2.1。
本实验需要用到两台虚拟机:
虚拟机①:10.0.2.13 执行嗅探程序,对ICMP请求进行伪造响应
虚拟机②:10.0.2.15:执行ping命令
正常执行ping命令:一台主机执行ping命令,发出一个ICMP请求,若ping的目的主机存在,则会进行一个ICMP响应。
实验流程:虚拟机②执行ping命令,虚拟机①运行一个嗅探-伪造程序,当监听到此局域网上有ICMP请求报文,便立即使用伪造技术来进行一个响应,并且伪造的ICMP回复数据包与正确的ICMP回复数据包不应该有过大的区别,使得不易被察觉。
1.4.1 观察正确的ICMP回复数据包
在虚拟机②上ping 10.0.2.14并利用嗅探程序捕获,观察echo-request以及echo-reply数据包的各个字段。
可知,当我们伪造ICMP回复包时应该将捕获的ICMP请求包的源目的MAC地址、源/目的IP地址进行交换,将ICMP的type字段置为echo-reply,id为识别号,用于匹配Request与Reply包,seq为报文序列号,用于标记报文顺序,所以id字段与seq字段都需要与请求包保持一致。
1.4.2 嗅探并伪造ICMP回复包
'''Task 1.4 Sniffing and-then Spoofing'''
# 嗅探伪造程序
#!usr/bin/env python3
from scapy.all import *
def print_pkt(pkt):
a = IP()
a.src = pkt[IP].dst
a.dst = pkt[IP].src
b = ICMP()
b.type = 'echo-reply'
b.id = pkt[ICMP].id
b.seq = pkt[ICMP].seq
c = Ether()
c.src = pkt['Ethernet'].dst
c.dst = pkt['Ethernet'].src
send(a/b/c)
pkt =sniff(filter = 'icmp[icmptype]=icmp-echo',prn=print_pkt)
1.4.3 Observation
Ping 1.2.3.4 ----- a non-existing host on the Internet
Ping 10.0.2.64 ---- a non-existing host on the LAN (10.0.2.0/24)
Ping 8.8.8.8 ----- an existing host on the Internet
1.4.4 Consequece
1. 我们可以再运行一个嗅探程序,捕获我们伪造的reply响应包。捕获的Echo-reply说明我们的Sniffing_Spoofing运行成功。
#!/usr/bin/python3py
from scapy.all import *
def print_pkt(pkt):
pkt.show()
pkt = sniff(filter = 'icmp[icmptype]=icmp-echoreply',prn = print_pkt,count = 3)
2. 当ping IP = 1.2.3.4(互联网上不存在的主机),只收到我们伪造的响应包,但是出现一个提示信息:truncated,表示收到的回复报文是被截断的而不是正常的响应包,因为我们发送的Echo-reply包仅有22bytes;同时我们发现ttl值均为64。
3. 当与 IP = 10.0.2.64(本局域网内不存在的主机)时,不会收到响应包,而且会有一个提示信息:目的主机不可到达;而且我们的Sniffing_Spoofing程序也没有发送伪造的Echo-Reply包。
3. 当ping IP = 8.8.8.8(在互联网上真实存在的IP)时,不仅可以收到伪造的Echo-reply包,而且还可以收到正常的Echo-reply包;伪造的reply包的长度为22bytes,ttl为64,而正常的reply包的长度为64bytes,ttl为107,且在正常响应包后有提示信息:DUP! 也就是收到重复的响应包。
1.4.5 Explanation
1. ARP协议的原理 ---- 以ping命令为例
若主机A想要ping主机B,首先会查自己的ARP高速缓存中是否有主机B的IP地址,若有,就在ARP高速缓存中查出对应的MAC地址,否则会在本局域网上以广播的形式发送ARP请求分组,在同一局域网内的主机都会收到此ARP请求分组,只有主机的IP与ARP请求分组中要查询的IP一致,才会作出响应,否则仅仅做丢弃处理;若主机B与主机A不在同一局域网,则主机A发送给主机B的ICMP报文首先需要通过与主机A连接在同一个局域网上的路由器来转发,所以主机A这时需要把路由器的IP地址解析为MAC地址,并向其发送ICMP报文,那么该路由器会在另一局域网继续发送ARP请求分组,直至找到主机B。
1. ping 1.2.3.4 ------ a non-existing host on the Internet
IP地址为1.2.3.4的主机与执行ping命令的10.0.2.15的主机(称为主机③)不在同一局域网,且1.2.3.4在互联网上并不真实存在,所以主机③的ARP高速缓存中并没有1.2.3.4,所以需要在本局域网内发送ARP请求分组,最终把路由器10.0.2.1的IP地址解析为MAC地址,并向其发送ICMP请求包,此时我们运行的Sniffing_Spoofing程序嗅探到Echo-Request包,并伪造Echo-Reply包发送,所以主机③会收到我们伪造的reply包;但是因为1.2.3.4并不存在于真实互联网中,所以无法解析到1.2.3.4的一个MAC地址,也就不会收到正常回复的包。
2. Ping 10.0.2.64 ------ a non-existing host on the LAN
IP地址为10.0.2.64的主机与主机③属于同一局域网,且10.0.2.64不存在于互联网中,所以主机③的高速缓存中也不会有10.0.2.64,所以会广播发送ARP请求分组,但是局域网中的每一个主机都不会与10.0.2.64匹配,所以并不会有一个IP到MAC的映射,只是重复发送ARP分组,所以会有提示信息:主机不可达,所以主机③不会发送ICMP请求包,所以Sniffing_Spoofing程序并不会嗅探到ICMP请求,也就不会进行一个ICMP响应包的伪造与发送。
3. Ping 8.8.8.8 ----- an existing host on the Internet
IP为8.8.8.8的主机与主机③不在同一局域网,但是存在于互联网中,所以主机③的高速缓存中也不会有8.8.8.8,所以在局域网内广播发送ARP请求分组,但是局域网内的每一个主机都不会接收,此时需要将路由器10.0.2.1的IP解析为MAC,之后路由器10.0.2.1会继续在其他局域网中广播ARP请求分组,直至找到主机8.8.8.8。那么解析到路由器10.0.2.1的MAC后,主机③便会发送ICMP请求包,这时Sniffing_Spoofing程序会嗅探到ICMP请求包,之后进行Echo-Reply的伪造并发送,同时当找到8.8.8.8的主机后,IP为8.8.8.8也会进行一个ICMP请求的响应,所以不仅会收到伪造的响应包,也会收到正常响应的包,但是因为我们伪造的响应包是由与主机③处于同一局域网内的主机发送,而正常响应的包需要经过路由器的转发才会到达主机③,所以会有一个时间差。