ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

数据包嗅探与欺骗:从原理到Scapy实战

数据包嗅探与欺骗:从原理到Scapy实战 1. 实验环境准备与拓扑设计先交代一下背景。SeedLab 是网络安全领域非常经典的一套教学实验几乎每个做安全方向的学生都接触过。Packet Sniffing and Spoofing 这个实验用一句话概括就是让你在真实的网络环境中亲手实现“偷听别人通信”和“伪造别人身份”这两个网络攻击中最基础的动作。实验的目标不是教你写攻击工具而是通过从零实现嗅探器和欺骗工具彻底搞懂网络协议栈中那些平时被隐藏起来的细节。我做这个实验用的环境是 SEED 官方提供的 Ubuntu 16.04 虚拟机镜像运行在 VirtualBox 里。选这个版本而不是在物理机上直接操作原因很现实实验过程中需要频繁地抓包、改包、发伪造包搞不好就会把宿主机的网络弄断虚拟化的隔离环境方便随时快照回滚。SEED 镜像默认已经装好了 Wireshark、Python 3.5、Scapy 等常用工具省去了大量编译安装的折腾时间这一点对新手非常友好。实验拓扑看起来很简单但里面藏着关键设计。两台虚拟机虚拟机A、虚拟机B都连接到同一个 NAT 网络宿主机也连在这个网络上。三台设备处在同一个广播域内这意味着任意一台机器发出的数据包其他机器都能在网卡层收到——这正是嗅探和欺骗实验的前提条件。注意SEED 文档里一直强调所有实验操作都要在这个隔离的虚拟网络里完成不要拿校园网、公司网络做实验。这不是保守而是法律风险问题——在未经授权的网络上抓包和发伪造包性质上就是网络攻击行为。实验归实验边界必须清楚。实际动手前我建议你先做两件事。第一给虚拟机拍一个干净的快照后面无论把网络配置折腾成什么样都能一键还原。第二熟悉一下 Wireshark 的基本操作至少要知道怎么开始抓包、怎么设置过滤条件、怎么查看包的十六进制内容。后面每一道 Task 都会用到这些基本功。2. 数据包嗅探的原理与实现思路2.1 从网卡的工作模式说起很多人第一次接触嗅探时脑子里都有个疑问为什么我能抓到别人的流量这要从网卡的工作模式讲起。正常情况下网卡收到一个数据帧后会检查帧头部的目的 MAC 地址。如果这个地址不是自己网卡的 MAC 地址也不是广播地址或多播地址网卡就直接丢弃这个帧不会把它交给操作系统处理。这个机制本身是为了节省 CPU 资源避免无关数据干扰系统。但当网卡被设置为**混杂模式Promiscuous Mode**后行为就变了所有经过网卡的帧都会被接收不管目的 MAC 地址是不是自己。一旦网卡处于这种状态配合适当的抓包工具比如 Wireshark同一个广播域内所有设备之间的通信内容就都能被看到。嗅探的本质就这么简单让数据包在更底层的位置被截获绕过操作系统网络协议栈的过滤机制。当然真正实施嗅探时单纯靠网卡混杂模式还不够因为现代系统里网络应用都是基于 Socket 接口来收发数据的普通 Socket 拿不到别人的数据包。这时候就需要引入一种特殊的机制——数据包捕获接口。2.2 Scapy 与 pcap两个层次的抓包方案在 Linux 系统里最底层的数据包捕获机制是 BPFBerkeley Packet Filter。你可以把 BPF 理解为在网卡驱动和协议栈之间加了一道“分流器”数据包到达网卡后BPF 会复制一份给注册了抓包功能的进程另一份继续走正常协议栈。开发者通过 libpcap 库来调用 BPF 的能力Wireshark 底层用的就是 libpcap。在 SeedLab 实验里实现嗅探器有两种典型方案。第一种是写 C 语言程序直接调用 libpcap 库接口第二种是用 Python 配合 Scapy 库在更高层的抽象上操作。我这次实验大部分任务都用的 Scapy因为 Scapy 的处理逻辑更直观一段几行的 Python 代码就能抓到包并展示结构省去了大量 C 语言的繁琐细节。但这不代表 C 方案没有价值。两种方案的对比我用一张表来说明对比维度C libpcapPython Scapy开发效率低需要手动管理缓冲区、处理字节序高几行代码就能完成复杂逻辑底层可控性高可以精细控制每个环节中Scapy 封装了底层细节运行性能高适合高流量环境中数据量大时解析速度偏慢学习价值高能深入理解协议栈与 BPF 机制中偏重功能实现适用范围生产环境的工具开发实验与原型验证我个人的建议是实验阶段用 Scapy 快速完成任务课余时间再把 libpcap 的官方文档翻一下至少跑通一个简单的 C 语言抓包程序。这样做的好处是你对“操作系统如何把数据包交给用户态进程”这条链路会有更扎实的理解。2.3 抓包过滤器的设计BPF 表达式详解无论是 Wireshark 还是 Scapy都支持 BPF 过滤表达式。这个过滤功能非常实用不然网卡一开混杂模式各种广播包、ARP 请求铺天盖地根本没法干活。实验 Task 1 要求抓取特定类型的包比如 ICMP 包、TCP 包以及特定子网的通信。这里 BPF 表达式的写法就非常关键。最基础的过滤器有icmp只抓 ICMP 协议包tcp只抓 TCP 协议包host 192.168.159.101只抓与这个主机通信的包net 192.168.159.0/24只抓这个子网内的包port 80只抓端口为 80 的包src host 192.168.159.101/dst host 192.168.159.101区分源地址和目的地址这些表达式还可以用逻辑运算符连接and、or、not。比如你想抓取“来自 192.168.159.101 的 ICMP 包”可以写成icmp and src host 192.168.159.101。写过滤表达式时有个关键细节BPF 表达式的执行发生在内核态不是你写什么条件它就遍历一遍全部数据包而是先把规则编译成一段伪机器码由 BPF 虚拟机在内核层面执行。这意味着写好过滤条件不只是省事还能显著降低抓包时的 CPU 开销。Scapy 中使用过滤器的写法是在sniff()函数里通过filter参数指定from scapy.all import * # 抓取 5 个 ICMP 包 packets sniff(filtericmp, count5) for pkt in packets: pkt.show()这段代码会调用底层 libpcap 的正则编译与过滤机制把不符合条件的包直接丢弃只有匹配的包才会被复制到用户空间。3. 数据包欺骗的原理与核心实现3.1 为什么可以伪造数据包嗅探是被动行为欺骗则是主动伪造。做欺骗的基础在于网络协议在数据包的发送方身份校验上几乎没有做任何验证。以 TCP/IP 协议族为例当一个进程调用系统接口发送数据包时协议栈会自动填好源 IP、源 MAC、源端口等信息。但是如果你不通过这些常规接口而是直接操作原始套接字Raw Socket或者使用 Scapy 这类底层数据包构造工具就可以自己指定数据包的全部字段——包括正常情况下不该由用户决定的源地址。这里本质上是因为 IP 协议设计时默认所有网络节点都是“值得信任”的。IP 协议只负责尽力而为地把数据包送到目的地它不保证源地址的真实性。TCP 握手可以设计各种机制来验证连接但那是在传输层IP 层之下依然存在巨大的伪造空间。用生活里的例子来理解就很简单寄快递时快递单上的寄件人信息是你自己手写的快递公司并不会核实你填的是不是真的。你完全可以填一个假的寄件人地址快递公司照样会把包裹送出去这个例子只是帮助你理解原理现实中填假寄件人信息是违法行为。3.2 用 Scapy 构造伪造包实验 Task 2 的核心目标是用 Scapy 构造一个数据包让它的目标地址指向受害者但源地址伪装成任意指定的 IP。实现代码非常简单from scapy.all import * # 构造一个 IP 层源地址为 1.2.3.4 的 ICMP Echo Request ip IP(src1.2.3.4, dst192.168.159.101) icmp ICMP() pkt ip / icmp send(pkt)这里面有值得深入说说的细节。第一行IP(src1.2.3.4, dst192.168.159.101)指定了 IP 层的源地址和目的地址但你有没有想过这个伪造的源地址和真实网卡上的地址不一致系统会怎么处理答案是要看走什么通道。使用 Scapy 的send()函数发送数据包时Scapy 默认会创建一个原始套接字这个套接字不经过操作系统正常的 TCP/IP 协议栈的填充逻辑所以你可以直接控制整个 IP 头。如果包比较大需要分片Scapy 也会自动处理分片并在每个分片中保留你伪造的源地址。使用send()发送的是三层包IP 层这就涉及一个关键问题目的 MAC 地址是从哪里来的这里 Scapy 会自己做 ARP 解析——向目标 IP 发送 ARP 请求得到目标 IP 对应的 MAC 地址后把数据帧的链路层目的地址填上。3.3 实验中的“看现象”环节Task 2 的实验设计非常巧妙它不要求你直接去看伪造包的结构而是通过“发送伪造源地址的包然后在目标机器上用 Wireshark 观察现象”来验证欺骗是否生效。具体操作是在虚拟机 A 上运行上面的 Python 脚本发送一个源地址为1.2.3.4的 ICMP Echo Request 包目标地址设为虚拟机 B 的 IP比如192.168.159.102。然后在虚拟机 B 上开启 Wireshark 抓包观察是否收到一个源地址为1.2.3.4的 ICMP 请求。这个环节最直观的收获是你会亲眼看到目标机器收到的数据包源地址确实是你伪造出来的那个 IP。这个地址在真实网络中根本不存在但包照样能到达受害者的网卡。但要注意这里有个更深层的现象值得思考虚拟机 B 收到这个 ICMP 请求后协议栈会正常回复 Echo Reply。但回复的目的地址是源地址1.2.3.4而1.2.3.4并不存在于当前网络中这条回包大概率会石沉大海或经过路由器转发后无法投递。所以你在虚拟机 B 上回复时抓包只会看到请求包不会看到成功的“一问一答”。3.4 欺骗的底层协议视角实验做到第 3 个任务Task 3的时候要求把嗅探和欺骗结合起来实现 “Sniff then Spoof”——先自动嗅探环境中的 ICMP 请求包然后立刻构造一个伪造的 ICMP Echo Reply 返回给请求者。这个任务光写代码不难难点在于你要理解链路层到网络层的完整流程。先从链路层的视角说。当前网络的拓扑是三台设备在一个网段内。当虚拟机 B 向虚拟机 A 发送 ICMP Echo Request 时B 的协议栈会先查 ARP 缓存。如果 ARP 缓存里没有 A 的 MAC 地址B 会先发一个 ARP 广播请求“谁是 192.168.159.101”A 收到后回应自己的 MAC 地址B 才能把 ICMP 包封装成链路层帧发出去。此时虚拟机 C或者宿主机上运行的工具开启了混杂模式它抓到了这整个过程中的所有包——包括 ICMP 请求以及可能的 ARP 请求与回应。当 C 抓到了 ICMP 请求时它立刻构建一个源地址为 A 的 IP 地址、内容为 Echo Reply 的数据包并发送回 B。这个回包不需要真实的 A 参与因为发送数据包时填的是 A 的 IPMAC 层填的是 B 的 MAC 地址网络层不会做任何源地址合法性验证。Sniff then Spoof 代码的核心框架from scapy.all import * def spoof_reply(pkt): # 只处理 ICMP Echo Request if pkt.haslayer(ICMP) and pkt[ICMP].type 8: # 伪造一个来自目标 IP 的 Echo Reply ip IP(srcpkt[IP].dst, dstpkt[IP].src) icmp ICMP(type0, idpkt[ICMP].id, seqpkt[ICMP].seq) # 最好带上原始数据部分有些 ping 程序会校验载荷 data pkt[Raw].load if pkt.haslayer(Raw) else b spoofed ip / icmp / data send(spoofed, verboseFalse) print(fSent spoofed reply to {pkt[IP].src} pretending to be {pkt[IP].dst}) sniff(filtericmp, prnspoof_reply)代码里有个容易忽略的细节ICMP(type0, idpkt[ICMP].id, seqpkt[ICMP].seq)。这里的id和seq必须和接收到的 Echo Request 保持一致。原因是ping程序在收到 Echo Reply 时会检查id和seq是否与它发出的请求一致如果不一致就会丢弃这个回包。这个细节决定了你的欺骗是否能“骗过”应用层程序的校验逻辑而不只是停留在网络层。3.5 为什么 ICMP 欺骗能成功这个任务做完后我不止一次被问到既然请求已经到达了真实的 A那 A 自己也会回一个 Echo ReplyB 会收到两个回复不冲突吗实际上在 Sniff then Spoof 的典型场景下真实的目标主机 A 可能根本收不到这个 ICMP 请求。为什么因为请求是虚拟机 C 伪造发送的目的 MAC 是 B 的 MAC源 IP 是 A 的 IP。但这个包并不是 A 发出的它只是“冒充”了 A 的 IP 地址。当包到达 B 时B 会把它看成来自 A 的正常请求B 的回复会发给 A 的真实 IP。这里的关键在于如果 B 和 A 在同一个局域网内A 收到 B 发来的 Echo Reply 时发现自己的协议栈根本没有发过对应的 Echo Request于是直接丢弃。而 C 伪造的 Echo Reply 如果能在 B 的真实回包之前先到达 BB 的ping程序就会愉快地接受这个伪造的回复——因为在 B 的视角里它只知道自己发了一个请求收到了一个回复而不会去验证这个回复是不是真的来自 A。所以Sniff then Spoof 成功的关键是速度你必须在真实主机 A 有机会响应之前把伪造的回包发送出去。在实际实验中由于 C 是在本地网络内直接监听到请求包并马上作出响应延迟极低通常都能抢先一步到达 B。4. 实操过程中的关键环节与抓包验证4.1 环境配置检查清单开始实验前我强烈建议你按下面的清单逐项检查配置任何一项缺失都会导致后面排查问题时无从下手虚拟机 A、B 是否处于同一 NAT 网络IP 地址是否已固定SEED 镜像默认分配好了固定 IP虚拟机 A、B 之间能否正常ping通这是在验证基础连通性Wireshark 是否可以正常启动并抓包确认当前用户有权限打开抓包接口Scapy 是否可以正常导入并执行send()发包确认没有权限和依赖问题是否已经拍摄虚拟机快照防止实验过程中误操作导致系统状态异常关于抓包权限这里有个常见的坑Wireshark 和 Scapy 在访问网络接口时需要 root 权限。如果你在普通用户下执行sniff()函数可能会报权限错误。SEED 镜像中的默认用户通常可以直接操作但如果使用自己安装的环境建议通过sudo运行或者在系统层面给对应用户授予抓包权限。4.2 用 Wireshark 验证欺骗现象的完整流程以 Task 2 为例完整的验证流程应该是先在虚拟机 B 上启动 Wireshark 并选择正确的网络接口通常是ens33或eth0然后点击开始抓包。然后在虚拟机 A 上运行伪造发包脚本python3 spoof.py脚本发完包后会提示已发送。回到虚拟机 B 的 Wireshark停止抓包然后在过滤栏输入icmp来筛选 ICMP 包。你应该能看到一条源地址为伪造 IP比如1.2.3.4的 ICMP Echo Request 记录。点击这条记录在 Wireshark 下方的十六进制区域你可以展开 IP 层查看 Source Address 字段确认它就是伪造的地址。你还会看到这条记录的源 MAC 地址却是虚拟机 A 的真实 MAC 地址——这说明网络层地址可以随便改但链路层地址还是受限于实际发送网卡。这里引出一个非常关键的安全认知如果攻击者在另一个网段那伪造包要经过路由器路由器可能会做源地址过滤ingress filtering但同一网段内的伪造几乎畅通无阻。4.3 Scapy 构造包的字段级细节Scapy 在构造包时有一些字段不需要你手动指定它会自动填充合理的默认值。但理解这些默认值背后发生了什么对做好实验至关重要。以IP()为例如果你不指定id字段Scapy 会随机生成一个 IP 标识符如果你不指定ttl默认值是 64。这些自动填充在大多数场景下没问题但在某些需要精确复现真实数据包行为的测试中你可能会发现默认值和真实情况不一样。比如真实 Windows 系统发出的 TCP 包的 TTL 通常是 128而 Scapy 默认是 64——如果你在做操作系统指纹识别相关的实验这个差异就会暴露你用的是 Scapy。另一个细节是send()和sendp()的区别send()发送第 3 层包IP 包Scapy 自动处理链路层封装会做 ARP 解析sendp()发送第 2 层包以太网帧你需要自己指定源 MAC、目的 MAC 和以太网协议类型在 Sniff then Spoof 场景中要用send()发送 IP 包让 Scapy 自动处理链路层封装。如果需要精确控制 MAC 层或者绕过 ARP 缓存直接指定目的 MAC那就要用sendp()并手动构造Ether()层。4.4 实验记录与结果分析的方法做这类实验养成记录结果的习惯非常重要。我不建议光把代码跑通就算完事而是要把每一步的现象截图、记录下来同时想清楚背后的协议行为。比如虚拟机 B 接收到的伪造包源 MAC 是什么ICMP 的 Identifier 和 Sequence Number 是否匹配Task 3 涉及TCP 包在构造时源端口的合法性之类是否需要考虑Task 1 中的 TCP 抓包验证如果是在真实的场景下做验证还可以结合 Wireshark 的Statistics - Protocol Hierarchy功能查看抓取到的数据包中各种协议的比例这能帮助你快速确认流量模式是否符合预期。比如你只发了一个 ICMP 包却发现 Wireshark 里统计出 ARP 包的占比远高于 ICMP说明你的程序在发送前额外触发了 ARP 解析流程——理解这个流程对排查类似问题很有帮助。5. 常见问题与排错技巧实录5.1 抓不到包权限、接口和过滤器的三大坑我在实验过程中遇到过好几次“明明发了包但 Wireshark 里就是看不到”的情况。事后排查发现绝大部分原因落在三个地方第一个是权限。Wireshark 没能在混杂模式下打开接口自然抓不到非本机通信的包。解决办法是确认当前用户的权限或者直接使用sudo wireshark启动。第二个是选择的接口不对。虚拟机的网络接口可能有好几个lo、ens33、ens38 等如果你在 ens33 上抓包但程序是从 ens38 发出去的那当然什么都看不到。用ip addr或ifconfig确认当前活跃接口再在抓包工具里选择对应接口。第三个是 BPF 过滤器写得过严。比如你只过滤icmp但实际发出的包是 UDP 或 ARP抓包结果就是空白的。排错时可以先把过滤条件放宽甚至取消过滤确认流量存在后再逐步收紧过滤条件。5.2 发出去的包没有响应在 Task 3 里我们实现了嗅探并自动回复 ICMP Echo Reply于是用ping命令时即使原本的目标主机已经关闭网卡ping结果依然显示通了。这就是攻击效果。但有同学实现后发现ping的输出依然是一堆“Destination Host Unreachable”或超时。这个问题的关键原因我在上文提到过——ICMP Echo Reply的id和seq必须与请求一致。在 Scapy 中如果用了默认参数构造出来的id是随机值seq默认从 0 开始而真实的ping程序发出去的请求seq可能是一个递增序列。一旦回复的id和seq对不上ping程序不会认为这是自己的回包直接丢弃。另外还有一个隐性原因如果你在抓到了 ICMP 请求后只是匆匆构造了一个 ICMP 回复包但没有附带请求包中的载荷数据Payload某些版本的ping命令会校验回包中的数据内容不一致时会丢弃。所以在代码里要用pkt[Raw].load把原请求中的数据部分一起拷贝到回包中。5.3 网络环境差异导致的“玄学”问题实验文档里的 IP 地址是示例值你运行在自己环境中时一定要用实际的 IP 替换。有同学直接把文档里的1.2.3.4和192.168.159.102照抄使用结果发现目标不响应或者发出去的包压根没到目标机器这多半是 IP 地址不匹配导致的。还有一种情况是防火墙干扰。虽然实验室环境通常没有防火墙但如果你自己装的系统里开启了ufw或者iptables规则可能会影响 ICMP 包的接收和发送。排查时先用ping把基础连通性确认后再继续其他操作。如果实验中途发现网络接口的 IP 变了可能是因为 DHCP 重新分配了地址。SEED 镜像默认是静态 IP但在某些虚拟机网络配置下系统启动时可能会触发 DHCP 请求导致地址变化。这时候最好把网络配置改成固定 IP或者每次实验开始前先检查一遍三台机器的 IP 地址。5.4 快速排错流程参考根据我的实践经验当实验结果不符合预期时按这个顺序排查效率最高确认三台机器的 IP 地址和网络连通性ping通是最基础的前提在目标机器上抓包确认是否真的收到了“预期类型”的数据包在发送端上抓包确认数据是否真的从网卡发出去了检查 BPF 过滤器条件和 Scapy 代码逻辑逐步去除过滤项或增加日志输出检查数据包内容的字段值是否合理重点对比 IP 头、ICMP 头的关键字段每次修改代码或配置后都重新抓包验证一次。这个方法看起来笨但实际操作中它能帮你快速锁定问题所在而不是在无所谓的地方反复消耗时间。6. 实验中的个人体会与后续扩展方向6.1 做完这个实验我理解了哪些东西从做实验到真正理解中间隔着一个“自己动手踩坑”的距离。实验前我也知道“IP 地址可以伪造”这个结论但只有当我在虚拟机 A 上发送一个源地址为1.2.3.4的包然后在虚拟机 B 上亲眼看到这条包出现时才对 IP 层无认证这件事有了真正的体感。同样地混杂模式在课堂讲义上只是“网卡的一种工作模式”这样一句干巴巴的描述。但是在实验里当我发现虚拟机 C 可以抓到 A 和 B 之间的全部通信内容时我才意识到广播域内的网络通信本就不是私密的。任何一台设备如果不小心被植入恶意程序开启混杂模式整个局域网内的明文通信都将暴露无遗。有几个细节我特别提醒自己注意Wireshark 里看到的包Source IP 是假的但 Source MAC 是真实的TCP 握手时序列号的生成是有规律可循的ICMP Echo Reply 要成功骗过 ping 程序id 和 seq 必须和请求保持一致。这些细节单拎出来都很小但组合在一起构成了网络欺骗攻击的基本功。6.2 这个实验在真实安全场景中的对应关系实验内容虽然基于教学场景但和真实安全威胁的对应关系非常直接。ARP 欺骗、IP 欺骗、DNS 欺骗、中间人攻击、会话劫持——这些攻击技术的基础都离不开数据包嗅探和数据包欺骗这两项能力。做这个实验时可以尝试把学到的技能迁移到模拟攻击场景中理解攻击链路的完整逻辑。比如将嗅探与 ARP 欺骗结合可以实现同一交换机下的流量劫持将 TCP 序列号预测与伪造包结合可以实现 TCP 会话注入。不过我必须再次强调这些技术只应在实验环境、CTF 比赛或授权的渗透测试中使用任何未经授权的网络攻击行为都涉及法律风险。6.3 可以继续尝试的几个扩展方向如果你做完实验还觉得不过瘾我推荐你看看下面这几个方向它们都是在 SeedLab 嗅探和欺骗实验基础上的自然延伸第一实验手册里还有“SYN Flooding”、“TCP RST Attacks”等任务可以直接顺着做下去。这些攻击方式本质上都依赖“构造并发送伪造数据包”的核心能力但具体到字段细节比如 TCP Flags 的设定、序列号的生成策略又有各自的门道。第二自己动手实现一个更完整的 ARP 欺骗与中间人攻击模拟。这需要你在抓包分析、数据包构造之外还要掌握 ARP 缓存表的管理机制和并发处理逻辑对网络协议的综合理解要求更高。第三用 C 语言实现一套基于 libpcap 的迷你嗅探器。这个方向不追求功能齐全而是追求代码的底层可控性。当你熟悉 libpcap 的回调机制、BPF 过滤器的编译过程后再回头看 Scapy 的代码你会理解 Scapy 为开发者做了多少底层工作。第四深入理解 BPF 本身。从经典的 cBPF 到内核中的 eBPF这个领域在可观测性和安全攻防中都扮演着越来越重要的角色。理解了实验中的 BPF 过滤器你再去学习 eBPF 的动态插桩、流量控制、安全检测等能力会发现很多概念是相通的。根据我个人的经验这个实验做完后不要急着往下走花一天时间把实验报告里涉及的每一个字段都从头到尾查一遍收获会比“做完任务就交差”大得多。网络安全是个需要抠细节的领域很多攻击手法之所以能生效正是因为攻击者对协议字段的理解比防御者更深一层。
返回列表