Nginx Map Regex Flaw Lets One Request Take Over a Server

Ethical Hacking Complete Course Zero to Expert
Hack like black hat hackers. Penetration testing, Kali Linux, WiFi and web hacking, and the hacker mindset behind it.
→ Take the full courseFor 15 years, a single crafted web request could quietly take over an nginx server, the software that receives and routes incoming traffic for roughly a third of the websites people load. The flaw was documented in public years ago and marked as something to fix, and the danger went unrecognised until a researcher looked again last week.
nginx is the piece of software sitting in front of a large slice of the internet. When you open a site, there is a good chance nginx is the first thing that reads your request, deals with the encryption, decides where the request should go, and passes it to whatever runs the site behind it.
It sits right at the front, it faces the open internet, and it reads input that comes straight from the outside. That position is what makes a memory bug inside it dangerous.
On July 15, F5, the company that maintains nginx, shipped fixes for a flaw tracked as CVE-2026-42533. It scores 9.2 out of 10 on CVSS v4, which lands it firmly in the critical band. A remote attacker with no account and no special access can send a specially built HTTP request and trigger a heap buffer overflow inside the nginx worker process, the part that handles incoming traffic. At the low end that crashes or restarts the worker, which is a denial of service. Under the right conditions it reaches further, into remote code execution, where the attacker runs their own code on your server.
There is a catch, and leaving it out would be dishonest. Running an older version is not enough on its own. It depends on your configuration.
nginx lets you match parts of a URL or a hostname with a pattern and reuse the pieces it grabs, written as $1, $2, and so on. It also lets you build a small lookup table called a map, and that map can use a pattern of its own to decide its answer. The trouble starts when a config uses one of those grabbed pieces and then, a little later in the same block, uses a map that runs its own pattern. That ordering is what triggers the bug.
Plenty of configs never do it. Plenty of others do it without a second thought, especially setups that pass request details along to a backend.
The part that makes this stand out is its age. The vulnerable code has been in nginx since March 2011, when the map feature first gained the ability to use patterns. But the strange behaviour underneath it was spotted and written down long before anyone treated it as a security problem. Back in 2014, a user filed a report in nginx’s public bug tracker describing the same thing, that a map with a pattern was overwriting the pieces captured by an earlier rule, so a $1 reference quietly returned different data depending on whether the map matched. The maintainer looked at it, agreed the behaviour was wrong, and marked it as something that should be fixed.
Over the years that followed, the ticket collected five more reports pointing at the same problem, and at one point someone argued it deserved a higher severity. It stayed open. For more than a decade the misbehaviour was documented, acknowledged, and public, and the connection to memory corruption stayed unrecognised until now.
Stan Shaw, a researcher who publishes as cyberstan and was one of the people who reported the flaw to F5, is the one who finally tied that old, known quirk to a heap overflow and an ASLR bypass sitting beneath it.
He was not the only one who caught it, either. More than a dozen researchers reported the same flaw to F5 independently, which tells you how plainly it had been sitting there once people started to look.
To see why the ordering breaks things, you need one detail about how nginx builds a piece of text while handling a request. It does the job in two rounds. The first round walks through the value and measures how many bytes it will need, then sets aside a buffer of exactly that size. The second round walks through the same value again and writes the actual bytes into that buffer. The design leans on a single assumption, that both rounds see the same thing and arrive at the same size. That assumption is where it breaks.
Those $1 captures are not copied into the value when the match happens. They are read live from one shared spot on the request, an array nginx calls r->captures, and both rounds read from that same spot.
When a map with a pattern gets evaluated in between the two rounds, its own match writes fresh results into that shared spot, now pointing at a different piece of the request that the attacker controls, like a header or the request body. Nothing saves the old value first, and nothing restores it afterward.
So the first round measures $1 as the short thing it originally matched and sets aside a small buffer, the map then swaps the shared spot for the attacker’s longer string, and the second round reads $1 again, sees the long version, and writes far more than the buffer was built to hold. The extra bytes spill past the end of the buffer into neighbouring memory, and both their length and their content came straight from the request.
The same trick works the other way around, and that is where it gets nasty. This time the attacker makes the swapped-in piece shorter than the original instead of longer, so the buffer ends up too big instead of too small. nginx set aside room for a large value, wrote only a couple of bytes, and handed back the full buffer anyway.
The leftover space was never wiped clean. It still holds whatever sat in that memory a moment before, and part of that is a handful of memory addresses, numbers that tell the server where its own code and data are loaded. The attacker gets to read those numbers straight out of the response.
Computers try to block that by loading everything at random spots each time, so an attacker cannot guess where anything sits. That defence is called ASLR. This leak hands over the addresses anyway, so the randomising counts for nothing. One request, no login, and the server just told the attacker its own layout. That is the moment a crash turns into a way in.
Now the part where it pays to be careful, because there are two stories about how bad this is.
F5, in its official advisory, keeps it modest. What they will commit to is a crash, a denial of service. They say code execution is possible, but only under certain conditions, like when that randomising defence is switched off or can be worked around.
Shaw, one of the people who reported the bug, goes further. His point is that the flaw does not need the defence to be off, because the leak we just walked through switches it off by itself. Chain the leak and the overflow together and you get code running on the server with no login needed. He says he got it working ten times out of ten on a plain Ubuntu 24.04 machine with that defence fully on.
So which is it. The crash, the bug, and the fix are solid, documented by both F5 and nginx. Shaw reported the flaw back in May and the fix shipped on July 15, so this went the coordinated route, patch first and details later. The full take-over chain rests on Shaw’s own testing, and he is sitting on the proof of concept and the exact steps for 21 days after the patch, until around August 5. The take-over claim cannot be checked independently yet.
And right now it is quiet. On July 20 the flaw was not on CISA’s list of bugs known to be exploited, and no public exploit code had shown up. This is not being used in attacks today. It is a countdown to the day the details go public.
The reach inside nginx is wider than one obscure setting. Shaw found the same two-round pattern in at least 13 separate places across nginx’s code, covering around 50 config directives, in both the normal web module and the stream module that handles raw TCP and UDP traffic. The capture and the map do not even need to share a line. Two ordinary lines in the same block are enough, which is the shape a lot of live setups already have when they forward request details to a backend:
| |
The first line hands $1 to both rounds. The second line runs the map in between and swaps the shared capture spot. When the first line gets written out during the second round, it uses the swapped value and writes more than was measured. Same overflow, reached through a config pattern that looks completely normal.
This is not the first bug of its kind in nginx this year either. It is the third flaw in a few months rooted in that same two-round design in nginx’s expression engine, after one called Rift (CVE-2026-42945) in May and an overlapping-capture bug in the rewrite module (CVE-2026-9256) days later. Different triggers, same underlying weakness, a measure-then-write design that assumes nothing changes between the two steps while the evaluation itself is quietly changing things. Rift is the reason to move fast on this one, because its exploit went public within days of disclosure and it was being used in attacks soon after.
The clean answer is to upgrade, and these are the fixed builds:
- โ nginx 1.30.4 (stable) or 1.31.3 (mainline)
- โ NGINX Plus 37.0.3.1, or R36 P7 on the older branch
- โ NGINX Ingress Controller 5.5.3 or 2026-lts-r4
- โ NGINX Gateway Fabric 2.6.7
The same July 15 release also patched two smaller memory bugs in nginx (CVE-2026-60005 and CVE-2026-56434), so a single upgrade clears all three.
If you cannot patch straight away, F5’s temporary workaround is to switch affected regex maps over to named captures instead of numbered ones. It closes the main path, but Shaw showed a second route through named captures that the workaround does not cover, so treat it as extra time and not a complete fix. Upgrading is the only full answer.
Before you change anything, you can find out whether you are exposed at all. Look through your config for any location block that combines a regex capture with a regex map evaluated after it in the same block. A read-only scanner that flags the risky ordering has been published, and it sends no traffic and exploits nothing, so you can check your own setup before you touch it.
What to do, in order:
- โ Check your config for any location block that pairs a regex capture with a regex map evaluated after it
- โ Upgrade any nginx from 0.9.6 up to 1.30.3 or 1.31.2, since versions across that range are affected
- โ Prioritise public-facing reverse proxies, ingress controllers, and edge servers, since those take requests straight from the open internet
- โ If you genuinely cannot upgrade today, apply the named-capture workaround and schedule the upgrade anyway
- โ Get it done before the full proof of concept goes public around August 5
F5 did not solve it the obvious way. Instead of saving the captures before the first round and restoring them before the second, they put a hard limit on the write step. nginx now records where the buffer ends and checks that boundary before every write, and the moment a write would run past it, the request stops with a plain 500 error rather than corrupting memory. The leak side is closed the same way, by returning only the bytes that were actually written instead of the full measured length, so an oversized buffer no longer sends its old contents back in the response.
nginx sits at the front of a network and reads whatever a stranger sends it. Learning to read a request and its response, spot the weak point where a server mishandles what you send, and turn one small opening into control of the machine is what I walk through step by step in my ethical hacking course, all the way to taking a CVE from the wild and turning a rough proof of concept into a working attack:
โ Join my complete ethical hacking course
Hacking is not a hobby but a way of life.
Sources:
nginx security advisories | F5 advisory K000162097 | cyberstan
Stay updated
Get the latest posts in your inbox every week. Ethical hacking, security news, tutorials, and everything that catches my attention. If that sounds useful, drop your email below.