不少人存在这样的观点:只要计算机安装各种专业的安全软件,系统及时更 新补丁,密码尽可能复杂,那么计算机就会避免遭到入侵。当然这样的确不容易 被入侵,但那也只是针对传统的病毒、木马而言,在流量攻击面前,这些防护就 会显得无能为力。无论如何,当你与其他设备进行通信时就会产生流量,当这些 流量脱离了你的计算机后,其安全就不能得到有效的保障,然而这些流量中却包 含着你的敏感数据,攻击者完全可以在不入侵你计算机的情况下获得你的敏感数 据,这个过程叫流量嗅探。
9.1.1 工作原理
互联网中的流量都是以数据包的形式传送的,流量嗅探是对数据包中的流量 进行数据分析的一种手段。通过网络嗅探工具可以捕获到目标计算机网络的数据 包,数据包中的数据是根据所采用协议的要求来组织的,只要能够掌握协议的格 式,就能够分析出这些数据所表示的意义。
图9-1 HTTP数据包
互联网中的大部分数据都没有采用加密的方式进行传输。例如,我们经常接 触的HTTP 、FTP 、Telnet等协议所传输的数据都是明文传输的,如图9-1~图9-3所 示。这也就意味着,一旦攻击者捕获了数据包,并用协议分析软件对数据包进行 分析,那么就可以截获这些数据。
图9-2 FTP数据包
图9-3 TELNET数据包
早期的局域网(LAN)是由集线器(HUB)构建的。因为HUB不具备交换机 的MAC地址表,所以它用广播的方式来发送数据。也就是说,HUB发送的数据, 局域网内的每台计算机都是能接收到的。如果把网络接口设置成“混杂”模式,就 可以实现不管是不是我的数据,我照单全收的情况,从而可以窃取到他人的流量 数据。
交换机的出现逐渐淘汰了HUB 。交换机会绑定MAC地址和接口,数据包最终 只发往一个终端主机,不会出现HUB的广播式方法数据。如果事先配置MAC地址 与对应的接口,理论上非常安全。但是很多人为了偷懒,直接使用了设备默认的 模式“ 自动学习” ,使得交换机成了非常容易被欺骗的对象。攻击者只要伪造一个 源MAC地址数据包,就能将这个地址的流量关联到自己的接口上,以此获得他人 的流量数据。
9.1.2 工具编写
9.1.1节我们介绍了流量嗅探的原理,本节我们使用Scapy模块来编写一个流量 嗅探工具来嗅探本机网卡上的流量。本次工具的编写需要使用到Scapy中的
sniff() 函数,该函数提供了多个参数,下面我们先了解其中几个比较重要的参 数的含义:
·iface:指定在哪个网络接口上抓包。
·count:表示要捕获数据包的数量。默认值为0 ,表示不限制数量。
·filter:流量的过滤规则。使用的是BPF(Berkeley Packet Filter ,柏克莱封包 过滤器)的语法。
·prn:定义回调函数,通常使用lambda表达式来写回调函数。当符合filter的 流量被捕获时,就会执行回调函数。
其中filter是最常用的参数。因为如果直接使用sniff() 函数,会捕获到大量 的流量数据,如果不进行过滤,我们很难从里面找到需要的数据库。filter采用的 是BPF ,利用它来匹配符合我们要求的流量并进行捕获。
BPF的过滤规则(表达式)由一个或多个原语组成。每个原语通常由一个标 识(ID 、名称或数字)和一个或多个限定词组成。
表达式主要有以下三种限定词:
·Type:类型限定词,指明ID或数字所代表的含义,例如host 、net和port等, 若不指定,则默认为host。
·Dir :方向限定词,指明数据包的传输方向,例如src 、dst 、src 、dst等。
·Proto:协议限定词,限定所要匹配的协议,例如tcp 、udp 、ip 、arp等。
表达式还可以使用逻辑运算符对原语进行组合,从而创建出更高级的表达 式,逻辑运算符主要有以下三种:
·&&:连接运算符。
· ||:选择运算符。
· ! :否定运算符。
下面举几个常见用例,帮助读者理解BPF语法:
· 只捕获与网络中某一IP的主机进行交互的流量:host 192.168.10.1。
· 只捕获与网络中某一MAC地址的主机的交互流量:ether src host 00:88: ca:86:f8:od。
· 只捕获来源于网络中某一IP的主机流量:src host 192.168.10.1。 · 只捕获去往网站中某一IP的主机的流量:dst host 192.168.10.1。
· 只捕获80端口的流量:port 80。
· 只捕获除80端口以外的其他端口的流量:!port 80。
· 只捕获ICMP流量:ICMP。
·只捕获源地址为192.168.10.1且目的端口为80的流量:src host 192.168.10.1&&dst port 80。
下面使用sniff() 来进行数据包的捕获。例如,我们捕获目的地址为 112.80.248.76的流量,如下所示:
这时Scapy就已经在开始捕获符合filter表达式的数据包,但是这个时候捕获到 数据是不会实时显示出来的,只有取消捕获时才会出现结果,如下所示:
如果想要实时显示捕获到的数据包,就要加上prn选项,这里prn的内容我们 用lambda表达式来编写,具体内容为prn=lambda x:x.summary() ,如下所示:
也可以进一步细化打印的内容。我们更改一下lambda表达式,让sniff() 打 印出源IP和目的IP ,如下所示:
如果需要更翔实的输出,则会需要更多的代码,那么sniff() 语句整体就会 很冗长。我们可以定义一个回调函数,然后让prn调用即可。定义一个
CallBack() 函数,代码如下:
U
def CallBack(packet) :
# 打印源地址和目标地址
print("Source:%s--->Target :%s"%(packet[IP] .src,packet[IP] .dst))
# 打印TTL值
print("TTL:%s"%packet[IP] .ttl)
# 使用内置函数show()打印数据包的内容
print(packet.show())
效果如下所示:
除了显示这些数据包,我们还可以将这些数据包保存,用专业的工具查看、 分析这些数据包。保存数据包的格式有很多种, 目前最为通用的格式为pcap 。可 以借助wrpcap() 函数进行数据包的保存:
packet=sniff(filter="dst packet) | 112.80.248.76", | count=4)wrpcap("ms08067 .pcap", |
同样,我们先通过sniff()进行捕获数据包,同时我们在增加一个count选 项,表明我们需要捕获数据包的数量,当捕获到规定数量的数据包时,sniff就停 止捕获。如下所示:
然后可以调用Wireshark来查看这些数据包
接下来,我们编写一个网络嗅探工具,根据用户传入的IP地址、数据包总数 来捕获相应的数据包,并保存为pcap格式。具体步骤如下:
1)导入相关模块并编写回调的打印函数,函数会打印输出源IP 、源端口、 目 标IP 、 目的端口以及整个数据包的信息。
2)编写一个时间戳转换函数,根据数据包内的时间戳进行转换输出,标明 该数据包的时间:
#!/usr/bin/python3 # -*- coding: utf-8 -*- from scapy .all import * import time import optparse
# 回调打印函数 def PackCallBack(packet) : print("*"*30) # 打印源IP、源端口、 目的IP、 目的端口 print("[%s]Source:%s:%s--->Target :%s:%s"%(TimeStamp2Time(packet.time), packet[IP] .src,packet.sport,packet[IP] .dst,packet.dport)) # print("[%s]Source:%s:%s--->Target :%s:%s"%(packet.time, packet[IP] . src, 4444, packet[IP] .dst, 5555)) # 打印输出数据包 print(packet.show()) print("*"*30) # 时间戳转换函数 def TimeStamp2Time(timeStamp) : timeTmp = time .localtime(timeStamp) myTime = time .strftime("%Y-%m-%d %H:%M:%S", timeTmp) return myTime |
3)编写main 函数,进行参数的定义以及流量数据的保存: |
if __name__ == '__main__ ' : parser = optparse .OptionParser("Example:python %prog -i 127 .0 .0 .1 -c 5 -o ms08067 .pcap\n") #添加IP参数 -i parser .add_option( '-i ', '--IP ', dest= 'host IP ', default="127.0.0.1", type= 'string ', help= 'IP address [default = 127 .0 .0 .1] ') #添加数据包总数参数-c parser .add_option( '-c ', '--count ', dest= 'packetCount ', default=5, type= 'in t ', help= 'Packet count [default = 5] ') #添加保存文件名参数-o parser .add_option( '-o ', '--output ', dest= 'fileName ', default="ms08067 .pcap", type= 'string ', help= 'save filename [default = ms08067 .pcap] ') (options, args) = parser .parse_args() def Filter = "dst " + options .host IP packets = sniff(filter=def Filter, prn=PackCallBack, count=options . packetCount) # 保存输出文件 wrpcap(options .fileName, packets) |
监听网络接口需要root权限,普通用户需要在命令前加上sudo ,否则会出现 错误,如下所示:
开启两个终端,一个终端进行监听,另一个终端使用curl命令,如下所示:
抓包效果如下所示:
此时, 目录下会多出一个ms08067.pcap文件,我们用Wireshark打开查看,如 图9-5所示。
图9-5 用Wireshark查看数据包