Someone left the loading-dock gate cracked open, and a locksmith just published the master key on YouTube
CVE-2026-9256, publicly nicknamed nginx-poolslip, is a heap buffer overflow (CWE-122) in ngx_http_rewrite_module affecting NGINX Open Source 0.1.17 through 1.31.0 and NGINX Plus R32 through R36 plus 37.0.0. The flaw fires when a rewrite directive uses a regex pattern containing overlapping PCRE capture groups — the canonical example is ^/((.*))$ paired with a replacement string referencing both $1 and $2 in a redirect or arguments context. NGINX miscalculates the output buffer length after URI escaping, producing an out-of-bounds write into the worker process memory pool. An unauthenticated attacker over plain HTTP can crash the worker (DoS) or, with the public PoolSlip × Rift chain (exp_official.py), achieve reliable ASLR-independent remote code execution by partial-overwriting cleanup pointers — no address leak needed. Patches landed 2026-05-22 in NGINX OSS 1.30.2 / 1.31.1 and NGINX Plus R36 P5, R32 P7, R37.0.1.1.
F5's vendor score of 8.1 HIGH (CVSS:3.1) reflects AC:H — attack complexity is high because the specific overlapping-capture rewrite pattern is required. That was a fair call *before* the ASLR-bypass PoC dropped in July 2026. Now that exp_official.py demonstrates ASLR-independent RCE against a hardened Linux server using only ~300 HTTP requests, the practical attack complexity for code execution has dropped materially. The CVSS v4.0 re-score to 9.2 Critical acknowledges this. noisgate upgrades to 8.6 HIGH — the configuration prerequisite is real friction that prevents CRITICAL, but the demonstrated RCE chain, active exploitation, and NGINX's canonical role at the network edge push the score above the vendor baseline.
6 steps from start to impact.
Identify internet-facing NGINX with vulnerable version
- Target NGINX exposes version in Server header or error pages
- Many hardened deployments strip
server_tokens; NGINX Plus by default still discloses major.minor
server_tokens off in nginx.conf reduces exposureProbe for exploitable rewrite configuration
- NGINX config contains
rewritewith overlapping PCRE captures referencing$1$2in redirect or args context
- Not a default config — requires the specific nested-capture pattern; many sites use
return 301ortry_filesinstead of regex rewrites - Configs using named captures (
(?<name>...)) are NOT vulnerable
Trigger heap overflow for DoS
curl, python-requests).- Vulnerable rewrite rule is reachable
- Worker restarts are fast; sustained DoS requires continuous request stream
- Rate limiting or connection limits can slow the attacker
error.log for signal 11 (SIGSEGV) and worker process exited on signal 11; correlate with 502 spikesHeap leak via PoolSlip info-disclosure primitive
exp_official.py (y198nt / DepthFirstDisclosures).- Worker has not yet crashed from the overflow (controlled overwrite)
- Chaining with CVE-2026-42945 (NGINX Rift) — target must also be unpatched for that CVE
- If target patched CVE-2026-42945 (1.30.1 / 1.31.0) but not CVE-2026-9256, the ASLR-bypass chain breaks — DoS/blind overflow only
- Precise heap grooming is fragile across different allocator configurations (glibc version, jemalloc, etc.)
ASLR-independent RCE via partial pointer overwrite
limit_conn cleanup-handler pointer, leaving the ASLR-randomized high bytes intact. This makes the technique ASLR-independent — no full address brute-force required. The corrupted pointer redirects control flow to attacker-controlled heap data. Approximately 8 upload requests plus 16 connection-setup requests groom the heap, then 1 final request triggers the overwrite. The attacker gains code execution as the NGINX worker user (nginx / www-data). Tool: exp_official.py published on GitHub with Docker lab environment.- Both CVE-2026-9256 and CVE-2026-42945 unpatched
- Heap grooming succeeds for the target's glibc version (demonstrated on glibc-2.41)
- Worker runs as unprivileged user — post-exploitation requires local privilege escalation
- Heap layout varies across NGINX compile options, modules loaded, and OS allocator
- Exploit reliability is high but not 100% — occasional worker crash instead of clean RCE
Post-exploitation: pivot from edge proxy
- RCE achieved on NGINX worker
- NGINX is deployed as a reverse proxy or ingress controller (canonical deployment)
- mTLS between proxy and backends limits credential theft
- Network segmentation and zero-trust policies restrict lateral movement
- Kubernetes RBAC limits Ingress Controller service account privileges
^/((.*))$ with ^/(?<path>.*)$ and reference $path instead of $1$2. This eliminates the vulnerable code path without upgrading NGINX. Audit your full config with nginx -T | grep -nE 'rewrite.*\$[0-9]+.*\$[0-9]+' or the nginx_poolslip_config_triage.py script from Penligent. Apply within 30 days if patching is delayed. Test redirects thoroughly after changes.cat /proc/sys/kernel/randomize_va_space — value must be 2 (full randomization). ASLR does NOT prevent the PoolSlip × Rift partial-overwrite chain, but it blocks older/simpler exploitation paths. If ASLR is disabled (value 0), the heap overflow becomes trivially exploitable for RCE without any chaining. This is a baseline hygiene check, not a standalone fix./foo/foo patterns or excessively long path components) targeting locations known to have rewrite rules. This raises the bar for automated scanning but is bypassable by a determined attacker who can craft payloads that evade pattern matching. Deploy within 30 days.- Rate limiting alone — the full RCE exploit requires only ~300 requests total, well within normal traffic volumes for a production NGINX instance. Rate limiting may slow crash-loop DoS but will not prevent a targeted RCE attempt.
server_tokens off— hiding the NGINX version from response headers slows passive reconnaissance but does not prevent exploitation. Attackers can fingerprint NGINX behaviorally or simply spray the exploit against all NGINX targets.- Switching from PCRE to PCRE2 — CVE-2026-9256 affects the rewrite module's buffer calculation logic, not the regex engine itself. The bug is in how NGINX computes replacement string length from capture references, not in how PCRE evaluates the regex.
The supporting signals.
| In-the-wild exploitation | Active. Multiple sources (Indusface, SecurityBoulevard, CyCognito) confirm in-the-wild exploitation of CVE-2026-9256 as of late May 2026. Exploitation causes NGINX worker crash loops (DoS). RCE exploitation in the wild is unconfirmed but the full chain is public. |
|---|---|
| Proof-of-concept | Public RCE exploit. exp_official.py by y198nt and DepthFirstDisclosures — chains PoolSlip (CVE-2026-9256) with Rift (CVE-2026-42945) for ASLR-independent RCE. Includes Docker lab for reproduction. Tracker repo: suominen/CVE-2026-9256. Config triage script: nginx_poolslip_config_triage.py (Penligent). Original discovery credited to Mufeed VH (Winfunc Research), Nebula Security, Vexera AI. |
| EPSS | 0.02697 (2.7%) — moderate probability of exploitation in the next 30 days. Given confirmed active exploitation, EPSS likely lags reality. |
| CISA KEV | Not listed as of 2026-09-30. Active exploitation suggests KEV addition is likely if exploitation broadens. |
| CVSS vector | CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H = 8.1 HIGH. AC:H reflects the config-dependent trigger. CVSS v4.0 re-scored to 9.2 CRITICAL. The ASLR-bypass PoC arguably makes AC:H too generous — real-world complexity for RCE is lower than v3.1 suggests. |
| Affected versions | NGINX Open Source 0.1.17–1.30.1 and 1.31.0; NGINX Plus R32–R36 and 37.0.0. Note: 1.30.1 / 1.31.0 patched CVE-2026-42945 only — still vulnerable to CVE-2026-9256. 0.x branch EOL, no fix. |
| Fixed versions | NGINX OSS 1.30.2 (stable), 1.31.1 (mainline). NGINX Plus R36 P5, R32 P7, R37.0.1.1. Downstream: RHEL/CentOS, Debian, Ubuntu, Amazon Linux, Alpine all have backport packages. TuxCare fix tracked as CLSA-2026-1779714591. |
| Internet exposure | Censys: ~5.7 million internet-facing NGINX instances in the vulnerable version range (VulnCheck, May 2026). CyCognito sector breakdown: Consumer Discretionary 27.7%, Information Technology 17.2% of observed vulnerable assets. Exploitable subset is smaller — config-dependent. |
| Disclosure timeline | 2026-05-22: F5 advisory + upstream patches released. 2026-07-05: PoolSlip × Rift ASLR-independent RCE PoC published. Active exploitation observed from late May 2026 onward. |
| Reporting researchers | Mufeed VH (Winfunc Research), Nebula Security, Vexera AI (original discovery). y198nt (ASLR-bypass chain, glibc-2.41 adaptation). DepthFirstDisclosures (Rift PoC component). |
Sources.
- F5 / NGINX Advisory K000161377
- CyCognito Emerging Threat Analysis
- PoolSlip × Rift ASLR-Independent RCE PoC Archive
- CyberSecurityNews — nginx-poolslip writeup
- suominen/CVE-2026-9256 patch tracker (GitHub)
- Indusface — NGINX Active Exploitation
- Penligent HackingLabs — CVE-2026-9256 deep dive
- NGINX Official Security Advisories
Why this verdict
- ASLR bypass eliminates the key RCE barrier: The vendor's
AC:Hrating assumed ASLR would gate code execution, making DoS the primary real-world outcome. The publicexp_official.pyexploit demonstrates ASLR-independent RCE via partial pointer overwrite, requiring only ~300 HTTP requests against a fully hardened Linux server. This materially reduces the practical attack complexity for RCE and justifies upward pressure from 8.1. - Active exploitation with public tooling: In-the-wild exploitation is confirmed by multiple threat intelligence sources since May 2026. The full RCE chain, including Docker reproduction lab, is publicly available on GitHub. The combination of active exploitation and weaponized public exploit code increases urgency beyond what the vendor score conveys.
- Role multiplier: NGINX is a canonical network edge component. The majority of NGINX deployments (estimated >60%) serve as reverse proxies, load balancers, API gateways, or Kubernetes Ingress Controllers at the network perimeter. This is not an incidental high-value role — it is the *primary* deployment pattern. RCE on an edge proxy gives the attacker a man-in-the-middle position on all proxied traffic (credential interception, response injection, session hijacking) plus pivot access to backend servers and internal networks. In Kubernetes, compromising the NGINX Ingress Controller puts the attacker at the cluster edge with potential access to the API server. Blast radius: host → network segment → backend tier → potentially domain/cluster. This floors the verdict at HIGH.
- Configuration prerequisite provides genuine friction (prevents CRITICAL): The overlapping PCRE capture pattern (
^/((.*))$with$1$2replacement) is required. While 'far from rare' in SEO redirects and URL canonicalization configs, it is NOT a default NGINX configuration. The attacker must find an instance with this specific rewrite pattern. This narrows the exploitable population from 5.7M internet-facing instances to a meaningful subset, providing real downward pressure that keeps this below CRITICAL despite the public RCE chain. - Chaining dependency adds fragility: The ASLR-independent RCE chain requires BOTH CVE-2026-9256 AND CVE-2026-42945 to be unpatched. Organizations that applied the earlier CVE-2026-42945 fix (1.30.1 / 1.31.0) broke the RCE chain — they face DoS only from CVE-2026-9256. This two-CVE dependency is an additional friction point that separates this from a single-shot CRITICAL.
Why not higher?
The overlapping PCRE capture pattern is a hard configuration prerequisite that is not present by default in NGINX. While common in SEO and migration configs, a significant majority of NGINX instances do not have this pattern, meaning the exploitable population is a fraction of the 5.7M exposed instances. Additionally, the full RCE chain requires both CVE-2026-9256 AND CVE-2026-42945 to be unpatched — sites that applied the earlier Rift fix are limited to DoS impact. These two real friction points prevent a CRITICAL rating despite the public ASLR-bypass exploit.
Why not lower?
NGINX is canonically deployed at the network edge — reverse proxy, load balancer, ingress controller — making it a high-value-role component by definition. A public, ASLR-independent RCE exploit exists and is actively exploited in the wild. Even with the configuration prerequisite, the blast radius when the chain succeeds on an edge proxy (traffic interception, backend pivot, potential cluster compromise) is too severe for MEDIUM. The floor for a canonical network-edge component with a working unauthenticated RCE chain is HIGH.
Crowdsourced verification payload.
Run this script on each host running NGINX as a user with read access to the nginx binary and config directory (typically root or a member of the nginx group). Invoke with: sudo bash check_cve_2026_9256.sh. Exit code 0 = PATCHED, 1 = VULNERABLE, 2 = UNKNOWN.
#!/usr/bin/env bash
# check_cve_2026_9256.sh — CVE-2026-9256 (nginx-poolslip) detector
# Checks nginx version AND config for exploitable rewrite patterns.
# Exit: 0=PATCHED 1=VULNERABLE 2=UNKNOWN
set -uo pipefail
NGINX_BIN=$(command -v nginx 2>/dev/null || true)
if [[ -z "$NGINX_BIN" ]]; then
echo "UNKNOWN - nginx binary not found in PATH"
exit 2
fi
VER=$("$NGINX_BIN" -v 2>&1 | grep -oP 'nginx/\K[0-9]+\.[0-9]+\.[0-9]+' || true)
if [[ -z "$VER" ]]; then
echo "UNKNOWN - could not parse nginx version"
exit 2
fi
echo "[*] Detected nginx version: $VER"
IFS='.' read -r MAJ MIN PAT <<< "$VER"
PATCHED=0
if (( MAJ > 1 )); then
PATCHED=1
elif (( MAJ == 1 )); then
if (( MIN >= 32 )); then
PATCHED=1
elif (( MIN == 31 && PAT >= 1 )); then
PATCHED=1
elif (( MIN == 30 && PAT >= 2 )); then
PATCHED=1
fi
fi
if (( PATCHED == 1 )); then
echo "PATCHED - nginx $VER is not in the affected range (CVE-2026-9256)"
exit 0
fi
# Version is vulnerable — check config for exploitable pattern
echo "[!] nginx $VER is in the vulnerable range (0.1.17 – 1.31.0)"
CONFIG_DUMP=$($NGINX_BIN -T 2>/dev/null || true)
VULN_PATTERN=0
if [[ -n "$CONFIG_DUMP" ]]; then
# Look for rewrite with multiple numeric backreferences ($1$2, etc.)
HITS=$(echo "$CONFIG_DUMP" | grep -nE 'rewrite\s+.*\$[0-9]+.*\$[0-9]+' || true)
# Also look for nested capture groups in rewrite
NESTED=$(echo "$CONFIG_DUMP" | grep -nE 'rewrite\s+.*\(\(' || true)
if [[ -n "$HITS" || -n "$NESTED" ]]; then
VULN_PATTERN=1
echo "[!!] Potentially exploitable rewrite patterns found:"
[[ -n "$HITS" ]] && echo "$HITS"
[[ -n "$NESTED" ]] && echo "$NESTED"
else
echo "[*] No overlapping-capture rewrite patterns detected in active config"
fi
else
echo "[*] Could not dump config (need root?) — skipping pattern check"
fi
if (( VULN_PATTERN == 1 )); then
echo "VULNERABLE - nginx $VER with exploitable rewrite config (CVE-2026-9256)"
else
echo "VULNERABLE - nginx $VER in affected range; no exploitable pattern found in config"
echo " NOTE: rewrite rules in dynamically included files may not have been checked"
fi
exit 1