y198nt/Nginx-chain-Rift-Poolslip
GitHub: y198nt/Nginx-chain-Rift-Poolslip
将 CVE-2026-9256 与 CVE-2026-42945 两个 nginx rewrite 引擎漏洞组合为一条针对官方原生镜像的 ASLR 无关 RCE 利用链,并提供自包含实验环境的 PoC 仓库。
Stars: 8 | Forks: 4
# nginx PoolSlip × Rift — 独立于 ASLR 的 RCE 利用链
将最近披露的两个 nginx rewrite-engine 漏洞首次公开组合成一条针对**官方原生 `nginx:1.30.0`** Docker 镜像的
**独立于 ASLR 的远程 `system()`** 利用链
(Debian 13, glibc 2.41) — **无需硬编码地址,无需重启 nginx,每个新建 worker 成功率约 90%**。
* **CVE-2026-9256 "PoolSlip"** — 一个 `$args` 堆越界读取 → 泄露有效的 **libc + 堆地址**。
* **CVE-2026-42945 "rift"** — 一个 `is_args` 堆溢出(仅限 URL 安全字节) → 对 `limit_conn` 的清理指针进行 **2 字节部分
覆写** → `ngx_destroy_pool` 遍历该指针 → 触发 `system(cmd)`。
两者都是*同一个*指向两个不同接收点的 `is_args` 两阶段解析不匹配漏洞。完整技术分析:[分析报告](https://hackmd.io/@y198/S1xoJVxWzl)
| CVE | 别名 | 受影响版本 | 已修复版本 (nginx OSS) |
|---|---|---|---|
| CVE-2026-42945 | rift | 0.6.27 – 1.30.0 | 1.30.1 / 1.31.0 |
| CVE-2026-9256 | PoolSlip | 0.1.17 – 1.30.1 + 1.31.0 | **1.30.2** / 1.31.1 |
## 仓库内容
| 文件 | 用途 |
|---|---|
| `nginx.conf` | 一个合理的 API 网关配置(即*目标* — 包含 `limit_conn`、一个 v1→v2 迁移 rewrite、一个上传代理、一个降级的搜索页面)。**未对内存分配器进行任何调优。** |
| `run.sh` | 搭建本地实验环境:原生 `nginx:1.30.0` 运行 `nginx.conf`,外加一个缓慢的上游服务,监听主机端口 `:19322`。 |
| `exp_official.py` | 完整的利用链:PoolSlip 内存泄露 → 推导 libc/堆地址 → 堆喷射 → 内存布局整理 → rift 部分覆写 → `system()`。 |
## 环境要求
* Docker, Python 3(用于运行 `exp_official.py`),`curl`, Linux x86-64。
* 该实验环境是**完全自包含 / 离线的** — `run.sh` 使用 `perl`(已包含在原生 nginx 镜像中)作为缓慢的上游服务,因此不需要额外的软件包,没有 `slow_backend` 文件,也不需要互联网。nginx 二进制程序本身保持原生状态。
* *可选:* 若要跟进 `WRITEUP.md` 中的 gdb 调试过程,可通过 `docker exec` 进入容器并执行 `apt-get install gdb file binutils`,然后加载 gef。
## 运行方式
```
git clone https://github.com/y198nt/Nginx-chain-Rift-Poolslip
cd Nginx-chain-Rift-Poolslip
bash run.sh # stock nginx:1.30.0 on http://127.0.0.1:19322
python3 exp_official.py --cmd 'id > /tmp/rce_proof' # leak → overwrite → system()
docker exec nginx-rift-official cat /tmp/rce_proof # → uid=101(nginx) ...
```
`exp_official.py` 是**单次执行**的(每个新建 worker 成功率约 90%):它泄露一次并触发一次。如果
证明文件为空,说明内存布局整理偏离了目标槽位 — 重新运行即可。
```
docker rm -f nginx-rift-official # tear the lab down
```
## 工作原理(简述)
1. **内存泄露。** 一条 `rewrite ^/search/((.*))$ /lookup?$1$2` 规则会在拷贝时将
`r->args.len` 设置得*超出*缓冲区;降级的 `/search` 页面会将 `$args` 反射到相邻的堆内存中。
经过短暂的预热后,一个 **libpcre 集群指针**(固定位于 libc 基址下方 `0xb0ad08` 处)和一个堆指针会停留在稳定的偏移位置 → 从而得出 `libc_base` 和 `heap_base`,无需任何硬编码。
2. **写入。** rift 溢出只能输出 **URL 安全字节**,因此在开启 ASLR 的情况下,完整 48 位地址的写入成功率仅为 ~0.9%。相反,我们只覆写一个*已有效*的 `limit_conn` 清理指针的**低 2 字节** — 经过 ASLR 随机化的高位字节保持原状 →
**独立于 ASLR**。
3. **触发。** 该指针被重定向到一个喷射的 `ngx_pool_cleanup_t{handler=&system,
data=cmd, next=0}` 结构中;关闭受害者连接会触发 `ngx_destroy_pool` 的清理遍历 →
`system(cmd)`。
请参阅[分析报告](https://hackmd.io/@y198/S1xoJVxWzl) 获取完整的推导过程、gdb 调试记录以及死胡同与思路转换。
## 缓解措施与检测
* **打补丁**(升级至 1.30.2 / 1.31.1)— 唯一真正的修复方法。
* 该写入操作只能通过 replacement 包含 `?` **并且**引用了 PCRE 捕获组(`$1`/`$2`)的 `rewrite` 规则触发,或者是类似 `^/x/((.*))$ → /y?$1$2` 的嵌套捕获模式。在打补丁之前,审查配置中的此类模式并修改有问题的 `rewrite`/`set` 规则。
* **反射的 `$args`**(或任何被原样返回的请求衍生变量)正是将越界读取转化为信息泄露的原因 — 不要原样反射请求参数。
* WAF / 日志特征:URI 路径中出现大量 `%2B`/`+` 泛滥(`GET /search/+++…`),来自反射长 `$args` 的降级页面产生的 `503` 错误,以及 worker 进程的 `SIGSEGV`/频繁重启风暴。
## 致谢与参考
* 原始组件 PoC:[DepthFirstDisclosures/Nginx-Rift](https://github.com/DepthFirstDisclosures/Nginx-Rift) (rift)。
* 厂商公告 — CVE-2026-42945 (rift) 和 CVE-2026-9256 (PoolSlip), F5/nginx。
* 利用链组合、独立于 ASLR 的技术,以及向 Debian glibc-2.41 的移植:来自本仓库。
标签:CISA项目, Nginx, Web报告查看器, 内存破坏, 应用安全, 漏洞验证 (PoC), 请求拦截, 逆向工具