SEKurity GmbH Logo
CVE Research

InSEKurity of the Week (CW20/2026): NGINX Rift -- 18-Year-Old Rewrite Module Heap Overflow, Unauthenticated DoS & Potential RCE (CVE-2026-42945)

A size-mismatch bug in the NGINX rewrite module lets a remote, unauthenticated attacker overflow the heap with a single crafted HTTP request -- reliable worker crashes for everyone, potential RCE where ASLR is off. CVSS 4.0 9.2, public PoC, exploited in the wild since 2026-05-16, ~5.7M exposed servers

SEKurity Team

Offensive Security Experts

18 min read
Share:

This week in our InSEKurity of the Week series: a bug that has been quietly riding along in the world’s most-deployed web server for eighteen years. CVE-2026-42945, nicknamed “NGINX Rift”, is a heap-based buffer overflow in ngx_http_rewrite_module. A remote, unauthenticated attacker can send a single crafted HTTP request that makes NGINX compute a destination buffer with one escaping rule and then write into it with a different, more expansive one — so characters like +, %, and & triple in size and the write runs off the end of the allocation. The corruption is attacker-shaped because the overflowing bytes come straight from the request URI. For the vast majority of vulnerable configurations the practical outcome is a reliable, repeatable worker-process crash (DoS); on hosts where ASLR is disabled, multiple analyses agree it crosses into remote code execution. CVSS 4.0 9.2 (Critical). The flaw was disclosed by depthfirst on 2026-05-13 with a working PoC published to GitHub the same day, and VulnCheck’s canary systems flagged in-the-wild exploitation by 2026-05-16 — three days later. With roughly 5.7 million internet-exposed NGINX servers running a potentially vulnerable build, this is the patch-now bug of the week for anyone who runs NGINX anywhere near a rewrite rule.

📋 Summary

  • CVE ID: CVE-2026-42945 (“NGINX Rift”)
  • CVSS 4.0 Score: 9.2 (Critical)
  • CVSS 4.0 Vector: CVSS:4.0/AV:N/AC:H/AT:N/PR:N/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N
  • CVSS 3.1 Score: 8.1 (High) — CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H
  • CWE: CWE-122 (Heap-based Buffer Overflow)
  • Affected Software: NGINX Open Source 0.6.27 through 1.30.0; NGINX Plus R32 through R36; F5 products bundling the engine — NGINX Ingress Controller, F5 WAF for NGINX, NGINX Instance Manager, NGINX Gateway Fabric, NGINX App Protect DoS
  • Configuration Prerequisite: a rewrite directive whose replacement contains a ?, uses an unnamed PCRE capture ($1, $2, …), and is followed by another rewrite, if, or set directive
  • Attack Vector: Network — a single crafted HTTP request to any vulnerable virtual host
  • Authentication Required: No — fully pre-auth
  • User Interaction: None
  • Impact: Heap buffer overflow in the NGINX worker. Practically: reliable Denial of Service (worker crash/restart) for essentially all vulnerable configs; potential unauthenticated Remote Code Execution where ASLR is disabled
  • Patch Status: Fixed in NGINX Open Source 1.30.1 and 1.31.0; NGINX Plus R32 P6, R36 P4, and 37.0.0. Distro packages from AlmaLinux, Debian, and Ubuntu.
  • Published: NVD entry / public disclosure 2026-05-13; F5 advisory K000161019 (2026-05-14)
  • Exploitation Status: Public PoC on GitHub since 2026-05-13; in-the-wild exploitation attempts flagged by VulnCheck canaries on 2026-05-16, publicly confirmed 2026-05-18
  • CISA KEV: Not listed at time of writing

🧩 What is NGINX (and the rewrite module)?

NGINX is, by a wide margin, the most widely deployed web server and reverse proxy on the internet. It fronts roughly a third of all websites, terminates TLS for an enormous fraction of the world’s HTTPS traffic, and runs as the ingress data plane in a huge slice of Kubernetes clusters (NGINX Ingress Controller). NGINX comes in two editions that share the same core: NGINX Open Source (the free, ubiquitous build that ships in essentially every Linux distribution) and NGINX Plus (F5’s commercial edition with extra load-balancing, observability, and API-management features). Either way, the request-handling core — including the module at the center of this bug — is the same code.

The ngx_http_rewrite_module is one of the most-used modules in real-world NGINX configurations. It implements the rewrite, if, set, return, and break directives that administrators reach for constantly: rewriting legacy URLs to new ones, normalising paths, stripping or rebuilding query strings, doing conditional routing, and preserving the original request path for an upstream. If you have ever written rewrite ^/old/(.*)$ /new/$1 ...;, you have used the exact code path that “NGINX Rift” lives in.

Typical Use Cases (i.e. who is exposed)

  • Reverse proxies / API gateways that rewrite inbound paths before forwarding to upstreams.
  • Legacy URL migration — permanent/temporary redirects and internal rewrites that preserve captured path segments.
  • Query-string manipulation — rules that append, strip, or rebuild ?key=value arguments (this is where the ?-triggered code path lives).
  • Multi-tenant hosting — per-vhost rewrite rules generated from templates, where one risky pattern propagates across thousands of sites.
  • Kubernetes ingress — NGINX Ingress Controller renders rewrite-target annotations into the very directives in scope here.
  • CDN / WAF edge tiers — NGINX-based edge nodes that normalise requests before origin.

The exposed population is genuinely internet-scale. Public scan data puts roughly 5.7 million internet-facing NGINX servers on a potentially vulnerable version. The truly exploitable subset is smaller — it requires the specific rewrite pattern below — but that pattern is common enough, and templated config is widespread enough, that nobody should assume they are out of scope without checking.

🔬 Technical Analysis

Vulnerability Description

CVE-2026-42945 is a heap-based buffer overflow (CWE-122) in the NGINX rewrite module’s handling of the replacement string in a rewrite directive. NGINX builds the rewritten URI in two distinct passes: a length pass that computes how big the destination buffer needs to be, and a copy pass that actually writes the bytes. The bug is a state mismatch between those two passes: under a specific (and common) directive arrangement, the length pass under-counts the size, the allocation is therefore too small, and the copy pass writes past the end of it. Because the overflowing bytes are derived directly from the attacker’s request URI, the heap corruption is shaped by the attacker, not random — which is what separates “guaranteed crash” from “potential code execution”.

Root Cause Analysis

  1. A ? in the rewrite replacement flips an internal is_args flag. When a rewrite directive’s replacement string contains a question mark, NGINX records that everything after it is a query string and sets the internal is_args state to 1. Crucially, that state is sticky for the rest of the rewrite phase when the directive is followed by another rewrite, if, or set directive.
  2. The length pass uses a fresh sub-engine where is_args == 0. It returns the raw, unescaped length of each unnamed PCRE capture ($1, $2, …). No escaping is accounted for, so the computed size is the “small” number.
  3. The copy pass uses the main engine where is_args == 1. This causes it to call ngx_escape_uri() with the NGX_ESCAPE_ARGS table while emitting the captured group. Under that table, characters that are legal in a path but must be percent-encoded in a query string — notably +, %, and & — each expand from 1 byte to 3 bytes (%2B, %25, %26).
  4. Small allocation, large write. The destination buffer was sized for the unescaped capture (step 2) but the copy pass writes the escaped, expanded capture (step 3). Every +, %, or & the attacker stuffs into the capture group adds two extra bytes to the write but zero extra bytes to the allocation. The write runs off the end of the heap chunk.
  5. Unnamed captures only. The mismatch hinges on the unnamed-capture code path ($1, $2, …). Named captures ((?<name>...)) take a different, correctly-sized path — which is the basis for the official workaround.
  6. Eighteen years of dormancy. The defective length/copy split has been in ngx_http_rewrite_module since roughly 2008 (NGINX 0.6.27), making every release up to and including 1.30.0 vulnerable. It survived this long because the trigger requires a precise combination (unnamed capture + ? in replacement + a following rewrite/if/set) that is common in production but rarely hit by fuzzers pointed at default configs.

Vulnerable Configuration Pattern

The bug only manifests when the configuration matches the trigger shape. A minimal vulnerable block:

# VULNERABLE: unnamed capture ($1) + '?' in the replacement +
# a following 'set' directive that references the rewrite phase.
location ~ ^/api/(.*)$ {
    rewrite ^/api/(.*)$ /internal?migrated=true;   # '?' sets is_args
    set $original_endpoint $1;                      # following directive
    proxy_pass http://backend;
}
# SAFE: named capture takes a different, correctly-sized code path.
location ~ ^/api/(?<rest>.*)$ {
    rewrite ^/api/(?<rest>.*)$ /internal?migrated=true;
    set $original_endpoint $rest;
    proxy_pass http://backend;
}

Attack Vector

The end-to-end path is trivial: find a vulnerable virtual host, send one request whose path lands in the captured group and is padded with characters that expand under query-string escaping. The snippet below is illustrative only and intentionally non-weaponised — it shows the shape defenders should recognise, not a working exploit.

# Step 1: Fingerprint NGINX and a version that is potentially in range.
# (Server header is often present; absence does not mean "safe".)
curl -sI https://target.example.com/ | grep -i '^server:'
# Server: nginx/1.28.0   <-- in the 0.6.27 - 1.30.0 vulnerable range

# Step 2: Identify a route that hits a 'rewrite' with an unnamed
# capture. Path-segment rewrites that preserve the tail are the
# typical candidates (legacy migration, API path stripping, etc.).

# Step 3: Send ONE crafted request. The captured group is padded
# with characters (+ % &) that expand 1 -> 3 bytes during the
# query-string re-escape, so the write overruns the buffer that
# was sized for the *unescaped* length.
#
# *** ILLUSTRATIVE ONLY - NOT A WORKING EXPLOIT ***
PAD=$(python3 -c 'print("%2B" "+++%%%&&&" * 512)')
curl -sk "https://target.example.com/api/${PAD}"
# Practical result for almost every vulnerable config:
#   -> NGINX worker process heap overflow -> crash -> restart (DoS)
# Where ASLR is disabled on the host:
#   -> attacker-shaped heap corruption -> potential RCE

A simplified view of the size mismatch:

                 ngx_http_rewrite_module
                 ------------------------

  LENGTH PASS (fresh sub-engine, is_args = 0)
     capture "$1" measured RAW/unescaped   ->  len = N
     malloc(N)  ............................  buffer too small
                                                     |
                                                     v
  COPY PASS (main engine, is_args = 1)
     '?' in replacement made is_args sticky
     ngx_escape_uri(..., NGX_ESCAPE_ARGS)
        '+'  -> "%2B"   (1 byte  -> 3 bytes)
        '%'  -> "%25"   (1 byte  -> 3 bytes)
        '&'  -> "%26"   (1 byte  -> 3 bytes)
     writes M bytes, where M  >>  N
                                                     |
                                                     v
   +------------------ heap chunk (N) ------------+##########
   |  written URI ................................|  OVERFLOW
   +----------------------------------------------+##########
                                                  ^
                              attacker-controlled bytes from URI

Exploitation in the Wild

  • 2026-05-13 — depthfirst publicly discloses the bug and publishes a working PoC to GitHub. NVD entry goes live; AlmaLinux/Debian/Ubuntu begin shipping patched packages.
  • 2026-05-14 — F5 publishes advisory K000161019 and patched NGINX Open Source / Plus builds.
  • 2026-05-16VulnCheck’s canary/honeypot systems flag exploitation attempts — only three days after the PoC dropped.
  • 2026-05-18 — VulnCheck’s Patrick Garrity publicly confirms active exploitation; reporting estimates ~5.7 million internet-exposed NGINX servers on potentially vulnerable versions.

The realistic threat today is mass, opportunistic DoS: the crash primitive is reliable and the PoC is public, so spraying crafted requests at NGINX fleets to knock workers over is cheap and repeatable. The RCE path is real but conditional — the public PoC achieved code execution against a deliberately vulnerable, ASLR-disabled target and does not demonstrate reliable execution against hardened, ASLR-enabled production hosts. Treat “DoS now, RCE where you were sloppy about hardening” as the working model.

Post-Exploitation Impact

  1. Service outage at the edge. NGINX usually is the front door — crashing workers means dropped TLS, dropped APIs, and dropped ingress for everything behind it.
  2. Repeatable, low-cost denial of service. One request per crash, no auth, no session — trivially scriptable against many vhosts at once.
  3. Ingress-wide blast radius in Kubernetes. Taking down NGINX Ingress Controller pods can sever access to every service routed through them.
  4. Code execution where ASLR is off. Embedded appliances, minimal containers, and misconfigured hosts with ASLR disabled are the realistic RCE population — and on those, the worker often runs with enough privilege to pivot.
  5. Heap-grooming research will improve. An 18-year-old, attacker-shaped overflow with a public PoC tends to attract follow-on exploitation engineering; the RCE reliability bar may drop over time.

💥 Impact Assessment

Immediate Impact

  • Internet-scale exposure: ~5.7M internet-facing NGINX servers on a potentially vulnerable build define the upper-bound population.
  • Pre-auth, single request, no user interaction: the cheapest possible trigger conditions.
  • Public PoC + confirmed in-the-wild activity within 3 days: the window for “patch before it’s exploited” has already closed — assume it is being sprayed.
  • Reliable DoS for nearly all vulnerable configs: this is not a theoretical edge case; the crash is the common case.
  • Conditional RCE: ASLR-disabled hosts (appliances, stripped containers) are the meaningful code-execution risk.

Affected Versions

ComponentVulnerable RangeFixed In
NGINX Open Source0.6.27 through 1.30.01.30.1, 1.31.0
NGINX PlusR32 through R36R32 P6, R36 P4, 37.0.0
NGINX Open Source 0.6.27–0.9.7VulnerableNo fix planned (EOL)
NGINX Ingress Controller / F5 WAF for NGINX / Instance Manager / Gateway Fabric / App Protect DoSBuilds bundling a vulnerable engineVendor-updated builds — see K000161019

Check the actual running build, not the package name. Many distros backport; nginx -v plus your distro’s changelog is the source of truth. A “1.28.0” that has the distro patch applied is fine; an unpatched 1.30.0 is not.

Affected Environments

  • Internet-facing reverse proxies and API gateways with path-rewriting rules.
  • Kubernetes clusters running NGINX Ingress Controller with rewrite-target annotations.
  • Multi-tenant / templated hosting where one risky rewrite snippet is rendered across many vhosts.
  • CDN / WAF edge tiers built on NGINX.
  • Appliances and embedded devices that bundle NGINX — disproportionately likely to ship with ASLR disabled, i.e. the RCE-relevant population.

Attacker Profiles

  • Opportunistic disruptors / botnets: a reliable, scriptable, unauthenticated crash against the most common web server is ideal mass-DoS material.
  • Hacktivists: high-visibility outages against recognisable targets with a one-line trigger.
  • Initial-access actors (selective): focused RCE attempts against ASLR-disabled appliance fleets.
  • Extortion / “booter” services: monetising a no-auth crash primitive at scale.
  • Red teams / authorized testers: a clean availability-impact test case for any NGINX-fronted scope this month.

🛡️ Mitigation Strategies

Immediate Actions (Priority 1)

  1. Inventory and version-check every NGINX instance, including the ones bundled inside appliances, containers, and ingress controllers.
    # Direct hosts
    nginx -v 2>&1                       # e.g. nginx version: nginx/1.30.0
    
    # Containers / Kubernetes ingress
    kubectl get pods -A -o jsonpath='{range .items[*]}{.spec.containers[*].image}{"\n"}{end}' \
      | grep -i nginx | sort -u
    
    # Fleet sweep over the Server header (indicative, not authoritative)
    for h in $(cat hosts.txt); do
      printf '%s ' "$h"; curl -sI "https://$h/" | awk -F': ' 'tolower($1)=="server"{print $2}'
    done
  2. Patch to a fixed build. NGINX Open Source 1.30.1 / 1.31.0; NGINX Plus R32 P6 / R36 P4 / 37.0.0; or your distro’s patched package (AlmaLinux/Debian/Ubuntu have shipped them).
    # Debian/Ubuntu
    sudo apt update && sudo apt install --only-upgrade nginx
    # RHEL/AlmaLinux
    sudo dnf upgrade nginx
    # Reload after upgrade (graceful, keeps connections)
    sudo nginx -t && sudo systemctl reload nginx
  3. Apply the official workaround until you can patch: convert unnamed captures to named captures in any rewrite directive whose replacement contains ? and is followed by rewrite/if/set.
    # Before (vulnerable)
    rewrite ^/api/(.*)$ /internal?migrated=true;
    set $original_endpoint $1;
    
    # After (safe) -- named capture avoids the mismatched code path
    rewrite ^/api/(?<rest>.*)$ /internal?migrated=true;
    set $original_endpoint $rest;
  4. Audit configs for the trigger pattern so you know your real exposure rather than guessing.
    # Flag rewrite directives that use an unnamed capture AND a '?'
    grep -RInE 'rewrite\s+\S+\s+\S*\?\S*' /etc/nginx/ | grep -E '\$[0-9]'
    # Then manually confirm a following rewrite/if/set in the same block.
  5. Confirm ASLR is enabled everywhere NGINX runs — this is the single control that keeps the bug at “DoS” instead of “RCE”.
    cat /proc/sys/kernel/randomize_va_space   # must be 2 (full ASLR)
    # If 0/1: set kernel.randomize_va_space=2 in /etc/sysctl.d and apply.

Detection Measures

# (a) Worker crash/restart signal -- the reliable DoS fingerprint.
journalctl -u nginx --since "2026-05-13" | \
  grep -Ei 'worker process .* exited on signal|SIGSEGV|signal 11|.*core dumped'
grep -Ei 'worker process [0-9]+ exited on signal (6|11)' /var/log/nginx/error.log

# (b) Inbound trigger shape -- heavily query-escapable padding in the
#     captured path segment (lots of +, %, & or %2B/%25/%26 runs).
grep -E '"[A-Z]+ /[^ ]*(%2[B5]|%26|[+%&]){20,}' /var/log/nginx/access.log

# (c) Crash storms -- many worker restarts in a short window from the
#     same source are an exploitation/DoS indicator, not noise.
grep -c 'exited on signal' /var/log/nginx/error.log
title: NGINX Rift (CVE-2026-42945) Trigger Pattern / Worker Crash
status: experimental
logsource:
  product: nginx
detection:
  uri_pattern:
    cs-uri-stem|re: '(%2[Bb5]|%26|[+%&]){20,}'
  worker_crash|re: 'worker process [0-9]+ exited on signal (6|11)'
  condition: uri_pattern or worker_crash
level: high
tags:
  - cve.2026.42945
  - attack.impact
  - attack.t1499        # Endpoint Denial of Service
  - attack.t1190        # Exploit Public-Facing Application

Long-term Security Improvements

  1. Treat the reverse proxy as Tier-0. The thing that terminates all your TLS and fronts all your apps deserves the same patch SLA and monitoring discipline as a domain controller.
  2. Pin and track the running NGINX build, not just the package name — backports make Server: headers and version strings lie. Maintain an SBOM that includes bundled engines inside appliances and ingress controllers.
  3. Prefer named captures by policy. Lint your NGINX configs in CI: ban unnamed-capture rewrites that also contain ?. This bug is fixed, but the readability/safety argument for named captures is permanent.
  4. Keep ASLR mandatory. A surprising number of appliances and minimal containers ship with it weakened. ASLR is the difference between this CVE being an availability nuisance and a remote shell.
  5. Rate-limit and bound request size at the edge. limit_req, large_client_header_buffers, and upstream WAF rules raise the cost of crash-spraying even before a patch lands.
  6. Aggressive advisory SLAs for edge software. Public PoC plus confirmed in-the-wild within 72 hours (as happened here) means a 7-day patch window is too slow for internet-facing NGINX — target 24-72h.

⚠️ Why is this Critical?

  1. The most-deployed web server on earth — a third of the web sits behind NGINX, and the bug is in one of its most-used modules.
  2. Pre-auth, single crafted request, no user interaction — minimal attacker prerequisites.
  3. Public PoC the day of disclosure, in-the-wild activity within three days — the patch-before-exploited window is already gone.
  4. Reliable DoS is the common case, not the edge case — nearly every vulnerable config can be crashed on demand.
  5. Conditional RCE on ASLR-disabled hosts — appliances and stripped containers are exactly where ASLR is most often off.
  6. 18 years dormant — enormous installed base of long-lived, rarely-touched NGINX instances that nobody is watching.
  7. Templated config amplifies blast radius — one risky rewrite snippet rendered across thousands of vhosts is one mistake, thousands of targets.

🗓️ Timeline and Disclosure

  • ~2008 — The length/copy escaping mismatch is introduced in ngx_http_rewrite_module (NGINX 0.6.27).
  • 2026-04-21 — depthfirst reports the vulnerability to F5 under coordinated disclosure.
  • 2026-05-13 — Public disclosure; depthfirst publishes a working PoC to GitHub; NVD entry live; AlmaLinux/Debian/Ubuntu ship patched packages.
  • 2026-05-14 — F5 publishes advisory K000161019 and patched NGINX Open Source / Plus builds.
  • 2026-05-16 — VulnCheck canary systems flag the first in-the-wild exploitation attempts.
  • 2026-05-18 — VulnCheck publicly confirms active exploitation; ~5.7M exposed servers reported.

🔗 Resources and References

🤝 SEKurity Supports You

A buffer overflow that hid in the world’s most popular web server for eighteen years — and went from “PoC published” to “exploited in the wild” in three days — is a clean illustration of why edge software is Tier-0 and why “we run a well-known, mature project” is not a security control. We help organizations measure their real exposure to this class of bug: enumerating every NGINX build that is actually running (including the ones quietly bundled inside appliances and ingress controllers), auditing rewrite configurations for the dangerous pattern, confirming ASLR is genuinely enabled on the hosts that matter, and stress-testing whether your detection content would actually catch a crash-spray or a targeted RCE attempt before your customers notice the outage.

Our Services

  • Penetration Testing: Web applications, mobile apps (Android & iOS), SAP systems, Active Directory
  • Large-Scale Attacks: Perimeter testing, IT infrastructure testing, Red Team engagements
  • Security Awareness: Phishing campaigns, hacking demonstrations

Act now — before attackers do.


Contact:

Website: www.sekurity.de

Inquiries: www.sekurity.de/kontakt

LinkedIn: SEKurity GmbH


Your SEKurity Team — Your Trusted Adversaries

The security of your web edge is our drive.


Sources

About the Author

SEKurity Team

Offensive Security Experts

The SEKurity GmbH team consists of experienced penetration testers, security researchers, and cybersecurity consultants. Under the motto 'Your Trusted Adversaries', we support organizations in evaluating their IT security from an attacker's perspective and improving it.

Related Articles