Eight days ago, sysadmins everywhere scrambled to patch a critical NGINX flaw called Rift (CVE-2026-42945). Many of those patched servers are now vulnerable to something else entirely.
On May 21, 2026, Nebula Security (NebSec) disclosed nginx-poolslip, an unpatched remote code execution zero-day affecting NGINX 1.31.0 – the very version released to fix Rift. No CVE has been assigned. No patch exists yet. NebSec is holding technical details under a 30-day responsible disclosure window while F5 works on a fix.
The kicker: this one bypasses ASLR.

What NGINX Rift Actually Fixed (and Didn’t)
To understand why poolslip exists, you need to know how NGINX handles memory.
Rather than using global heap allocations for each HTTP request, NGINX creates a dedicated memory pool (ngx_pool_t) per request. As headers are parsed, URIs processed, and routing handled, memory is carved sequentially from this pre-allocated pool. When the request is done, the entire pool is destroyed at once which is a clean, fast design that reduces fragmentation.
Inside that pool structure, NGINX maintains a linked list of cleanup handlers (ngx_pool_cleanup_t). If a module needs to close file descriptors or release library memory before pool destruction, it registers a function pointer there.
CVE-2026-42945 (Rift) exploited a calculation error in the rewrite module’s script compilation engine. The bug allowed an attacker to overwrite the cleanup function pointer at offset 64 of the pool structure through cross-request heap manipulation. When NGINX freed the pool, it executed whatever address had been written there but in practice, pointing execution into libc functions like system().
F5’s patch in versions 1.31.0 and 1.30.1 tightened boundary checks on heap-allocated strings produced by the rewrite parser. That fix just wasn’t complete.
How nginx-poolslip Works
NebSec describes poolslip as a technique where an attacker triggers a controlled pointer “slip” across adjacent linked structures in the same ngx_pool_t – a different code path to the same corruption target.
Where Rift used the specific buffer calculation error that F5 patched, poolslip exploits a separate, unpatched path through dynamic variable parsing in core directives: set, map, geo, and upstream connection configurations. These directives share dynamic buffer construction logic with the rewrite engine, and that shared logic was not addressed by the 1.31.0 patch.
The result is the same end state – a corrupted pool structure with a hijacked cleanup pointer but reached through a route the patch didn’t touch.
What makes this worse is the confirmed ASLR bypass. Modern Linux systems randomize the memory layout of processes, so even if an attacker can corrupt a pointer, they typically don’t know where libc lives in memory. NebSec says poolslip includes a memory disclosure primitive that leaks layout information back to the attacker before the write payload executes. On Ubuntu, Debian, Red Hat, and AlmaLinux and ASLR is enabled by default and as a result, exploitation is described as highly reliable.
Security researcher Artem Russakovskii, Android Police founder, put it plainly on X: “you may not be safe even on nginx 1.31.0.”
What Makes nginx-poolslip Dangerous
At a low level, nginx-poolslip appears to target the same class of weakness that made NGINX Rift dangerous: corruption of request-scoped memory inside NGINX’s pool allocator.
NGINX does not allocate and free every request object individually through the normal heap. Instead, it uses a per-request memory pool, represented internally by ngx_pool_t. Request metadata, parsed variables, rewritten URI components, temporary buffers, and module-specific structures are allocated from this pool. When the request finishes, NGINX destroys the entire pool in one operation.
That design is extremely fast, but it also means that if an attacker can disturb object boundaries inside the pool, adjacent structures may become reachable through a memory corruption primitive.
The most sensitive target is the pool cleanup chain. NGINX modules can register cleanup handlers using structures similar to:
typedef struct ngx_pool_cleanup_s ngx_pool_cleanup_t;struct ngx_pool_cleanup_s { ngx_pool_cleanup_pt handler; void *data; ngx_pool_cleanup_t *next;};
The handler field is a function pointer. Under normal conditions, NGINX calls these handlers when the request pool is destroyed, allowing modules to close files, release resources, or clean up temporary state. If an attacker can overwrite or redirect that handler pointer, pool destruction becomes a control-flow hijack opportunity.
For CVE-2026-42945, the confirmed vulnerable area is ngx_http_rewrite_module. F5 describes the issue as affecting NGINX Open Source and NGINX Plus when a rewrite directive is followed by rewrite, if, or set, and unnamed PCRE captures such as $1 or $2 are used with a replacement string containing ?. F5 states that exploitation can allow denial of service and may possibly trigger code execution. NVD describes the same vulnerable condition around unnamed PCRE captures and crafted HTTP requests.
nginx-poolslip is different because it is claimed to bypass the Rift patch rather than reuse the exact same patched bug. Instead of relying on the original rewrite-module calculation flaw, the attack allegedly reaches the same kind of pool corruption through dynamic variable construction paths used by directives such as set, map, geo, and upstream-related configuration logic. In other words, the dangerous part is not merely “rewrite rules exist,” but that multiple NGINX configuration features eventually rely on similar internal variable-expansion and buffer-building behavior.
A simplified attack model looks like this:
Crafted HTTP request ↓Dynamic variable expansion / rewrite-style parsing ↓Incorrect buffer sizing or pointer advancement ↓Pointer “slips” across adjacent ngx_pool_t allocations ↓Cleanup chain or cleanup handler metadata becomes corrupted ↓Request finishes and pool cleanup runs ↓Worker process dereferences attacker-influenced function pointer ↓Crash, or in stronger conditions, code execution
The important part is timing. The corrupted function pointer does not necessarily execute immediately during parsing. It may only become dangerous when the request lifecycle ends and NGINX walks the cleanup list. That makes the bug harder to reason about from logs because the crash or hijack can occur after the triggering parser path has already completed.
Why ASLR Bypass Matters
Modern Linux systems randomize memory locations using ASLR, so even if an attacker corrupts a function pointer, they usually still need to know where useful code lives in memory. Without an information leak, an overwritten pointer often just crashes the worker.
The nginx-poolslip claim is more serious because NebSec says the chain includes a memory disclosure primitive before the final control-flow hijack. Public reporting says the full ASLR bypass mechanics are being withheld during the disclosure window, but the implication is that the exploit can first leak enough address information to calculate the location of libc, NGINX text segments, heap regions, or other reusable gadgets.
That changes the exploitability profile:
Memory corruption only:Pointer overwrite → unknown address → likely crashMemory corruption + info leak:Leak address → calculate target → overwrite pointer → controlled execution
This is why the phrase “ASLR bypass” matters. It suggests the attack is not just a theoretical heap corruption that works in a lab with ASLR disabled. It implies a stronger chain where the attacker can first defeat layout randomization, then use the pool cleanup path as the execution trigger.
Why Named Captures Are Safer Than $1 and $2
A practical mitigation is to replace anonymous PCRE captures with named captures wherever possible.
Riskier pattern:
rewrite ^/users/([0-9]+)/profile/(.*)$ /profile.php?id=$1&tab=$2 last;
Safer pattern:
rewrite ^/users/(?<user_id>[0-9]+)/profile/(?<section>.*)$ /profile.php?id=$user_id&tab=$section last;
The reason this matters is that anonymous captures such as $1, $2, and $3 rely on positional capture expansion. The Rift advisory specifically calls out unnamed PCRE captures as part of the vulnerable condition. Named captures typically travel through a different lookup path and make the configuration easier to audit. They also reduce mistakes where a later regex edit changes the meaning of $1 or $2.
This does not prove named captures are a complete fix for nginx-poolslip, because the public technical writeup is not yet available. But as a defensive hardening measure, replacing anonymous captures reduces exposure to the exact class of parser behavior already implicated in Rift and now suspected in poolslip.
Why This Is Not Just a “Bad Regex” Bug
It is tempting to describe this as a rewrite-rule bug, but technically the issue is deeper. The risky area is the interaction between:
- PCRE capture extraction
- NGINX variable expansion
- dynamically sized temporary buffers
- request-pool allocation layout
- cleanup-handler execution during request teardown
A single malformed request may not be enough in every environment. Reliable exploitation often depends on shaping memory layout so that the object overwritten by the parser is useful to the attacker. That is why heap grooming or heap spraying appears in discussions around this bug. The attacker wants predictable objects to sit next to each other inside the request pool, so a pointer slip or overflow lands on something security-sensitive rather than random unused memory.
In NGINX’s case, a corrupted cleanup handler is especially valuable because it naturally gives the attacker a later indirect function call. That is much more useful than corrupting ordinary request text, because the worker process eventually treats the corrupted value as executable control flow.
Indicators That Failed Exploitation May Be Happening
Because public exploit details are limited, detection is mostly behavioral. Failed attempts are likely to crash worker processes rather than produce a neat HTTP signature.
Useful signals include:
worker process exited on signal 11worker process exited on signal 6segmentation faultcore dumpedmalloc(): corrupted top sizefree(): invalid pointer
For systemd-based deployments:
journalctl -u nginx --since "24 hours ago" | grep -Ei "signal 11|segmentation|core dumped|invalid pointer|corrupt"
For standard log locations:
grep -Ei "signal 11|segmentation|core dumped|invalid pointer|corrupt" /var/log/nginx/error.log
A single crash is not proof of exploitation. But repeated worker crashes combined with strange requests hitting rewrite-heavy endpoints, redirect endpoints, legacy URL handlers, or proxy routes should be treated as suspicious.
Who Found It
The disclosure is also notable for how the bug was found. nginx-poolslip was discovered by Vega, an autonomous AI security agent built by NebSec – not through manual code review.
Vega doesn’t work like traditional security tools. Fuzzers throw random inputs at a running binary and watch for crashes. Static scanners look for known-bad patterns in source code. Vega maps execution and call paths systematically, generates hypotheses about how variables move through memory, and writes targeted tests to validate those hypotheses against a running instance of the software.
NebSec’s published statistics on Vega: 727 verified Linux kernel bugs, 709 zero-days in Chrome’s V8 engine, 33 CVE reports filed with MITRE (8 already publicly assigned). The tool integrates into developer workflows as a GitHub App, CLI, and MCP server compatible with Claude Code and Cursor.
This is part of a broader trend. Earlier in 2026, Anthropic’s Claude Mythos bypassed Apple’s M5 Memory Integrity Enforcement within five days of debugging and subsequently mapped exploit chains across dozens of Cloudflare production repositories. These aren’t research demos anymore. AI systems are finding real, exploitable bugs faster than human teams can patch them.
What to Do Right Now
There is no official patch yet. These mitigations reduce exposure while you wait.
Convert anonymous PCRE captures to named captures. The primary trigger for both Rift and poolslip involves the rewrite parser handling anonymous capture groups ($1, $2). Named captures route through a different, safer code path.
Vulnerable pattern:
rewrite ^/users/([0-9]+)/profile/(.*)$ /profile.php?id=$1&tab=$2 last;
Safer alternative:
rewrite ^/users/(?<user_id>[0-9]+)/profile/(?<section>.*)$ /profile.php?id=$user_id&tab=$section last;
Verify ASLR is fully enabled. The exploit includes an ASLR bypass, but simpler variants may not. Maximum randomization raises the bar.
Check current state:
cat /proc/sys/kernel/randomize_va_space
If the output is 0 or 1, set it to 2:
sysctl -w kernel.randomize_va_space=2
To persist across reboots, add to /etc/sysctl.conf:
kernel.randomize_va_space = 2
Update WAF rules to catch heap spray patterns. Pool corruption exploits typically require the attacker to spray memory with structured fake ngx_pool_cleanup_s blocks. If you run ModSecurity, AWS WAF, or Cloudflare, push updated rule sets that flag high-entropy hexadecimal payloads in POST bodies targeting endpoints that invoke rewrite or variable parsing logic.
Audit and trim your rewrite directives. Many production configs carry rewrite rules that are years old and no longer necessary. Where you can, replace them with exact location matches, static alias rules, or push routing logic into your application layer. Less rewrite logic means less exposure.
Watch your error logs for signal 11. Failed exploitation attempts against pool structures almost always cause memory access violations, which kill and restart the worker process. Monitor /var/log/nginx/error.log (or journald) for:
worker process exited on signal 11 (core dumped)
A sudden spike in these messages is a sign someone is probing your server.
The Patching Pipeline Problem
Once F5 ships a patch (which is expected sometime in early June), the timeline diverges significantly by environment.
Debian and Ubuntu LTS typically push packages within 6 to 24 hours of an upstream fix. AlmaLinux and RHEL follow within 12 to 36 hours. Docker’s official images often update within hours of a new git tag, though only if your deployments pull the latest tag rather than a pinned hash.
Kubernetes Ingress Controllers are the slow lane. The NGINX Ingress Controller wraps the core binary, and updated images from maintainers typically lag 2 to 5 days behind upstream. If you run ingress at scale, plan accordingly.
What Happens Next
NebSec has filed the full technical details with F5’s Security Incident Response Team. The public writeup – including the ASLR bypass mechanics and full code execution chain – stays confidential for 30 days after an official patch ships.
The practical timeline, as things stand:
- Late May: F5 and upstream NGINX maintainers reproducing and patching the bug
- Early June: Expected release of NGINX 1.31.1 (mainline) and 1.30.2 (stable)
- Mid-June: Tenable, Qualys, and Rapid7 scanner signatures start rolling out
- Early July: NebSec publishes the full writeup; public exploit scripts likely appear on GitHub shortly after
Once that writeup drops and exploits circulate, the threat index for unpatched servers goes up sharply. The window to get ahead of this is now.
FAQ
Has a CVE been assigned? No. As of May 22, 2026, NebSec is tracking this under the name “nginx-poolslip.” A CVE is expected once F5 completes triage and registers with MITRE.
Does NGINX Plus have the same problem? Yes. NGINX Plus shares the core C codebase with the open-source distribution. If your Plus configuration uses dynamic variable rewrite or mapping modules, treat it as vulnerable.
Why NGINX 1.31.0 specifically? Because 1.31.0 was the patch release for Rift. The fix addressed the specific parser error used in the Rift proof-of-concept but left adjacent memory manipulation paths open. Vega found one of them.
Can vulnerability scanners detect this now? Only by version check. There’s no public exploit payload or unique network signature yet, so scanners can’t run active checks without risking crashing your worker processes.








