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), 请求拦截, 逆向工具