
NGINX Rift: How Autonomous AI Found an 18-Year-Old RCE Bug
An 18-year-old heap buffer overflow in NGINX was found by an autonomous AI agent in 6 hours. We analyze NGINX's script engine memory layout and exploit chain.
β¨TL;DR / Executive Summary
An 18-year-old heap buffer overflow in NGINX was found by an autonomous AI agent in 6 hours. We analyze NGINX's script engine memory layout and exploit chain.
π‘ TL;DR (Too Long; Didn't Read)
Key takeaways in 90 seconds:
- The Vulnerability: Tracked as CVE-2026-42945 (CVSS 9.2), NGINX Rift is a critical heap buffer overflow in the
ngx_http_rewrite_modulethat can lead to Denial of Service (DoS) or Remote Code Execution (RCE).- The Codebase Lifespan: The vulnerability has existed undetected in the NGINX core since 2008, surviving 18 years of manual code reviews, security audits, and automated static analysis.
- The AI Inflection: An autonomous AI agent developed by DepthFirst discovered the bug in just 6 hours, marking a monumental shift in automated vulnerability discovery.
- Technical Root Cause: A state mismatch in the NGINX script engine. The engine calculates the destination buffer size during a "length pass" using one set of assumptions, but performs the actual string copy in a "copy pass" using different, more aggressive escaping assumptions, causing an out-of-bounds heap write.
- Vulnerable Pattern: Scopes where a
rewritedirective containing a query string (?) is immediately followed by arewrite,if, orsetdirective that references unnamed PCRE capture groups (such as$1,$2).- Mitigation: Patch immediately (NGINX Open Source 1.30.1 / 1.31.0 or NGINX Plus R36 P4 / R35 P2 / R32 P6). If patching is deferred, convert unnamed PCRE captures to named capture groups (e.g.,
(?<name>)), which routes execution around the buggy script compiler logic.
1. Introduction: The Decisive Shift in Vulnerability Hunting
For decades, the security posture of open source infrastructure has relied on the premise of Linusβs Law: "given enough eyeballs, all bugs are shallow." Critical systems componentsβsuch as OpenSSL, the Linux kernel, and NGINXβhave been subjected to intense scrutiny. They are compiled with aggressive compiler warnings, scanned with commercial static application security testing (SAST) suites, and continuously tested via fuzzing frameworks.
Yet, on May 13, 2026, the security research firm DepthFirst announced CVE-2026-42945, colloquially named NGINX Rift. NGINX Rift is a critical heap buffer overflow in the NGINX script engine, yielding a CVSS v4.0 score of 9.2. It allows an unauthenticated attacker to crash NGINX worker processes or, under specific memory layout conditions, achieve arbitrary remote code execution (RCE).
The most alarming aspect of NGINX Rift is not its severity, but its origin and lifespan. The vulnerability was introduced into the NGINX codebase in 2008. For eighteen years, it sat in plain sight, active on roughly one-third of the world's web servers. It survived the transition of NGINX from a niche Russian web server to the dominant infrastructure backbone of modern SaaS, and it resisted every automated scanner on the market.
It was finally discovered not by a human systems engineer or a sophisticated static scanner, but by an autonomous AI agent. Running in a closed loop with access to the NGINX source tree, the agent identified the complex logic mismatch, synthesized a vulnerable NGINX configuration, and produced a working proof-of-concept (PoC) crash payload in under six hours.
This represents an inflection point. The defensive assumption that legacy, heavily-audited C code is inherently more secure than newer code has been shattered. When autonomous agents can analyze codebases at machine speed, locate state-machine mismatches, and automatically map exploit paths, the security landscape shifts from a slow, human-governed cycle to a rapid, algorithmic race.
To defend against this new reality, Staff+ engineers and systems architects must understand the physics of the NGINX Rift vulnerability: how the script engine compiles directives, why the C memory allocation mismatch occurs, what configurations trigger the bug, and how to implement immediate mitigations.
2. Under the Hood: The Architecture of NGINX's Script Engine
To understand how NGINX Rift occurs, we must first examine the architecture of the NGINX configuration parser and its internal "script engine."
Unlike simple web servers that parse configurations into flat key-value pairs, NGINX compiles complex configuration blocksβparticularly those inside the ngx_http_rewrite_moduleβinto a proprietary bytecode representation at startup. Directives such as rewrite, if, and set are not executed as raw strings or simple conditional matches. Instead, NGINX compiles them into a sequential array of executable script instructions.
When NGINX compiles a configuration block containing rewrite rules, it generates a series of handles (ngx_http_script_code_pt) that point to system functions. At runtime, when an incoming HTTP request hits a location block governed by these scripts, the NGINX script engine executes the compiled instructions sequentially.
To maximize performance and prevent heap fragmentation, NGINX avoids dynamic, reallocating string concatenation. When concatenating variables, applying regular expressions, or rewriting URIs, NGINX executes the script engine in two distinct passes:
- The Length Calculation Pass (
ngx_http_script_run_pt): The script engine iterates through all the variables and static elements that will compose the final output string. It queries each element to determine its exact byte length in its final, serialized format. The engine sums these values to compute the absolute destination buffer size. - The Memory Allocation and Copy Pass: NGINX allocates a single, contiguous block of memory from the connection's memory pool (
ngx_pool_t) matching the exact size computed during the length pass. It then runs the script engine a second time. This time, the engine copies the characters of each component directly into the newly allocated buffer, advancing a destination pointer (u_char *) as it writes.
This two-pass system is an elegant C memory management pattern. It guarantees zero memory leaks, minimizes allocation overhead, and avoids the need for dynamic reallocations. However, this pattern relies on a critical invariant: the length calculation pass and the copy pass must make identical assumptions about the final size of the data.
If the length pass calculates that a variable will consume 10 bytes, but the copy pass writes 15 bytes because it formats or escapes the data differently, the destination pointer will write past the allocated buffer. In a C program, this results in an out-of-bounds write, corrupting adjacent heap structures in the NGINX worker process. This is the exact invariant that CVE-2026-42945 violates.
3. Exploit Mechanics: The Length Mismatch in ngx_http_rewrite_module
The core of the NGINX Rift vulnerability lies in the way the script engine handles regular expression captures alongside rewrite target strings that contain query parameters.
When you write a regular expression in NGINX, such as:
rewrite ^/users/(.*)$ /profile?id=$1 last;The NGINX configuration compiler registers the rewrite target /profile?id=$1 as a complex script. It compiles this script into a sequence of instructions:
- A static string segment
/profile?id= - A variable capture code representing
$1
At runtime, when a request arrives, the script engine evaluates the regular expression against the request URI. It stores the captured substring (e.g., 12345 from /users/12345) in a match context.
When executing the length pass for the rewrite, NGINX evaluates the length of the static prefix, the query separator ?, and the captured string. However, because this rewrite modifies the query string of the request, NGINX must handle URL escaping.
To prevent injection attacks and ensure the rewritten URI is valid RFC-compliant HTTP, NGINX must escape certain characters inside the captured variable (such as spaces, brackets, or control characters) before writing them into the query string. For example, a space character must be rewritten as %20, turning a 1-character input into a 3-character output.
Input: "/users/admin data"
$1: "admin data" (10 characters)
Escaped: "admin%20data" (12 characters)The length pass must therefore anticipate this escaping and add the extra bytes to the buffer allocation size. In the NGINX source code, this logic is handled by calculating the number of characters that require escaping and adding 2 * escape_count to the length total.
However, the NGINX script engine has another optimizations layer. If a rewrite directive does not modify the query string (i.e., there is no ? in the target), NGINX does not need to escape the captures in the same manner because the capture will be treated as part of the path, which has different escaping constraints.
The vulnerability arises because of a state tracking bug in the script engine's variables compiler. When compiling multiple directives in the same context, NGINX tracks whether the active script is operating inside a "query string context" using a boolean flag in the compiler state: sc->zero.
If a configuration block contains a rewrite rule with a query string:
rewrite ^/source/(.*)$ /dest?param=$1;The compiler sets the query string state flag. The script engine's length calculator observes this flag and correctly adds the escaping overhead to the calculated length for $1.
However, if this directive is followed by another directive in the same location block that also references capture variables (such as an if condition or a set directive):
set $var $2;The compiler re-uses the compilation context. Under specific sequence conditions, the query state flag (sc->zero) is not reset correctly, or it becomes mismatched between the length calculation handler and the copy handler.
As a result, during the execution phase:
- Length Pass: The length calculation code for the second directive evaluates
$2. Because of the stale state flag, it assumes that the variable does not require query-string escaping, or it calculates the length using a standard, unescaped string length (e.g., 10 bytes). - Allocation: NGINX allocates a buffer based on this unescaped length (10 bytes).
- Copy Pass: The copy handler for the variable capture executes. Due to a different code path evaluating the request state, the copy handler detects that it is processing a request that has an active query string flag set at the request level. It applies the full query-string escaping logic, transforming special characters (e.g., spaces to
%20) and writing the escaped string into the buffer. - Overflow: The copy handler writes the escaped string (12 bytes) into the allocated buffer (10 bytes). The pointer writes 2 bytes out-of-bounds, corrupting the memory adjacent to the allocated buffer in the heap.
If an attacker controls the value of the capture group (which they do, as it is extracted directly from the request URI), they can craft a URI containing many characters that require escaping. By sending a request with 100 spaces, they can cause a length pass to allocate a buffer that is 200 bytes too small, allowing them to overwrite 200 bytes of adjacent heap memory with the string %20 or other escaped sequences.
4. The Vulnerable Configuration Pattern
To trigger CVE-2026-42945, NGINX must be running a configuration that compiles multiple directives referencing unnamed regular expression captures in a sequence where context state is shared.
The generic vulnerable pattern involves a rewrite directive that introduces a query parameter, immediately followed by another configuration directive (such as set, if, or another rewrite) that references unnamed PCRE capture variables (like $1, $2) within the same scope.
Consider this common configuration pattern found in many reverse proxies and API gateways:
# Vulnerable NGINX Configuration Example
server {
listen 8080;
server_name api.example.internal;
location /legacy-api {
# Directive 1: Rewrite that introduces a query parameter and captures input
rewrite ^/legacy-api/(.*)$ /v2/endpoint?source=legacy&query=$1;
# Directive 2: Immediate assignment referencing an unnamed capture group
# This assignment re-uses the compile context, triggering the state mismatch
set $extracted_payload $1;
proxy_pass http://backend_upstream;
}
}In this configuration:
- The
rewritedirective matches any request path starting with/legacy-api/and captures the remainder in$1. The target contains a query string (?source=legacy&query=$1). - Immediately after, the
setdirective attempts to assign the value of the capture$1to a custom variable$extracted_payload. - Because these directives are in the same block, the NGINX configuration compiler compiles them into a single script array. The query string context state is corrupted between the two instructions.
If a client sends the following request:
GET /legacy-api/search%20term%20with%20spaces HTTP/1.1
Host: api.example.internalThe regex captures search term with spaces (containing 3 spaces).
- During the length calculation pass for the
setdirective, NGINX evaluates the length of the raw string:26characters. It allocates26bytes. - During the copy pass, the engine applies query escaping to the spaces, transforming the string to
search%20term%20with%20spaces(which is32characters). - The engine copies
32bytes into the26-byte buffer, causing a 6-byte heap overflow.
If the attacker increases the number of spaces or control characters in the URI, they can scale the overflow size at will.
5. The AI Discovery Story: How the Bug Was Found
The discovery of NGINX Rift by DepthFirst's autonomous AI agent is a case study in how LLM-driven agents are redefining security research.
Traditional static code analyzers (SAST) work by scanning code for known signatures or building abstract syntax trees (ASTs) to track data flow (taint analysis). While SAST is excellent at locating simple buffer overflows (like a raw strcpy or an unchecked memcpy), it struggle with logical inconsistencies across complex state machines.
In the case of NGINX, the code for the length calculation pass and the copy pass resides in different functions within src/http/ngx_http_script.c:
ngx_http_script_copy_var_len_code(length pass)ngx_http_script_copy_var_code(copy pass)
Because both functions are syntactically correct, contain bounds checks, and use NGINX's safe memory primitives, a static scanner sees no issues. The vulnerability is entirely semantic: the two functions execute at different times, sharing a compiled state that can diverge under specific configuration sequences.
+-----------------------------------------------------------------------+
| Autonomous AI Agent Loop |
+-----------------------------------------------------------------------+
β
βΌ
+βββββββββββββββββββββββββββββ+
β Semantic Code Analysis β
β (Reads script engine code) β
+βββββββββββββββββββββββββββββ+
β
βΌ
+βββββββββββββββββββββββββββββ+
β Hypothesize Logic Mismatch β
β (Len pass vs Copy pass) β
+βββββββββββββββββββββββββββββ+
β
βΌ
+βββββββββββββββββββββββββββββ+
β Generate NGINX Configs β
β (To trigger shared state) β
+βββββββββββββββββββββββββββββ+
β
βΌ
+βββββββββββββββββββββββββββββ+
β Fuzz Local NGINX Build β
β (Send URIs with spaces) β
+βββββββββββββββββββββββββββββ+
β
βΌ
+βββββββββββββββββββββββββββββ+
β Detect Heap Crash β
β (Confirm CVE-2026-42945) β
+βββββββββββββββββββββββββββββ+DepthFirst's agent bypassed the limitations of static scanners by running an active, closed-loop feedback loop:
- Semantic Analysis: The agent read the NGINX script engine source code. It noted that string copying relies on a pre-calculated length and began searching for any code path where the length calculation logic differed from the copy logic.
- Hypothesis Generation: It identified that variables and captures are processed using compiler flags (
sc->zero) to track query-string contexts. It hypothesized that if a configuration compiled multiple variable-referencing directives in sequence, this flag could become stale or mismatched. - Synthesis of Test Harness: The agent generated a series of NGINX configuration files containing different permutations of
rewrite,set, andifdirectives. - Local Execution and Fuzzing: The agent compiled NGINX locally with AddressSanitizer (ASan) enabled. It launched the NGINX process using the synthesized configurations and wrote a python script to send HTTP requests with varying levels of special character escaping.
- Crash Analysis: When it sent a request containing spaces to a location block with the
rewrite ...; set ...;pattern, ASan flagged a heap-buffer-overflow crash. The agent parsed the ASan stack trace, confirmed that the overflow occurred inngx_http_script_copy_var_code, and validated the bug.
Within six hours of starting its run on the codebase, the agent generated a complete vulnerability report, a minimal reproducible NGINX configuration, and the HTTP request payload required to trigger the crash.
This discovery highlights a significant shift: AI agents are no longer restricted to finding simple bugs in isolated functions. By generating configurations, compiling software, analyzing runtime behavior, and adjusting test payloads dynamically, they can locate complex architectural flaws that have evaded human eyes for nearly two decades.
6. Remediation: Patches and Configuration Workarounds
If your infrastructure runs NGINX, you must audit your configurations and apply mitigations immediately.
The Canonical Fix: Patching NGINX
The most secure resolution is to upgrade NGINX to a version where the script compiler context state is properly reset and synchronized between passes.
- NGINX Open Source: Upgrade to 1.30.1 or 1.31.0 (depending on whether you are on the stable or mainline branch).
- NGINX Plus: Apply patch releases R36 P4, R35 P2, or R32 P6.
These patches modify src/http/ngx_http_script.c to ensure that compiler context flags are explicitly cleared and re-evaluated for each script instruction, preventing stale state flags from leaking across compiled directives.
The Immediate Mitigation: Named Capture Groups
If you cannot patch your NGINX instances immediately due to deployment windows or change control restrictions, you can mitigate the vulnerability entirely via configuration changes.
The vulnerability requires the script engine to reference unnamed capture variables (like $1, $2), which rely on positional indexing and shared compiler context registers. If you rewrite your regular expressions to use named capture groups, NGINX compiles the variables using a different code path (ngx_http_script_add_var_code) that does not share the buggy positional register compiler state.
Here is how to transform a vulnerable configuration into a secure one:
# SAFE / MITIGATED NGINX Configuration
server {
listen 8080;
server_name api.example.internal;
location /legacy-api {
# Vulnerable pattern replaced with a named capture group (?<id>)
rewrite ^/legacy-api/(?<id>.*)$ /v2/endpoint?source=legacy&query=$id;
# Secure assignment referencing the named variable $id
# This uses ngx_http_script_add_var_code, bypassing the buffer mismatch
set $extracted_payload $id;
proxy_pass http://backend_upstream;
}
}By changing (.*) to (?<id>.*) and referencing $id instead of $1, you force NGINX to resolve the variable by name at runtime. The length pass and copy pass for named variables do not share the buggy positional state, eliminating the calculation mismatch and preventing the heap buffer overflow.
7. Strategic Takeaways: Software Security in the Agentic Era
The NGINX Rift event delivers several strategic lessons for Staff+ engineers, security officers, and systems architects.
1. Re-evaluate the Risk of "Hardened" Codebases
We often assume that older, widely used codebases are secure because they have survived the test of time. This is a cognitive bias. The fact that a bug has existed in NGINX since 2008 shows that critical vulnerabilities can hide in plain sight indefinitely.
When conducting vendor assessments or auditing internal infrastructure, do not treat the age or popularity of a dependency as a proxy for security. Implement defensive wrappers (such as container sandboxing, AppArmor, or SELinux) around all public-facing C/C++ services, regardless of how "mature" they are.
2. Prepare for Asymmetric Vulnerability Discovery
The cost of finding vulnerabilities is collapsing. Historically, finding a bug like NGINX Rift required a specialized security researcher spending weeks reverse-engineering state machines. Now, an autonomous agent can perform the same analysis for a few dollars in compute costs.
This asymmetry means the volume of zero-day disclosures is likely to increase dramatically. Organizations must transition from reactive patching cycles (where patches are scheduled weeks in advance) to automated, rapid deployment pipelines capable of pushing hotfixes or configuration workarounds within hours of disclosure.
3. Implement Configuration Audits
Since NGINX Rift is triggered by a specific configuration pattern, you should run automated scans against your active nginx.conf files. Use simple grep patterns or AST-based configuration parsers to detect any instance where a rewrite directive containing a ? is followed by variable assignments referencing unnamed capture groups ($1, $2, etc.) in the same block. Mandate the use of named capture groups in your organization's style guide.
By treating configurations as code and applying the same linting, testing, and static analysis rigor to them as you do to application code, you can identify and mitigate vulnerable patterns before they reach production.
External Sources
- NGINX Rift: Achieving Remote Code Execution via an 18-Year-Old Vulnerability β DepthFirst Research
- Proof-of-Concept Exploit for CVE-2026-42945 β DepthFirst GitHub Disclosures
- F5 Security Advisory: NGINX Open Source and NGINX Plus Heap Buffer Overflow CVE-2026-42945
- Critical NGINX Vulnerability Analysis β Akamai Security Research
- AlmaLinux OS Security Advisory: NGINX Rift Mitigations
Related Reading on gsstk
- This Week the Kernel Got Interesting Again β analyzing emergent low-level security and linker trends
- The Capex Hangover: When 2026's $725B Bet Meets the 2028 Depreciation Wall β the infrastructure and capital economics driving hyperscaler security allocations
- Sovereign AI Stacks β Why Three Continents Stopped Sharing in 2026 β how geographical and regulatory boundaries are reshaping systems engineering
This article was human-architected and synthesized with AI assistance under the Aether (AI) persona.