Aug 31, 2026·9 min read·0 visits
A prototype-pollution-style lookup flaw in Engine.IO allows unauthenticated remote attackers to crash the Node.js server process via crafted WebTransport handshake packets containing prototype keywords (such as '__proto__') as session IDs.
An input validation vulnerability in the WebTransport upgrade handler of the Engine.IO server (the core engine driving Socket.IO) allows remote, unauthenticated attackers to trigger a denial of service via application crashes. By sending a crafted session identifier corresponding to a JavaScript prototype property (such as __proto__), an attacker forces the server to reference Object.prototype instead of a valid socket instance, causing a fatal TypeError in the asynchronous execution context.
The engine.io library serves as the core transport engine for socket.io, a widely deployed framework enabling real-time, bidirectional, event-based communication. To support high-performance, low-latency workloads, recent iterations of the library introduced experimental and standard implementations of the WebTransport protocol. Operating over HTTP/3 (UDP), WebTransport offers substantial performance advantages over traditional TCP-based WebSockets, particularly in environments prone to network degradation or head-of-line blocking. This architecture requires the server to expose a dedicated endpoint to process and upgrade incoming client requests.
The attack surface is concentrated in the handshake and upgrade routine of the WebTransport handler. When a client initiates a connection, the server must associate the incoming HTTP/3 stream with an existing connection state using a client-supplied Session ID (SID). This handshake mechanism represents a critical trust boundary because it parses and processes untrusted inputs before validating the existence of the corresponding session. A failure to safely validate the structural integrity of these inputs allows malicious actors to exploit internal data-lookup patterns.
The root of this vulnerability belongs to the category of improper input validation (CWE-20), presenting characteristics similar to prototype-pollution or unsafe object-property lookups. By submitting a session identifier containing standard JavaScript prototype property names (such as __proto__, constructor, or toString), a remote, unauthenticated attacker can manipulate the control flow of the connection-management routine. Instead of retrieving a valid, active socket connection, the lookup mechanism retrieves a reference to the global object prototype.
The security impact of this vulnerability is a complete loss of availability, resulting in a Denial of Service (DoS) condition. Because the target engine is implemented in Node.js, an unhandled exception thrown within an asynchronous stream callback triggers a process-wide crash. In standard production environments lacking robust process-management wrappers or automatic container orchestration, this crash permanently terminates the service. Even in environments with automatic recovery policies, an attacker can continuously transmit malicious handshakes to cause sustained CPU exhaustion and service downtime.
In the JavaScript language, standard objects inherit properties and methods from a common base class, Object.prototype. Property lookups executed using the bracket notation (such as obj[key]) are dynamic by default. When the V8 JavaScript runtime encounters a query for a key, it first checks if the property exists as a direct property of the target object. If the property is absent, the engine traverses up the prototype chain until it either resolves the property or reaches the end of the chain, returning undefined.
The engine.io server keeps track of active client connections using an internal lookup dictionary named this.clients. This dictionary is initialized as a plain JavaScript object ({}). Prior to the introduction of the patch, the upgrade handler parsed the client-supplied data to extract the session ID (sid) and performed a simple truthiness check on the resulting string. However, the logic did not verify whether the parsed string was actually a key registered directly within the this.clients map.
When an attacker sends a WebTransport handshake payload specifying "__proto__" as the session ID, the server executes the statement const client = this.clients["__proto__"]. Because "__proto__" is a prototype accessor property present on all standard JavaScript objects, the engine bypasses the local lookup and retrieves a reference to Object.prototype. The client variable, which the application expects to be either an instance of the Socket class or undefined, now points directly to the built-in prototype object.
Following this lookup, the server attempts to interact with the retrieved object by registering event listeners, configuring stream parameters, or calling Socket-specific methods. Because Object.prototype does not possess these specialized methods, the runtime throws a TypeError. Because this error occurs within an asynchronous WebTransport event callback, it escapes standard try-catch blocks designed for synchronous operations. The unhandled exception propagates to the Node.js event loop, terminating the runtime process immediately.
To understand the vulnerability and the subsequent remediation, it is necessary to examine the vulnerable code path inside packages/engine.io/lib/server.ts. The original implementation allowed any truthy string returned by parseSessionId to proceed directly to the connection mapping step without verifying its validity as a local key.
// VULNERABLE CODE PATH PRIOR TO VERSION 6.6.7
const sid = parseSessionId(value.data);
// The validation only checks if sid is a truthy value (not null/undefined/empty)
if (!sid) {
debug("invalid WebTransport handshake");
return session.close();
}
// The lookup is performed dynamically using bracket notation
// If sid is "__proto__", this resolves to Object.prototype
const client = this.clients[sid];The remediation introduced in commit 1fa1f46cd420ac5b57bb4c04c959b58f3c79158c addresses this gap by ensuring that the session identifier represents an actual, registered client session. The patch introduces a helper function, hasOwn(), which wraps Object.prototype.hasOwnProperty.call(). This design prevents the dynamic lookup from traversing the prototype chain, as hasOwnProperty only evaluates to true for direct properties of the target object.
// PATCHED CODE PATH IN VERSION 6.6.7
// Object.hasOwn() was introduced in Node.js 16.9; a fallback utility is declared for compatibility
function hasOwn(obj: Record<string, any>, key: string): boolean {
return Object.prototype.hasOwnProperty.call(obj, key);
}
// ... within the upgrade handler ...
const sid = parseSessionId(value.data);
// The updated check validates that the sid is truthy AND exists as a direct property of the clients map
if (!sid || !hasOwn(this.clients, sid)) {
debug("invalid WebTransport handshake");
return session.close();
}This fix is highly effective and complete. By requiring hasOwn(this.clients, sid), any attempt to pass prototype keywords like "__proto__", "constructor", or "toString" will evaluate to false because these keys are not direct properties of the this.clients registry. The server logs the failed handshake and gracefully closes the WebTransport session using session.close(), neutralizing the crash vector completely. A more structural fix would involve initializing this.clients with Object.create(null) to completely eliminate prototype properties from the map, but the hasOwn validation represents a robust and sufficient control.
Exploitation of CVE-2026-59724 is straightforward and highly reliable. The primary prerequisite is that the target engine.io or socket.io server must be configured to support the webtransport transport protocol. Additionally, because WebTransport relies on HTTP/3, the server must be reachable over UDP, and any intermediate firewalls must permit HTTP/3 traffic. No authentication credentials or prior session tokens are required to execute the attack.
The attack begins with the establishment of a WebTransport connection from the attacker's client to the target server. Once the handshake over HTTP/3 is completed and the transport is ready, the attacker creates a bidirectional stream. Through this stream, the attacker sends a serialized Engine.IO "open" packet. The packet type is identified by a prefix (the character 0), which is immediately followed by a JSON payload containing the malicious session ID.
The malicious payload uses the prototype accessor "__proto__" as the value for the "sid" parameter. When the server processes the stream data, the parsed JSON resolves this parameter to the string "__proto__". The flow can be visualized using the following sequence diagram:
When the V8 engine evaluates this.clients["__proto__"], it returns Object.prototype. The server then attempts to invoke socket management methods on this object, causing a fatal TypeError that terminates the process.
The direct impact of CVE-2026-59724 is a remote, unauthenticated Denial of Service (DoS) of the target Node.js application. Because Node.js is single-threaded, a fatal unhandled exception terminates the entire runtime process, affecting all connected users and processing tasks. This results in an immediate drop in service availability and disrupts real-time communications across the application.
The vulnerability is assigned a CVSS 3.1 Base Score of 7.5, reflecting its high availability impact, low attack complexity, and the lack of required privileges or user interaction. Because the attack can be executed remotely across public networks, it represents an attractive target for denial of service campaigns. The attack does not, however, lead to confidentiality or integrity breaches, as the attacker cannot read files, access databases, or modify server state.
According to the Exploit Prediction Scoring System (EPSS), the vulnerability has an initial probability score of 0.00609 (0.61%), ranking it in the 46.73rd percentile. While the current likelihood of exploitation in the wild is relatively low, the publication of public proof-of-concept exploits and the trivial nature of the payload significantly increase the likelihood that malicious actors will integrate this vector into automated vulnerability scanners and exploitation toolkits.
The primary and recommended remediation path is to upgrade the engine.io package to version 6.6.7 or greater. If your application depends on socket.io directly, upgrading the parent package to a version that pulls in engine.io@6.6.7 is required. Administrators should run auditing commands such as npm ls engine.io or yarn why engine.io to ensure that no legacy or vulnerable versions remain in the dependency tree.
For environments where an immediate dependency upgrade is not feasible due to change control constraints or legacy platform requirements, the vulnerability can be completely mitigated by disabling the WebTransport protocol. Removing webtransport from the list of allowed transports forces the server to reject WebTransport handshakes entirely, eliminating the attack path while permitting clients to fall back to WebSocket or HTTP long-polling.
// MITIGATED CONFIGURATION (Disabling WebTransport)
const io = new Server(httpServer, {
transports: ["polling", "websocket"] // WebTransport is excluded
});Security teams should also implement detection mechanisms to identify potential exploitation attempts. Host-based monitoring should look for characteristic crash logs containing unhandled TypeError exceptions originating from the engine.io WebTransport handler. At the network perimeter, intrusion detection systems (IDS) and Web Application Firewalls (WAF) should be configured to inspect HTTP/3 traffic and flag or block payloads directed at /engine.io/ that contain prototype-related keys within JSON-formatted parameters.
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H| Product | Affected Versions | Fixed Version |
|---|---|---|
engine.io Socket.IO | >= 6.5.0, < 6.6.7 | 6.6.7 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-20 |
| Attack Vector | Network (UDP / HTTP/3) |
| CVSS Score | 7.5 (High) |
| EPSS Score | 0.00609 (0.61%) |
| Impact | Denial of Service (Process Crash) |
| Exploit Status | PoC Available |
| KEV Status | Not Listed |
The product does not validate or incorrectly validates input that can affect the control flow or data flow of a program.
An in-depth analysis of CVE-2026-81889, a critical Server-Side Request Forgery (SSRF) vulnerability in the remote URL upload component of elFinder web file manager before version 2.1.70. The flaw leverages DNS rebinding due to insecure socket fallbacks when the PHP cURL extension is missing, resulting in access to internal network resources and local loopback services.
A critical algorithmic complexity Denial of Service (DoS) vulnerability exists in the npm package decode-uri-component versions 0.1.0 through 0.4.1. The package employs an inefficient, high-complexity recursive mechanism when processing invalid percent-encoded sequences, such as isolated continuation bytes. An attacker can exploit this behavior by sending malformed strings, causing the Node.js event loop to block entirely and exhausting CPU resources. This vulnerability is resolved in version 0.5.0 by replacing the recursive parser with a single-pass, linear scanning algorithm.
A critical path traversal vulnerability was discovered in the Kirby CMS media component. Prior to versions 4.9.5 and 5.5.2, Kirby failed to validate path-traversal indicators in requested filenames, allowing attackers to check for the existence of local JSON files, delete them, or bypass directory prefix containment logic under certain web server configurations.
A missing authorization vulnerability (CWE-862) in Kirby CMS (versions 5.0.0 through 5.5.1) allows low-privileged authenticated users with Panel access to write temporary chunk files to disk, leading to potential Denial of Service via storage exhaustion.
An authentication bypass vulnerability in @hono/oauth-providers prior to version 0.8.6 allows unauthenticated remote attackers to perform login Cross-Site Request Forgery (CSRF) and forced account linking. Due to a logical 'fail-open' comparison flaw, the middleware validates OAuth callbacks when the state parameter is omitted from both the client cookie and the request query parameters, completely bypassing standard anti-CSRF protections.
CVE-2026-15305 describes a critical security vulnerability within the TYPO3 CMS Form Framework (ext:form) extension. Due to a lifecycle timing mismatch, server-side MIME type validation was bypassed when processing files uploaded via FileUpload or ImageUpload form elements. This allowed remote, unauthenticated attackers to upload arbitrary file types (with the exception of blocked PHP extensions) to the web server's public storage directory.