Hamid-K/nginx-rift-private-lab
GitHub: Hamid-K/nginx-rift-private-lab
这是一个针对Nginx漏洞CVE-2026-42945的私有实验室,提供绕过ASLR的利用链和自动化评估工具。
Stars: 75 | Forks: 15
# ool/library name, so keep in English.
针对 **CVE-2026-42945** 的远程代码执行(RCE)概念验证,这是 NGINX 的 `ngx_http_rewrite_module` 自 2008 年引入的一个严重堆缓冲区溢出漏洞。该漏洞使得攻击者可以对使用 `rewrite` 和 `set` 指令的服务器进行未经身份验证的远程代码执行。
此分支扩展了原始 PoC,增加了绕过地址空间布局随机化(ASLR)的利用链,该链将 NGINX 溢出与一个常见的同一主机本地文件包含(LFI)/任意文件读取原语相结合。该文件读取原语用于恢复 nginx worker 映射、libc 和活动的 `/proc//mem`,然后远程推导出 `system()` 地址和可用的堆目标。
此实验室的早期版本会故意使一个 nginx worker 崩溃,从而让服务生成一个核心转储文件,然后通过文件读取原语获取并解析该核心转储文件,以恢复包括堆目标在内的、受 ASLR 保护的进程状态。在本仓库中,**coreless** 仅作为"不使用可读崩溃核心转储"的简写:当前的默认路径用活动的 procfs 内存读取取代了对崩溃核心转储的依赖,而保留的遗留 **core-guided** 路径仍使用生成的 worker 核心转储。
此漏洞——连同另外三个内存损坏问题(CVE-2026-42946、CVE-2026-40701、CVE-2026-42934)——是由 [depthfirst](https://depthfirst.com) 的安全分析系统在仅点击一次导入 NGINX 源代码后自主发现的。
## 漏洞简述(TL;DR)
NGINX 的脚本引擎使用两遍处理过程:首先计算所需缓冲区大小,然后复制数据。当 `rewrite` 替换字符串包含 `?` 时,主引擎上的 `is_args` 标志会被设置,但长度计算过程运行在一个新初始化的子引擎上。因此:
- **长度计算阶段**看到 `is_args = 0` → 返回原始捕获长度。
- **数据复制阶段**看到 `is_args = 1` → 使用 `NGX_ESCAPE_ARGS` 调用 `ngx_escape_uri`,将每个可转义字节扩展为 3 个字节。
复制操作导致堆缓冲区溢出,其中包含攻击者控制的 URI 数据。利用过程使用跨请求的堆布局技术(heap feng shui)来损坏相邻 `ngx_pool_t` 的 `cleanup` 指针(通过 POST 主体进行喷射,因为 URI 字节不能包含空字节),将其重定向到一个伪造的 `ngx_pool_cleanup_s` 结构体,该结构体在内存池销毁时调用 `system()`。
在此处阅读更多关于此漏洞的详细信息:[技术分析](https://depthfirst.com/research/nginx-rift-achieving-nginx-rce-via-an-18-year-old-vulnerability)。
## 受影响及修复版本
厂商完整公告:
## 私有研究分支:启用 ASLR 的远程实验链



此分支保留了原始披露 PoC 的完整性,但增加了第二条研究路径,聚焦于一个更现实的问题:
在此研究分支中的答案是**是,但存在重要限制条件**。工作链并未禁用 ASLR,也未使用原始的硬编码堆/libc 地址。相反,它们通过同一端口的 HTTP 可访问原语推导运行时状态,然后从远程获取的泄露数据中选择最终的堆目标。
目前有两条启用 ASLR 的利用路径,其中无核心转储路径被视为当前最佳 PoC:
- `nginx_rifter.py`:清晰、自包含的评估工具和集成利用入口点。其默认利用方法现在是无核心转储的 `/proc//mem` 链。
- `nginx_rifter_core_v2_1.py`:保留的、基于核心转储的旧版 `nginx_rifter.py`。它可用于复现旧的、在 VM 中测试的基于崩溃核心转储的研究路径,但它不再是首选的 PoC。
- `tools/proc_mem_coreless_exploit.py`:较早的独立无核心转储研究测试工具。其逻辑已合并到 `nginx_rifter.py` 中;该工具保留用于原始实验重放。
目标拓扑故意设计为同一端口:
- 漏洞路由:`/api/...`
- PHP 本地文件读取路由:`/lfi.php?file=...`
- phpinfo 提示路由:`/phpinfo.php`
- HTTP/2 受害连接:相同的 nginx 监听器和 worker
- 证明验证:通过 PHP LFI 端点读回标记文件
当前无核心转储的 proc-mem 路径执行以下高级步骤:
1. 使用 PHP LFI 读取 PHP 标识、nginx PID 文件、nginx worker `/proc//maps` 以及映射的 libc 文件。
2. 通过 LFI 解析目标 libc,计算该 worker 的绝对 `system()` 地址。
3. 发送正常的 NGINX Rift 喷射/探测流量,同时保持 worker 状态活跃。
4. 通过文件读取原语从 `/proc//mem` 读取映射范围。
5. 扫描活动内存,查找带有 nonce 标记的伪造 cleanup 结构体和 cleanup-pool 候选对象。
6. 使用从活动 worker 内存中推导出的、有界的最终候选对象,而不是硬编码的实验室偏移或可读的崩溃核心转储。
7. 通过文件读取原语读取标记输出来验证命令执行。
旧的基于核心转储的路径执行类似的基地址推导,然后故意使一个 worker 崩溃,通过 LFI 读取生成的核心文件,并从该核心文件中挖掘喷射的伪造 cleanup 插槽。这曾是一个有用的研究桥梁,但它依赖于核心转储策略和文件系统权限,这些在默认部署中不太常见。
这与原始的确定性 Docker 演示不同。x86_64 VM 路径保持正常的 Linux ASLR 启用,并在每次运行时重新计算特定于进程的地址。Docker 无核心转储路径也保持 ASLR 启用,并移除了不常见的可读核心转储要求,但它依赖于必须为目标类别验证的 procfs 权限行为。
### 范围与注意事项
此分支是一个受控的研究实验室。启用 ASLR 的链依赖于并非普遍适用于生产环境的强条件:
- PHP 必须暴露一个有用的本地文件读取原语。
- 对于默认的无核心转储 proc-mem 路径,PHP 必须能够读取相同 UID 的 nginx worker 的 `/proc//maps`、映射的 libc 以及大偏移映射下的 `/proc//mem`。
- 对于旧的基于核心转储的路径,PHP 必须能够读取相同 UID 的 nginx worker 的 `/proc//maps`、映射的 libc 以及生成的 worker 核心转储。
- HTTP/2 必须在同一个 nginx 监听器上启用,以提供最终链所需的连接池清理目标。
`phpinfo()` 和 `/proc//maps` 足以恢复 PIE/libc 基地址,但它们本身不足以恢复此利用所需的精确堆对象/窗口。旧链使用可读的崩溃核心转储进行最终信息泄露。当前的默认链则使用 `/proc//mem`,这更接近真实任意文件读取在相同 UID 部署中的后果,因为它暴露了活动的 worker 内存而无需更改核心转储策略。
剩余的重要限制:
- `/proc//mem` 受 ptrace 门控。它在 Docker 实验室中以及在对官方 `nginx:stable` 镜像模型的相同 UID 检查中有效,但在默认 procfs 保护下,不同 UID 的应用进程应该会失败。
- 文件读取原语必须支持大偏移或等效的范围 API。
- proc-mem 路径的真正 Ubuntu VM 复测仍在进行中。
- 尚未找到直接的非 LFI nginx 响应内存泄露。被动反射、重定向/头部/主体探测、初始的延迟代理过读扫描以及 SSRF 辅助的源代码审查均未产生与 ASLR 相关的泄露。
### 当前工具
当前清晰的入口点是 `nginx_rifter.py`,这是一个评估优先的工具,旨在更接近授权测试人员如何评估一个已知存在漏洞、且具有 HTTP 可访问本地文件读取原语的 nginx 部署。
与初始演示运行器相比,`nginx_rifter.py` 在多个方面改进了工作流程:
- 评估是默认模式。除非明确提供 `--exploit`,否则不会运行崩溃利用。
- 目标以 `HOST:PORT` 形式提供,文件读取原语通过 `--file-read-template` 模块化。
- 在依赖 LFI 原语之前,它会对其进行分析,包括文本读取、二进制读取、范围读取、`/proc/self/status`、`/proc/self/maps` 以及相同 UID worker procfs 的可达性。
- 它通过远程原语发现 nginx worker 映射、libc、`system()`、构建 ID、二进制哈希、OS 详情以及 proc-mem/核心转储设置。
- 它尝试从主进程命令行和常见配置路径发现 nginx 配置,然后标记易受攻击的 `rewrite` + `set` 路由候选。
- 它打印一个利用链可行性矩阵,以便在任何尝试利用之前清楚地看到缺失的先决条件。
- 利用模式是显式的,并集成到 `nginx_rifter.py` 中;默认方法是无核心转储的 proc-mem。
当前的 `nginx_rifter.py` 是自包含的。它不再导入或调用早期的演示 PoC 版本或 `tools/proc_mem_coreless_exploit.py` 来进行评估或利用。
旧的基于核心转储的 `nginx_rifter.py` 实现被保留为 `nginx_rifter_core_v2_1.py`。
较新的 `artifacts/nginx_rifter_v3_coreless_exploit_20260519.gif` 展示了合并后的 v3 `nginx_rifter.py` 无核心转储利用路径。`demo4.gif` 展示了早期一体化核心转储工具的评估和显式利用流程。更早的 `nginx-aslr-demo.gif` 仍作为原始的启用 ASLR 的利用演示。
## 使用方法
在 Ubuntu 24.04.3 LTS 上测试。
原始 ASLR 禁用的 Docker 复现:
1. `./setup.sh` — 构建容器。
2. `docker compose -f env/docker-compose.yml up` — 启动存在漏洞的 NGINX 服务器。
3. `python3 poc.py --shell` — 获得一个 shell。
关于本地 Docker 复现流程,请参见 [LAB.md](LAB.md)。
旧版启用 ASLR 的 VM 基于核心转储的链:
```
./nginx_rifter_core_v2_1.py --target :19321 --exploit --cmd id --fast
```
评估优先的 v3 工具:
```
./nginx_rifter.py --target :19321
```
`nginx_rifter.py` 是当前面向现实世界的评估器和集成 PoC 入口点。其默认模式不运行崩溃利用路径。它分析 HTTP 文件读取原语,检查范围和二进制读取,对 OS/nginx/libc 进行指纹识别,发现 nginx worker 和与 ASLR 相关的映射,测试相同 UID `/proc//mem` 的可读性,尝试通过 pid/cmdline/配置读取恢复 nginx 配置路径,标记易受攻击的 `rewrite` + `set` 路由候选,并打印当前无核心转储链的可行性矩阵。
当前的 `nginx_rifter.py` 是自包含的。它不再导入或调用早期的演示 PoC 版本或独立的 proc-mem 研究工具进行评估或利用。
对于自定义的 LFI/下载格式:
```
./nginx_rifter.py --target :19321 \
--file-read-template 'http://{host}:{port}/download?path={path_url}{range_query}'
```
利用执行是显式的:
```
./nginx_rifter.py --target :19321 --exploit --cmd id --fast
# - "Rift" – if it's part of a technical term or name, keep in English. But in the second heading, "exploit" and others are kept in English.
./nginx_rifter.py --target :19321 --exploit --derive-only --cmd id
```
默认的利用方法是无核心转储的 proc-mem。以下选项已被默认选中,因为它们是对于无核心转储 Docker 证明最可靠的:
```
--exploit-method proc-mem
--target-len 6
--max-region 268435456
```
旧的可读核心转储模式仍可用于比较,但版本化脚本在复现该旧路径时更清晰:
```
./nginx_rifter_core_v2_1.py --target :19321 --exploit --cmd id --fast
```
旧版便于录制的终端演示:
```
./demo_ctf_exploit_v1_9.py --host :19321 --cmd id --clear
```
`demo_ctf_exploit_v1_9.py` 是用于基于核心转储的实验室路径的旧版操作员运行器。当前首选的 PoC 入口点是 `nginx_rifter.py`。
默认的文件读取原语是此分支的 PHP 路由:
```
/lfi.php?file=&offset=&length=
```
对于其他已知存在漏洞的 CTF 应用或测试平台,文件读取向量是模块化的:
```
./nginx_rifter.py --target :19321 --exploit --cmd id \
--target-profile generic \
--file-read-template 'http://{host}:{port}/download?path={path_url}{range_query}'
```
模板支持 `{host}`、`{port}`、`{path_url}`、`{offset}`、`{length}` 和 `{range_query}`。通用分析会跳过此分支实验室特定的 nginx 配置断言,但默认利用仍然需要相同的基础能力:可读的 nginx worker `/proc` 映射、可读的 libc 以及可读的 `/proc//mem`。`phpinfo()` 是可选的;使用 `--phpinfo-path ''` 可禁用它。
现实性注意事项:LFI/文件读取漏洞类别和同主机 nginx/PHP-FPM 部署模型是现实的。proc-mem 链比早期的崩溃核心转储链更现实,因为它不需要启用或读取 worker 核心转储。它仍然不是普遍的默认生产环境假设:相同 UID 的进程布局、procfs/Yama 策略、容器命名空间设置以及文件读取原语的质量决定了 `/proc//mem` 是否可达。
无 LFI 研究探测:
```
python3 tools/non_lfi_leak_probe.py --target :19321
python3 tools/non_lfi_active_response_probe.py --target :19321
```
这些是阴性研究探测,而非利用入口点。它们在不使用 LFI、phpinfo、procfs、核心转储、调试器访问或硬编码实时 ASLR 基址的情况下,测试被动反射汇点和初始的延迟响应过读格式。
其他实验室笔记和运行日志位于 `docs/` 下,特别是:
- `docs/CTF_PLAN.md`
- `docs/CTF_FINDINGS.md`
- `docs/CTF_TESTS.md`
- `docs/CTF_EXPERIMENT_LOG.md`
- `docs/DEMO_POC_IMPROVEMENTS.md`
- `docs/KNOWN_LAYOUT_PATTERNS.md`
- `docs/VAGRANT_ESXI.md`
- `docs/CORELESS_ASLR_PLAN.md`
- `docs/CORELESS_ASLR_FINDINGS.md`
- `docs/NON_LFI_ASLR_BYPASS_LOG.md`
- `docs/DEMO_RECORDING_WORKFLOW.md`
标签:请求拦截, 逆向工具