A critical heap buffer overflow (CVSS 9.2) in NGINX's rewrite module — sitting undetected since 2008 — allows an unauthenticated attacker to crash or remotely execute code on any NGINX server using rewrite and set directives, with a single crafted HTTP request. Discovered in six hours by an autonomous AI analysis system, the flaw affects every NGINX Open Source release from 0.6.27 through 1.30.0, NGINX Plus R32–R36, and a wide range of F5 products including Kubernetes Ingress Controllers and Gateway Fabric. This post covers the root cause (a two-pass script engine state mismatch), the full exploit chain, a step-by-step lab reproduction using the public PoC, detection guidance, and a prioritized remediation checklist. Patches are available — upgrade to NGINX 1.30.1 or 1.31.0 now.
| CVE | CVE-2026-42945 (NGINX Rift) |
| CVSS Score | 9.2 — Critical |
| Attack Vector | Network — no authentication required |
| Impact | Remote Code Execution / Denial of Service (worker process) |
| Introduced | 2008 — NGINX 0.6.27 |
| Patched In | NGINX 1.31.0 / 1.30.1 stable |
On May 13, 2026, F5 and depthfirst jointly disclosed CVE-2026-42945 — a critical heap buffer overflow in NGINX's ngx_http_rewrite_module that has been sitting silently in the codebase since 2008. The vulnerability allows an unauthenticated attacker to crash a NGINX worker process or, under exploitable conditions, achieve full remote code execution with a single crafted HTTP request. The flaw affects every NGINX Open Source release from 0.6.27 through 1.30.0, NGINX Plus R32 through R36, and a wide range of downstream F5 products.
NGINX powers approximately one-third of all websites globally — it is the default reverse proxy, load balancer, and TLS terminator in Kubernetes ingress controllers, CI/CD platforms, and cloud-native application stacks. This is not a niche library. A single exploitable NGINX instance sits at the perimeter of most modern infrastructure. This post is a forensic breakdown to help defenders triage, patch, and detect exploitation attempts.
Quick context for non-specialists
NGINX is a web server and reverse proxy used to receive incoming HTTP/HTTPS traffic and route it to backend services. The rewrite directive lets administrators rewrite request URLs on the fly using regex patterns. The set directive stores values (like a captured regex group) into a variable. Together they're a standard API gateway pattern. The vulnerability lives in the C code that processes these two directives in sequence — a size-accounting bug that has gone unnoticed since the code was originally written 18 years ago.
TL;DR
- A heap buffer overflow in NGINX's script engine (
ngx_http_script.c) is triggered when arewritedirective with a question mark in the replacement string is followed by asetdirective that references a regex capture group. - The root cause is a state-mismatch between NGINX's two-pass script evaluation: the length pass uses a zeroed sub-engine (
le.is_args = 0) while the copy pass runs on the main engine wheree.is_args = 1, causingngx_escape_urito write more bytes than were allocated. - Overflow size is fully attacker-controlled: each URI-escapable character (
+,%,&) expands from 1 byte to 3 bytes, making the overflow proportional to the number of such characters in the crafted request URI. - depthfirst's autonomous AI system found the bug in six hours of scanning — 18 years after it was introduced. This is the first public disclosure of this class of NGINX vulnerability.
- A working proof-of-concept demonstrating unauthenticated RCE (ASLR off) has been published on GitHub. Exploitation uses heap feng shui across two connections to overwrite the
ngx_pool_t cleanuppointer and hijack control flow viasystem(). - Patches are available: NGINX Open Source 1.31.0 / 1.30.1, NGINX Plus R36 P1. Upgrade now. If you cannot upgrade immediately, remove
rewrite+setdirective pairings or disable the rewrite module. - Three additional CVEs were disclosed alongside NGINX Rift: CVE-2026-42946 (CVSS 8.3), CVE-2026-40701 (CVSS 6.3), and CVE-2026-42934 (CVSS 6.3).
Am I affected? (60-second triage)
| If… | Then… |
|---|---|
| You run NGINX Open Source 0.6.27 – 1.30.0 | You are running vulnerable software. Upgrade to 1.30.1 (stable branch) or 1.31.0 (mainline) immediately. |
| You run NGINX Plus R32 – R36 | Apply patch R36 P1. Earlier Plus releases are also affected — check the F5 advisory for per-release guidance. |
Your NGINX config contains a rewrite with ? in the replacement, followed by set $var $1 in the same block | Treat as actively exploitable at this moment. This is the exact trigger configuration. Patch or remove the directive pair now. |
| You run NGINX Ingress Controller 3.5.0–5.4.1 or 4.x / 5.x on Kubernetes | Patch or upgrade the controller image. Exploit success here = potential lateral movement to the entire cluster. Rotate cluster-admin tokens if exploitation cannot be ruled out. |
| You run NGINX Gateway Fabric 1.3.0–2.5.1 or 2.0.0–2.5.1 | Upgrade to 2.6.0+ or 1.7.0+. Same urgency as Ingress Controller. |
| You use F5 NGINX App Protect WAF / DoS in the affected range | Check F5 advisory K000160932 for the relevant patch per product version. |
Your NGINX config does not use rewrite + set $N in combination | Crash (DoS) via the sibling CVEs remains possible. Still upgrade. Unaffected by the RCE vector specifically. |
| ASLR is enabled on your NGINX host (default on modern Linux) | RCE is significantly harder — the published PoC only demonstrates RCE with ASLR off. DoS (worker crash → restart) is still straightforward. Patch regardless. |
| You are unsure whether your NGINX config uses the vulnerable pattern | Run grep -rn 'rewrite.*\?' /etc/nginx/ and check each hit for a subsequent set $ in the same block. |
Important note on ASLR
The published depthfirst PoC demonstrates RCE with ASLR disabled. With ASLR enabled (the Linux default), full RCE requires a bypass — the researchers note this is theoretically achievable by progressively overwriting pointers byte-by-byte across the deterministic multi-worker layout, but they have not published an ASLR-bypass PoC. Do not treat "ASLR is on" as a complete mitigation — it raises the bar, it doesn't remove the risk.
The root cause — a two-pass size mismatch
Understanding the bug requires understanding how NGINX's script engine works. When the configuration parser encounters a rewrite or set directive, it compiles it into a sequence of script operations stored in the request's memory pool. At runtime, NGINX evaluates these operations using a strict two-pass process:
- Pass 1 (length pass): Walk the script operations using a fresh, zeroed sub-engine (
le) to calculate the exact number of bytes needed, then allocate precisely that much memory from the pool. - Pass 2 (copy pass): Walk the script operations again on the main engine (
e) to actually write the data into the newly allocated buffer.
This design is intentional — it avoids repeated reallocations. But it creates an invariant: the length pass and the copy pass must see identical engine state. If any state differs between the two passes, the size computed in pass 1 won't match the bytes written in pass 2.
The bug breaks this invariant. When a rewrite directive's replacement string contains a question mark, NGINX calls ngx_http_script_start_args_code, which permanently sets e->is_args = 1 on the main engine. This flag is never cleared between script evaluations:
void
ngx_http_script_start_args_code(ngx_http_script_engine_t *e)
{
e->is_args = 1; /* SET — never reset */
e->args = e->pos;
e->ip += sizeof(uintptr_t);
}
When the subsequent set $original_path $1 directive runs, it calls ngx_http_script_complex_value_code, which creates the zeroed sub-engine for the length pass:
ngx_memzero(&le, sizeof(ngx_http_script_engine_t)); /* le.is_args = 0 */
le.ip = code->lengths->elts;
Because it is initialized with zeros, le.is_args is zero. The length calculation function ngx_http_script_copy_capture_len_code checks e->is_args to decide if escaping is needed — and since le.is_args == 0, the escaping branch is skipped, allocating only N raw bytes. In the copy pass, the same check runs on the main engine where e->is_args == 1. Now the escaping branch is taken, and ngx_escape_uri expands every escapable character from 1 byte to 3 bytes, writing N + 2*K bytes into a buffer allocated for only N:
/* OVERFLOW HAPPENS HERE */
e->pos = (u_char *) ngx_escape_uri(pos, &p[cap[n]],
cap[n + 1] - cap[n],
NGX_ESCAPE_ARGS);The overflow size 2*K is directly proportional to the number of escapable characters the attacker places in the URI — making the overflow fully attacker-controlled.
Minimal trigger configuration
location ~ ^/api/(.*)$ {
rewrite ^/api/(.*)$ /internal?migrated=true; # <-- ? sets is_args=1
set $original_endpoint $1; # <-- copy pass overflows
}A request to /api/hello+world+foo+bar+baz provides five + characters in the capture group, each expanding to %2B — writing 10 extra bytes past the allocation.
Exploitation — heap feng shui and cleanup pointer hijack
The depthfirst researchers translated the heap overflow into RCE using the following exploit chain:
Step 1 — Overwriting ngx_pool_t
NGINX allocates a ngx_pool_t structure per connection. The target field is cleanup at offset 64 — a pointer to a linked list of ngx_pool_cleanup_t objects, each holding a function pointer (handler) and its argument (data). When the pool is destroyed, NGINX iterates this list and calls every handler. The overflow is contiguous, so to reach offset 64 the attacker must first overwrite all preceding pool metadata fields (d, max, current, chain, large). Corrupting these fields would normally crash the worker before the exploit completes.
Step 2 — Cross-request heap feng shui
NGINX's multi-process architecture is key: worker processes fork from a master, so the heap layout is bit-for-bit identical across all workers. A crashed worker is simply replaced with a fresh clone of the same memory layout — the attacker gets unlimited retries at zero cost. The researchers use a two-connection ordering to achieve precise timing:
- Open a trigger connection and send only partial headers — NGINX allocates a pool for this connection.
- Open a victim connection — NGINX allocates a victim pool exactly adjacent to the trigger pool.
- Complete the trigger connection's headers — the rewrite overflow fires, writing past the trigger pool into the victim pool's header, corrupting the
cleanuppointer. - Immediately close the victim connection — NGINX calls
ngx_destroy_pool, which iterates the corruptedcleanuplist before touching any of the other corrupted fields.
Step 3 — POST body spray
Since the overflow only allows URI-safe bytes (no null bytes), arbitrary binary pointers cannot be injected through the URI. The researchers solve this by spraying the heap with POST request bodies — which are treated as raw binary streams and can contain arbitrary bytes including nulls. The spray deposits fake ngx_pool_cleanup_t structures whose handler field points to libc's system() and whose data field points to an attacker-controlled command string. Because the heap layout is deterministic across workers, the spray lands at predictable offsets, and triggering pool destruction invokes system(cmd).
Proof of concept status
depthfirst has published a working PoC at github.com/depthfirstdisclosures/nginx-rift. The PoC demonstrates RCE with ASLR disabled. An ASLR-bypass component has not been published.
Proof of concept — lab reproduction
Authorized use only
The steps below reproduce CVE-2026-42945 inside an isolated Docker environment provided by the original researchers. Do not run this against any system you do not own or have explicit written permission to test. The vulnerable container is intentionally misconfigured for research purposes and must never be exposed to a public network interface.
The full PoC — including the vulnerable NGINX environment, exploit script, and setup automation — was published by depthfirst as part of coordinated disclosure:
Repository: https://github.com/depthfirstdisclosures/nginx-rift
Prerequisites
Before beginning, ensure the following are installed on your test machine (Linux or macOS recommended):
| Requirement | Version | Purpose |
|---|---|---|
| Docker Engine | 24.x+ | Runs the vulnerable NGINX container |
| Docker Compose | v2+ | Orchestrates the lab environment |
| Python 3 | 3.9+ | Executes the exploit script |
| Git | Any | Clones the research repository |
docker info # confirm Docker is running before proceedingStep 1 — Clone the repository
git clone https://github.com/depthfirstdisclosures/nginx-rift.git
cd nginx-riftThe repository contains the vulnerable environment definition, the exploit script (poc.py), and a setup script that handles all container build steps automatically.
Step 2 — Build the vulnerable environment
Run the provided setup script. This builds a Docker image running a vulnerable version of NGINX (pre-1.30.1) configured with the exact rewrite + set directive pair that triggers CVE-2026-42945:
chmod +x setup.sh
./setup.shThe script pulls the base image, applies the vulnerable NGINX configuration, and prepares the lab network. This may take 2–3 minutes on first run. The container is not started yet at this stage.

Step 3 — Start the vulnerable server
docker compose -f env/docker-compose.yml upNGINX will start inside the container and begin listening on port 8080 on localhost. Leave this terminal open — NGINX worker process logs will stream here and are useful for observing crash-and-respawn events during exploitation. Verify the server is up in a second terminal:
curl -i http://localhost:8080/api/testA 200 OK or 404 response from NGINX (not a connection refused) confirms the environment is ready.
Step 4 — Run the exploit
python3 poc.py --shellThe script sends a crafted HTTP request containing a high density of URI-escapable characters (+) in the capture-group segment of the URL. On a vulnerable target, the ngx_escape_uri overflow corrupts the adjacent heap pool, and the exploit chain — heap feng shui, POST body spray, ngx_pool_t cleanup pointer overwrite — executes system() with an attacker-supplied command.
Expected output: The script reports a successful connection to the compromised NGINX worker process, demonstrating arbitrary command execution under the worker's privilege context.

What you should observe
| Event | Where to see it | What it means |
|---|---|---|
| Connection reset or empty response during payload send | poc.py terminal output | Overflow reached the heap pool boundary — stage 1 successful |
NGINX master respawning worker (nginx: worker process … exited) | Docker Compose terminal | Worker killed by the overflow; master spawns a fresh clone with identical heap layout |
Shell prompt / command output in poc.py | poc.py terminal | Full RCE achieved — attacker code executed inside the worker process |
Cleanup
# In the Docker Compose terminal
Ctrl+C
# Remove containers and volumes
docker compose -f env/docker-compose.yml down -v
# Confirm no residual containers are running
docker ps -a | grep nginx-riftWhat a patched version looks like
To observe the patched behaviour, rebuild the environment with NGINX 1.30.1 or 1.31.0 substituted in the Dockerfile and rerun poc.py. The exploit script will receive a normal HTTP response — no crash, no respawn, no code execution. This is the expected baseline for any production system after applying the patch.
The three sibling CVEs
| CVE | Severity | Module | Description |
|---|---|---|---|
CVE-2026-42945 | Critical 9.2 | ngx_http_rewrite_module | Heap buffer overflow via is_args state mismatch — RCE / DoS. This post's subject. |
CVE-2026-42946 | High 8.3 | ngx_http_scgi_module, ngx_http_uwsgi_module | State mismatch after an incomplete upstream status line read causes a cross-buffer pointer subtraction, producing a ~1 TB key length and crashing the worker (DoS). |
CVE-2026-40701 | Medium 6.3 | ngx_http_ssl_module | Use-after-free if a TLS connection closes before async OCSP DNS resolution completes. DNS timer later dereferences the freed context pool pointer. |
CVE-2026-42934 | Medium 6.3 | ngx_http_charset_module | Off-by-one error on incomplete UTF-8 sequences across proxy buffer boundaries computes a negative source offset, reading 2 bytes before the upstream buffer (OOB read). |
All four were discovered by the same AI-assisted analysis run and reported to NGINX on 2026-04-21. Patches for all four are included in NGINX 1.30.1 / 1.31.0.
Affected versions
| Product | Affected Versions | Fixed In |
|---|---|---|
| NGINX Open Source | 0.6.27 – 1.30.0 | 1.30.1 (stable) / 1.31.0 (mainline) |
| NGINX Plus | R32 – R36 | R36 P1 |
| NGINX Instance Manager | 2.16.0 – 2.21.1 | See F5 advisory K000160932 |
| F5 WAF for NGINX | 5.9.0 – 5.12.1 | See F5 advisory K000160932 |
| NGINX App Protect WAF | 4.9.0 – 4.16.0 and 5.1.0 – 5.8.0 | See F5 advisory K000160932 |
| F5 DoS for NGINX | 4.8.0 | See F5 advisory K000160932 |
| NGINX App Protect DoS | 4.3.0 – 4.7.0 | See F5 advisory K000160932 |
| NGINX Gateway Fabric | 1.3.0 – 1.6.2 and 2.0.0 – 2.5.1 | 2.6.0 / 1.7.0 |
| NGINX Ingress Controller | 3.5.0 – 3.7.2, 4.0.0 – 4.0.1, 5.0.0 – 5.4.1 | See F5 advisory K000160932 |
Detection — what to look for
Configuration scanning (immediate)
Run this grep across your NGINX config tree to identify the exploitable directive combination:
grep -rn 'rewrite.*?' /etc/nginx/ | grep -v '.bak'For each hit, manually verify whether the same location or server block also contains a set $varname $1 (or $2, $3, etc.). That combination is the exploitable pattern.
HTTP log anomalies
The exploit sends requests with high concentrations of URI-escapable characters (+, %) in path components. An IDS/WAF rule looking for paths where the percent of escapable characters exceeds a threshold (e.g. >20% of path bytes) will flag exploit attempts before a worker crash confirms them.
Worker crash frequency
Even failed exploit attempts crash the targeted worker process. A spike in NGINX worker restarts — visible in journalctl -u nginx or your container orchestration platform's pod restart counter — is an early indicator of active exploitation attempts. Alert on ≥3 restarts in 60 seconds.
Suricata / Snort rule sketch
alert http any any -> $HTTP_SERVERS any (
msg:"NGINX Rift CVE-2026-42945 exploit attempt";
flow:established,to_server;
content:"GET"; http_method;
pcre:"/\/[a-z0-9_-]+\/([%+]{10,})/U";
threshold:type both, track by_src, count 5, seconds 10;
sid:2026042945; rev:1;
)Disclosure timeline
| 2008 | NGINX 0.6.27 released. The vulnerable two-pass size accounting code for the rewrite module is introduced — and goes undetected. |
| 2026-04-18 | depthfirst's autonomous vulnerability analysis system is pointed at the NGINX source code. Six hours later it reports 5 memory corruption findings, including the heap overflow in ngx_http_rewrite_module. |
| 2026-04-21 | depthfirst files a GitHub security advisory with F5/NGINX covering all 5 findings, including root cause analysis and AI-generated proof of concept. |
| 2026-04-24 | NGINX confirms 4 of the 5 reported issues. CVE IDs assigned. |
| 2026-04-28 | depthfirst notifies NGINX that a working RCE proof-of-concept has been developed. |
| 2026-05-05 | depthfirst shares the full RCE PoC and a demonstration video with NGINX under coordinated disclosure terms. |
| 2026-05-13 | F5 publishes NGINX security advisory K000160932. Patches released for all affected products. depthfirst publishes full research writeup and PoC source code simultaneously. |
| 2026-05-14 | This analysis published. See NVD and F5 K000160932 for ongoing updates. |
Recommended actions
| # | Recommendation | When | Effort |
|---|---|---|---|
| 1 | Upgrade NGINX Open Source to 1.30.1 (stable) or 1.31.0 (mainline). Apply R36 P1 if you are on NGINX Plus. This is the only complete fix. | Today | 30 min |
| 2 | If immediate upgrade is not possible, audit your nginx.conf for the rewrite…?… / set $var $N pattern and temporarily replace the set directive with an alternative that does not reference a capture group in a post-rewrite context. | Today | 1–2 hrs |
| 3 | If the rewrite pattern cannot be removed, add a WAF rule blocking requests with high concentrations of URI-escapable characters (+, %, &) in path segments. | Today | 1 hr |
| 4 | Patch all NGINX Ingress Controllers and NGINX Gateway Fabric instances running in Kubernetes clusters. Treat this as equivalent urgency to item 1 — the blast radius of a compromised ingress controller is cluster-wide. | Today | 1–3 hrs |
| 5 | Enable ASLR on all NGINX hosts if not already active (echo 2 > /proc/sys/kernel/randomize_va_space). This does not patch the vulnerability but materially increases exploit complexity for the RCE path. | Today | 5 min |
| 6 | Add monitoring for NGINX worker restart spikes. Alert on ≥3 restarts in 60 seconds. | This week | 1–2 hrs |
| 7 | Deploy the Suricata/Snort detection rule above on perimeter sensors. | This week | 2 hrs |
| 8 | Review your F5 product inventory against the affected version table. Patch per F5 advisory K000160932. | This week | 2–4 hrs |
| 9 | Add NGINX to your continuous dependency monitoring. This 18-year-old bug proves that infrastructure components require the same scanning discipline as npm or PyPI packages. | This month | 1 day |
| 10 | Brief your platform and infrastructure teams on AI-discovered vulnerability patterns. The cadence of long-dormant bugs in foundational software will accelerate. Detection-in-depth becomes more important, not less. | This month | 1–2 hrs |
Vulnerability identifier catalog
Primary CVE
CVE-2026-42945 CVSS 9.2 Critical Heap overflow — ngx_http_rewrite_module RCE / DoSSibling CVEs (same advisory)
CVE-2026-42946 CVSS 8.3 High ~1 TB allocation crash — ngx_http_scgi_module / ngx_http_uwsgi_module
CVE-2026-40701 CVSS 6.3 Medium TLS use-after-free — ngx_http_ssl_module
CVE-2026-42934 CVSS 6.3 Medium Off-by-one OOB read — ngx_http_charset_moduleAffected source file
src/http/ngx_http_script.c
ngx_http_script_start_args_code() — sets is_args, never reset
ngx_http_script_complex_value_code() — creates zeroed sub-engine for length pass
ngx_http_script_copy_capture_len_code() — length pass uses le.is_args = 0
ngx_http_script_copy_capture_code() — copy pass uses e->is_args = 1 → OVERFLOWVulnerable NGINX version range
Open Source: 0.6.27 – 1.30.0 (introduced 2008, 18 years of exposure)
Plus: R32 – R36
Gateway Fab: 1.3.0–1.6.2, 2.0.0–2.5.1
Ingress Ctrl: 3.5.0–3.7.2, 4.0.0–4.0.1, 5.0.0–5.4.1Trigger configuration pattern (exploitable)
rewrite ^/path/(.*)$ /dest?arg=val; # replacement contains '?'
set $var $1; # set uses capture group $N in same blockReferences
F5 Advisory https://my.f5.com/manage/s/article/K000160932
depthfirst PoC https://github.com/depthfirstdisclosures/nginx-rift
depthfirst Post https://depthfirst.com/research/nginx-rift-achieving-nginx-rce-via-an-18-year-old-vulnerability
NVD https://nvd.nist.gov/vuln/detail/CVE-2026-42945What this means
1. Infrastructure software carries the same vulnerability debt as application code — and gets less scrutiny. NGINX has been reviewed by thousands of engineers over 18 years. The rewrite module is one of the most commonly configured features. And yet a size-accounting bug that directly enables heap corruption went unnoticed across every audit, every fuzz campaign, every human code review. The class of bug (two-pass invariant violation in a hot path) is precisely what human reviewers are poorly positioned to catch and what automated static analysis with sufficient semantic depth can find systematically.
2. AI-assisted vulnerability discovery is now operating ahead of the public exploit timeline. depthfirst found this bug in six hours of autonomous scanning. The PoC was complete 10 days after disclosure. For defenders, this means the window between "vulnerability exists" and "weaponized exploit exists" is shrinking to days, not weeks. Patch cadence must match.
3. The structural fingerprint of the vulnerable configuration is detectable before exploitation. Unlike a memory-corruption vulnerability that leaves no network-visible signal until exploitation, the rewrite + set $N pattern is auditable in configuration files right now. Organizations that scan their own infrastructure configs — the same way they scan their code — would have known their exposure before this advisory landed. Configuration-as-code with continuous scanning is no longer optional hygiene; it's the earliest possible detection layer for this class of finding.
About the Author
Cybersecurity expert with extensive experience in threat analysis and security architecture.
