News

The nginx Kill Chain — Three CVEs, One Root Shell

Muhammad SaadMay 26, 202617 min read
The nginx Kill Chain — Three CVEs, One Root Shell

A Single HTTP Request to Root

Nginx powers roughly 34% of the world's web servers. Linux runs on more than 96% of them. In May 2026, three separately-disclosed CVEs — from three different researchers, targeting two different software stacks — quietly assembled into something far worse than the sum of their parts: a complete, forensically silent kill chain from the public internet to a root shell on any Linux server running nginx.

The path is: crafted HTTP request → nginx worker process compromise → kernel page cache manipulation → root. No authentication. No disk writes. No forensic evidence. Seconds from start to finish.

This is not a theoretical exercise. Working proof-of-concept exploits exist for every step of the chain. A public GitHub repository hosts the nginx exploit. The kernel privilege escalation fits in 732 bytes of Python. And the attacker gets two independent paths to root from the nginx worker — patching one doesn't close the other.

This article walks through every step of the chain, explains the underlying mechanisms, and tells you exactly what you need to do to close these gaps before someone else walks through them.


Step 1: CVE-2026-42945 — "Nginx Rift" (Initial Access)

CVE CVE-2026-42945
Name Nginx Rift
CVSS v4.0 9.2 (Critical)
Disclosed May 13, 2026
Type Heap buffer overflow (CWE-122)
Component ngx_http_rewrite_module (src/http/ngx_http_script.c)
Affected Versions NGINX 0.6.27 through 1.30.0, NGINX Plus R32–R36, all downstream builds
Age 18 years undetected (introduced 2008)
Fixed In NGINX 1.30.1 (stable), 1.31.0 (mainline)

The Bug: A Two-Pass Contract Violation

Nginx's script engine uses a two-pass process for rewrite directives. In the first pass, it computes the required buffer size for the rewritten URI. In the second pass, it copies the data into that buffer. The assumption — held for 18 years — was that both passes would compute the same length. They don't.

The problem is an internal state flag called is_args. When a rewrite rule's replacement string contains a question mark (?), the main script engine sets is_args = 1. But the length-calculation pass runs on a freshly zeroed sub-engine where is_args = 0. This matters because of what happens next.

When is_args = 0 (the length pass), the engine returns the raw length of the captured substring — say, 10 bytes. When is_args = 1 (the copy pass), the engine calls ngx_escape_uri() with NGX_ESCAPE_ARGS, which percent-encodes characters like +, %, and &. Each escapable byte expands from 1 byte to 3 bytes. The buffer was allocated for 10 bytes. The copy writes 30.

The triggering configuration pattern is any rewrite rule that combines an unnamed PCRE capture ($1, $2, etc.) with a question mark in the replacement, followed by another rewrite, if, or set directive. For example:

location /api {
    rewrite ^/api/(.*) /internal?migrated=true;
    set $dummy "value";
}

This pattern is common in legacy configurations that redirect API endpoints or manage query string migrations. It has existed, vulnerable and undetected, across the entire history of nginx deployments.

From Heap Overflow to Code Execution

A heap buffer overflow on its own does not automatically grant code execution. Nginx Rift's exploit uses a technique called cross-request heap feng shui to turn the overflow into reliable RCE.

The exploit works in four stages:

  1. Connection 1 (setup): The attacker opens a connection with partial HTTP headers, causing nginx to allocate a request pool (ngx_pool_t) on the heap.
  2. Connection 2 (victim): A second connection is opened, allocating a victim pool adjacent to the first one in memory. POST body spraying is used to place controlled data in predictable heap locations, because URI bytes cannot contain null bytes but POST body data can.
  3. Trigger: The attacker completes the first connection's headers with a crafted URI containing escapable characters. The rewrite engine overflows the undersized buffer and corrupts the adjacent victim pool's cleanup pointer.
  4. Execution: The attacker immediately closes the victim connection. Nginx calls ngx_destroy_pool(), which walks the cleanup chain. The corrupted pointer now points to a fake ngx_pool_cleanup_s structure (sprayed via POST bodies) whose handler field points to system().

The result: unauthenticated remote code execution in the nginx worker process, triggered by a single crafted HTTP request. No credentials required. No session needed. The attacker now has a shell running as the nginx user — typically www-data or nginx.

Companion CVEs Disclosed the Same Day

Nginx Rift was not the only vulnerability disclosed on May 13, 2026. Three additional CVEs were published alongside it, all discovered by the same autonomous AI analysis system (depthfirst):

  • CVE-2026-42946 (CVSS 8.3) — Excessive memory allocation in ngx_http_scgi_module and ngx_http_uwsgi_module, exploitable via adversary-in-the-middle
  • CVE-2026-40701 (CVSS 6.3) — Use-after-free in the SSL module when ssl_verify_client and ssl_ocsp are enabled
  • CVE-2026-42934 (CVSS 6.3) — Out-of-bounds read in ngx_http_charset_module

All four vulnerabilities were reported to nginx via a GitHub security advisory on April 21, 2026, with coordinated public disclosure on May 13. All are patched in nginx 1.30.1 and 1.31.0.


Step 2: CVE-2026-31431 — "Copy Fail" (Privilege Escalation, Path A)

CVE CVE-2026-31431
Name Copy Fail
CVSS 7.8 (High)
Disclosed April 29, 2026
Type Logic bug — controlled page cache write
Component algif_aead (kernel crypto API, AF_ALG socket interface)
Affected Linux kernel 4.14+ (every mainstream distribution since 2017)
Fix Mainline commit a664bf3d603d (reverts the 2017 optimization)

The Bug: Nine Years of Shared Pages

In 2017, a performance optimization was added to algif_aead.c (commit 72548b093ee3) that allowed AEAD cryptographic operations to be performed in-place. The optimization copied AAD and ciphertext data from the source scatterlist into the destination buffer, but chained the authentication tag pages by reference rather than copying them.

This created a problem: when splice() hands page-cache pages into the crypto subsystem, those pages end up in the writable destination scatterlist. The authencesn template's write at dst[assoclen + cryptlen] walks into the chained tag pages — and writes directly into the kernel's file page cache.

The result is a deterministic, controlled 4-byte write into the page cache of any readable file on the system.

732 Bytes to Root

The exploit is remarkable for its simplicity and reliability:

  • 732 bytes of Python — that is the entire exploit
  • No race conditions — the bug is a logic flaw, not a timing window
  • No kernel offsets needed — works without knowing the exact kernel version or configuration
  • No disk writes — the modification happens in the page cache (memory), not on disk
  • Cross-distribution — works on Ubuntu 24.04, Amazon Linux 2023, RHEL 10.1, and SUSE 16 without modification

The default exploit path targets /usr/bin/su, a setuid-root binary present on virtually every Linux installation. The four-syscall sequence is:

  1. Open an AF_ALG socket for the authencesn algorithm
  2. Use splice() to feed page-cache pages from /usr/bin/su into the crypto subsystem
  3. The controlled 4-byte write modifies the in-memory copy of the binary
  4. Execute the now-modified setuid binary — root shell

Because the write targets the page cache, there is no forensic residue on disk. The original binary on disk is untouched. A reboot clears the page cache and restores the original binary, erasing the last trace of the exploit. If the attacker wants persistence, they write it to disk after gaining root. If they want stealth, they don't.

Container Escape

Because the page cache is shared between containers and the host kernel, Copy Fail also enables container escape scenarios. A process inside a container can modify the page-cached copy of a setuid binary that the host or another container subsequently executes. This makes the vulnerability especially dangerous in shared hosting environments where multiple tenants share a kernel.


Step 3: CVE-2026-43284 + CVE-2026-43500 — "Dirty Frag" (Privilege Escalation, Path B)

CVEs CVE-2026-43284 (CVSS 8.8), CVE-2026-43500 (CVSS 7.8)
Name Dirty Frag
Disclosed May 7, 2026
Researcher Hyunwoo Kim
Type Page cache corruption via IPsec/RxRPC decryption
Components esp4/esp6 (IPsec ESP) and rxrpc (AFS)
Affected ESP: since January 2017. RxRPC: since June 2023. All major distributions.
PoC Published same day as disclosure — before any distro shipped patches

The Bug: Decryption Over Borrowed Pages

Dirty Frag is conceptually similar to Copy Fail but exploits a different subsystem. The flaw sits in the in-place decryption path of the ESP (Encapsulating Security Payload) and RxRPC modules. When the receive path decrypts data over paged buffers that are not privately owned by the kernel, unprivileged processes can retain references to the resulting plaintext — yielding a write primitive into the page cache.

The vulnerability consists of two bugs:

  • CVE-2026-43284 — The xfrm-ESP page-cache write vulnerability, present in esp4 and esp6 modules since January 2017
  • CVE-2026-43500 — The RxRPC page-cache write vulnerability, introduced in June 2023

Both bugs exploit the same fundamental mistake: the kernel performs in-place cryptographic operations on pages it does not exclusively own, allowing unprivileged userspace to modify file contents through the page cache.

Why Dirty Frag Matters: The Second Path

This is the critical point that makes the kill chain so dangerous. Dirty Frag is not the same vulnerability as Copy Fail. It exploits different kernel modules (esp4/esp6/rxrpc instead of algif_aead). It was disclosed by a different researcher. It uses a different exploit mechanism.

This means the attacker has two completely independent paths to root from an unprivileged shell:

  • Path A (Copy Fail): algif_aead → page cache write → root
  • Path B (Dirty Frag): esp4/esp6/rxrpc → page cache write → root

If a sysadmin patches algif_aead or disables the module, the attacker pivots to Dirty Frag. If they patch the ESP modules, the attacker uses Copy Fail. Both paths must be closed to stop the privilege escalation.

The exploit relies only on standard syscalls and modules that are fully enabled in the default kernel packages of every major enterprise distribution. No special configuration is required. The PoC was published on the same day as disclosure — May 7, 2026 — before any distribution had shipped patches.


The Complete Kill Chain

Here is the full chain, step by step, from the public internet to a root shell:

Phase 1: Initial Access (CVE-2026-42945)

  1. The attacker identifies a target running nginx with a rewrite rule containing a question mark in the replacement string. This pattern is common in production configurations.
  2. The attacker sends a single crafted HTTP request to the server. No authentication, no session, no prior access of any kind.
  3. The request triggers the heap buffer overflow in ngx_http_rewrite_module.
  4. Cross-request heap feng shui corrupts the adjacent ngx_pool_t cleanup pointer.
  5. On pool destruction, the corrupted cleanup chain invokes system() with attacker-controlled arguments.
  6. Result: The attacker has code execution as the nginx worker process user (typically www-data or nginx).

Phase 2: Privilege Escalation (CVE-2026-31431 or CVE-2026-43284/43500)

  1. From the nginx worker shell, the attacker runs the Copy Fail exploit — 732 bytes of Python. The exploit modifies the page-cached copy of /usr/bin/su via algif_aead and splice().
  2. OR, if algif_aead is patched or disabled, the attacker runs the Dirty Frag exploit instead, using esp4/esp6 or rxrpc to achieve the same page cache write.
  3. The attacker executes the modified setuid binary.
  4. Result: Root shell.

Timing and Forensics

Aspect Detail
Total time Seconds
Authentication required None
Disk writes Zero (page cache only)
Log entries One HTTP access log line (normal-looking request)
Forensic artifacts None on disk — page cache clears on reboot
Detection difficulty Extremely high without runtime monitoring
Persistence Attacker's choice — persist after root, or stay silent

The entire attack produces a single HTTP access log entry that looks like an ordinary request. The kernel exploits operate entirely in memory. There is nothing to find on disk. Nothing in syslog. Nothing in auth.log. By the time you know something is wrong, the attacker has root and has decided whether or not you'll ever find out.


Why This Chain Matters for Hosting

Each of these vulnerabilities is serious individually. Together, they create a systemic threat to internet infrastructure. Here is why:

Market Share Creates Attack Surface

Nginx serves approximately one-third of all websites on the internet. Linux runs on more than 96% of servers. The intersection of "runs nginx" and "runs Linux" covers an enormous fraction of the world's web infrastructure. Every server in that intersection is potentially vulnerable to this complete chain.

Shared Hosting Amplifies Impact

In shared hosting environments, a single nginx instance serves hundreds or thousands of websites. A single exploit of this chain compromises not one site but all of them. The attacker lands on the nginx worker, escalates to root, and now controls every site on the server — every database connection string, every SSL private key, every customer's data.

Cloud Infrastructure Puts Nginx at the Edge

Nginx is the most common reverse proxy and load balancer in cloud architectures. It sits at the outermost layer of the network stack, directly exposed to the public internet. This is exactly where the chain begins: with an unauthenticated HTTP request. The most exposed component is the vulnerable one.

Container Hosts Are Not Safe

Containers share the host kernel. Both Copy Fail and Dirty Frag exploit kernel-level page cache mechanisms. An attacker who gains code execution inside a containerized nginx instance can use either kernel exploit to escape the container and compromise the host. Container isolation limits the blast radius of application-level bugs, but kernel bugs route around it.

Forensic Silence Is the Real Danger

Traditional intrusion detection relies on artifacts: unusual log entries, modified files, new processes. This chain produces none of those. The HTTP request looks normal. The kernel exploit leaves no disk trace. The only detection opportunity is runtime behavioral monitoring of the nginx worker process — watching for unexpected child process spawning or unusual system calls. Most hosting environments do not have this level of monitoring in place.


What You Need To Do Right Now

This is not a "schedule it for the next maintenance window" situation. The nginx exploit is public. The kernel exploits are public. Active exploitation of CVE-2026-42945 has already been observed in the wild. Here is the priority-ordered checklist:

1. Patch Nginx Immediately

Update to nginx 1.30.1 (stable) or 1.31.0 (mainline). This closes the initial access vector — without it, the kill chain cannot begin.

# Debian/Ubuntu
sudo apt update && sudo apt install nginx

# RHEL/CentOS/AlmaLinux
sudo dnf update nginx

# From source
wget https://nginx.org/download/nginx-1.30.1.tar.gz
# ... build and install

Verify your version after patching:

nginx -v
# Should show: nginx version: nginx/1.30.1 or higher

2. Update Your Linux Kernel

Apply kernel updates that include fixes for both Copy Fail (a664bf3d603d) and the Dirty Frag pair. Both paths must be closed.

# Debian/Ubuntu
sudo apt update && sudo apt install linux-image-generic && sudo reboot

# RHEL/CentOS/AlmaLinux
sudo dnf update kernel && sudo reboot

# Check currently running kernel after reboot
uname -r

3. If You Cannot Patch the Kernel Immediately, Disable Vulnerable Modules

This is a stopgap, not a fix. But it closes the privilege escalation paths until you can apply kernel patches.

Block Copy Fail (algif_aead):

echo "install algif_aead /bin/false" | sudo tee /etc/modprobe.d/disable-algif-aead.conf
sudo modprobe -r algif_aead 2>/dev/null

Block Dirty Frag (esp4, esp6, rxrpc):

echo "install esp4 /bin/false" | sudo tee /etc/modprobe.d/disable-esp4.conf
echo "install esp6 /bin/false" | sudo tee /etc/modprobe.d/disable-esp6.conf
echo "install rxrpc /bin/false" | sudo tee /etc/modprobe.d/disable-rxrpc.conf
sudo modprobe -r esp4 esp6 rxrpc 2>/dev/null

Important: Disabling esp4/esp6 will break IPsec VPN connections. Disabling rxrpc will break AFS filesystem access. Assess your dependencies before applying these mitigations.

4. Audit Your Nginx Configurations

Search for the triggering pattern: rewrite rules with unnamed PCRE captures and question marks in the replacement string.

# Find potentially vulnerable configurations
grep -rn 'rewrite.*\$[0-9].*?' /etc/nginx/
grep -rn 'rewrite.*(.*).*?' /etc/nginx/

Any configuration matching this pattern is a potential trigger for CVE-2026-42945. Patching nginx is the correct fix, but identifying vulnerable configurations helps prioritize which servers to patch first.

5. Deploy WAF Rules

If you run a web application firewall in front of nginx, deploy rules to detect and block requests containing heavy concentrations of escapable characters (+, %, &) in URI paths that target rewrite-eligible locations. This is defense in depth — it does not replace patching.

6. Monitor Nginx Worker Processes

The exploit produces code execution as the nginx worker user. Legitimate nginx workers do not spawn child processes, open outbound network connections, or execute Python scripts. Monitor for:

  • Child processes spawned by nginx worker PIDs
  • Unexpected outbound connections from the nginx user
  • Execution of interpreters (python, perl, bash) by the nginx user
  • AF_ALG socket creation by non-root, non-crypto processes
  • Unusual splice() patterns targeting setuid binaries
# Quick check: are any nginx workers running unexpected child processes?
for pid in $(pgrep -f "nginx: worker"); do
    children=$(ps --ppid "$pid" -o pid,comm --no-headers 2>/dev/null)
    if [ -n "$children" ]; then
        echo "WARNING: nginx worker $pid has children: $children"
    fi
done

7. Use Container Isolation

Containers do not prevent the kernel exploits (since they share the host kernel), but they do limit what the attacker can reach before escalating privileges. Running nginx inside a container with a minimal filesystem, no setuid binaries, and restricted capabilities reduces the attack surface available to the kernel exploit.

# Drop all capabilities and remove setuid binaries in container
# In your Dockerfile:
RUN find / -perm /4000 -type f -exec chmod u-s {} \;

# In your docker-compose.yml:
security_opt:
  - no-new-privileges:true
cap_drop:
  - ALL

This is mitigation, not prevention. Patch the kernel.


Disclosure Timeline

Date Event
April 21, 2026 Depthfirst reports CVE-2026-42945 and companion CVEs to nginx via GitHub security advisory
April 29, 2026 CVE-2026-31431 (Copy Fail) publicly disclosed; working PoC published
May 1, 2026 First vendor kernel patches begin shipping for Copy Fail
May 7, 2026 CVE-2026-43284 and CVE-2026-43500 (Dirty Frag) disclosed by Hyunwoo Kim; PoC published same day
May 13, 2026 CVE-2026-42945 (Nginx Rift) publicly disclosed; nginx 1.30.1 and 1.31.0 released
May 18, 2026 Active exploitation of CVE-2026-42945 observed in the wild

Three CVEs, three different researchers, three weeks of disclosure, two software stacks. The window between "all three pieces are public" and "patches are widely deployed" is exactly the window an attacker needs.


The Uncomfortable Math

Let's be direct about the numbers:

  • Nginx serves approximately 34% of all websites
  • Linux powers more than 96% of servers
  • The nginx vulnerability existed for 18 years before detection
  • The Copy Fail kernel bug existed for 9 years
  • The Dirty Frag ESP bug existed for 9 years
  • The entire exploit chain requires zero authentication
  • The entire exploit chain produces zero disk forensic artifacts
  • The attacker gets two independent paths to root from the nginx worker

These are not exotic, difficult-to-exploit vulnerabilities. The nginx exploit has a public GitHub repository. The kernel exploit is 732 bytes of Python. Active exploitation has been confirmed. The question is not whether this chain will be used in the wild. It's whether you will have patched before it reaches your servers.

Patch nginx. Patch the kernel. Disable the vulnerable modules if you can't patch immediately. Do it today.

Share this article

Muhammad Saad

Muhammad Saad

DeployBase Team

Explore DeployBase

Related features and resources for this topic

Ready to Get Started?

Join thousands of developers who trust DeployBase for their hosting needs.