Sep 16, 2026·8 min read·7 visits
A socket leak and logic comparison bug in node-opcua client keepalives triggers an infinite, high-frequency reconnection storm. The improper socket teardown leaves connections in FIN-WAIT-2 indefinitely, exhausting system file descriptors and causing client-side crashes.
CVE-2026-68904 is a high-severity Denial of Service (DoS) vulnerability in the node-opcua library. It arises from a logical flaw in the keepalive session manager combined with incorrect socket termination at the TCP transport layer. When server-side anomalies occur, affected clients fall into an infinite, high-frequency reconnection loop. Due to the use of graceful teardown (socket.end) instead of immediate termination (socket.destroy) during negotiation failures, sockets remain open in the FIN-WAIT-2 state. This accumulates system file descriptors and memory, eventually crashing the client process.
The node-opcua library is an open-source, full-stack implementation of the OPC Unified Architecture (OPC UA) standard, written in TypeScript and executed within the Node.js runtime environment. OPC UA is a machine-to-machine communication protocol utilized heavily in industrial automation, supervisory control and data acquisition (SCADA) systems, and smart manufacturing ecosystems. In these contexts, node-opcua clients serve as the critical bridge between control networks and enterprise applications, requesting real-time telemetry from programmable logic controllers (PLCs) and industrial servers.
To maintain persistent and reliable communication sessions across potentially unstable network environments, the library implements a background session manager. This manager periodically polls the remote server to verify the active state of the session—a process designated as the keepalive transaction. The attack surface is exposed via this keepalive and the underlying TCP transport layer, where client-side resource management mistakes can have significant operational consequences.
CVE-2026-68904 represents a high-severity Denial of Service (DoS) vulnerability categorized under CWE-400 (Uncontrolled Resource Consumption). The flaw lies in the convergence of a logical misclassification of application-level faults as network disconnects, an object-identity comparison error, and improper transport socket disposal. This configuration triggers a rapid reconnection cycle where sockets are left dangling, leading to complete client process failure due to file descriptor and memory exhaustion.
The technical root cause of CVE-2026-68904 is divided into two distinct logical errors working in tandem: one at the application layer and one at the transport layer. The application-layer issue begins in the ClientSessionKeepAliveManager which monitors the integrity of active sessions. During a standard keepalive check, the client reads the status of a specific node on the server (typically the server status variable). If an error occurs during this transaction, the client evaluates whether the session is still valid or has terminated.
In vulnerable versions, the keepalive manager evaluates the returned error status against expected failure states using strict reference equality (===) on complex StatusCodes objects. Because of Node.js object instantiating patterns, the status code returned in the error response might be a separate object instance with the same value, causing the identity comparison to fail. Consequently, when the server returns expected errors indicating the session is invalid, the check fails to identify them, falling back to a generic network-failure routine that forcefully tears down the channel.
This behavior is compounded by a second logic flaw: the inability to distinguish between transport-level failures and valid application-level failures. For example, if a server's system clock drifts, it will successfully return a valid OPC UA ServiceFault containing the status code BadInvalidTimestamp. Although this fault represents a correct cryptographic/protocol response indicating the session is intact but experiencing clock skew, the client's keepalive manager misinterprets it as a severe network outage. This misinterpretation triggers an immediate transport teardown and an aggressive reconnection sequence.
The transport layer exacerbates these errors during the socket teardown process. When the client initiates a reconnection, it attempts to negotiate a new OPC UA transport session via a HEL/ACK (Hello/Acknowledge) handshake. If this handshake fails, the _on_ACK_response handler initiates socket closure using Node.js's socket.end(). In Node.js, socket.end() initiates a half-close sequence, transmitting a TCP FIN packet and waiting for the peer to acknowledge and transmit its own FIN packet. If the remote peer fails to respond (due to load, network conditions, or malicious design), the client socket remains trapped in the FIN-WAIT-2 state indefinitely, leaking both memory and system file descriptors.
To understand the mechanical changes applied to address these flaws, we analyze the specific code differentials implemented across the affected modules. In the transport-layer module (packages/node-opcua-transport/source/client_tcp_transport.ts), the patch forces immediate, ungraceful closure of failing sockets instead of attempting a polite half-close. By replacing socket.end() with socket.destroy(), the system forcefully tears down the socket state machine and releases operating system resources.
// Packages/node-opcua-transport/source/client_tcp_transport.ts
// Before the patch:
if (this._socket) {
this._socket.end();
}
// After the patch:
if (this._socket) {
const s = this._socket;
this._socket = null; // Prevent re-entrancy bugs
s.destroy(); // Force immediate connection termination
}At the application layer, the strict reference equality issue was corrected by replacing direct identity comparisons with structural equality checks using the .equals() method. This ensures that even if the returned StatusCodes represent separate memory allocations, they are evaluated correctly based on their underlying status value. Additionally, a new interface ServiceFaultAnnotatedError is introduced to allow the client to extract the precise serviceResult status code from server response headers, allowing the client to differentiate between transient transport drops and valid application faults.
// Packages/node-opcua-client/source/client_session_keepalive_manager.ts
// Strict object reference check fix:
const serviceFaultResponse = (err as ServiceFaultAnnotatedError).response;
if (serviceFaultResponse) {
const sc = serviceFaultResponse.responseHeader?.serviceResult;
// Before: sc === StatusCodes.BadSessionIdInvalid || sc === StatusCodes.BadSessionClosed
if (sc?.equals(StatusCodes.BadSessionIdInvalid) || sc?.equals(StatusCodes.BadSessionClosed)) {
this.emit("failure");
terminateConnection(session._client);
resolve(0);
} else {
this.consecutiveFailures++;
this.emit("keepalive_failure");
// Resolve with backoff algorithm
resolve(Math.min(this.checkInterval * 2 ** this.consecutiveFailures, maxBackoffInterval));
}
}Finally, the keepalive manager was refactored to introduce an exponential backoff mechanism. Consecutive failures increment a consecutiveFailures counter, which mathematically increases the retry delay up to a hard cap of 60 seconds (maxBackoffInterval). This stops the client from generating an immediate, high-frequency reconnection storm in the event of persistent server faults or clock drift.
Exploitation of CVE-2026-68904 does not require the injection of shellcode or memory manipulation payloads. Instead, the vulnerability is triggered through external environmental manipulation or protocol abuse that forces the client into the vulnerable reconnection cycle. A prime methodology involves manipulating network-level variables to induce a clock skew on either the client or server host. If the client clock drifts relative to the server, subsequent keepalive requests trigger a BadInvalidTimestamp ServiceFault, initiating the infinite loop.
Alternatively, an attacker positioned as a man-in-the-middle or controlling an unstable destination gateway can systematically trigger the socket leak. By allowing the initial TCP connection but refusing to complete or cleanly terminate the OPC UA HEL/ACK handshake, the attacker forces the client to invoke its handshake error handler. Because the client closed these connections via socket.end(), and the attacker intentionally refuses to transmit the corresponding FIN packet, the client's host operating system accumulates sockets in the FIN-WAIT-2 state.
The rapid rate of the reconnection attempts (lacking any backoff) causes the client to exhaust its allocated file descriptors within minutes. Once the limit is reached, the Node.js runtime is unable to open new file descriptors for standard operations, leading to unhandled exceptions and process termination. This represents a highly reliable, low-complexity Denial of Service vector against critical industrial monitoring clients.
The impact of CVE-2026-68904 is classified primarily as a high-severity Denial of Service (DoS) affecting the availability of industrial automation data flows. In SCADA environments, client processes are responsible for logging telemetry, monitoring safety parameters, and bridging industrial controllers to databases. If the client process terminates or becomes unresponsive, operators lose visibility into physical processes, which can lead to delayed incident response or operational disruptions.
The CVSS v3.1 base score is evaluated at 7.0 (High), with a vector of CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:L/A:H. The attack complexity is rated as high because triggering the fault requires specific, non-default conditions such as clock drift, transport failures, or controlled TCP behavior from the server. However, the impact on availability is complete, as the client process will inevitably crash due to host-level resource exhaustion.
Because Node.js processes typically execute within containerized environments (e.g., Docker or Kubernetes), a local resource exhaustion bug can have cascading effects. A single container leaking file descriptors and memory can saturate the host operating system's socket table or exceed container-group limits. This can cause adjacent containers or the orchestration node itself to experience degradation, magnifying the overall operational impact.
The primary remediation pathway is upgrading the node-opcua dependency to version 2.170.0 or later. This version contains the comprehensive set of patches that address the strict equality failure, implement the .equals() comparison, introduce the exponential backoff, and force ungraceful TCP socket destruction (socket.destroy()) on handshake failure. Projects consuming this library should update their package.json lockfiles and rebuild their deployment artifacts immediately.
In environments where upgrading the library is not immediately feasible, system-level workarounds can mitigate the risk of file descriptor exhaustion. System administrators can harden the host operating system's TCP timeout parameters. Specifically, reducing the tcp_fin_timeout parameter forces the kernel to reclaim sockets stuck in the FIN-WAIT-2 state much faster than the default 60-second window, mitigating the rate of the resource leak.
# Temporarily reduce FIN-WAIT-2 timeout to 15 seconds
sysctl -w net.ipv4.tcp_fin_timeout=15Additionally, network-level validation should be implemented to ensure tight NTP synchronization between the OPC UA client host and server hosts. Keeping clock drift to a minimum reduces the probability of triggering BadInvalidTimestamp errors. Finally, monitoring tools should track the quantity of active TCP connections in the FIN_WAIT_2 state to identify ongoing exploit attempts or environment instability before a process crash occurs.
CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:L/A:H| Product | Affected Versions | Fixed Version |
|---|---|---|
node-opcua node-opcua | >= 2.0.0, < 2.170.0 | 2.170.0 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-400: Uncontrolled Resource Consumption |
| Attack Vector | Network |
| CVSS v3.1 Score | 7.0 (High) |
| EPSS Score | Not Available |
| Impact | Availability (High) - Client Process Crash |
| Exploit Status | PoC / Functional Test Available |
| KEV Status | Not Listed |
The product does not properly control the allocation and maintenance of a limited resource, enabling an actor to influence the amount of resources consumed, eventually leading to exhaustion.
LMDeploy prior to version 0.10.2 is vulnerable to remote code execution because its AsyncRPCServer component implements unauthenticated, remote-accessible communication sockets and uses the insecure pickle.loads() deserializer to process incoming requests.
CVE-2026-61593 is a high-severity Cross-Site Request Forgery (CSRF) vulnerability discovered in the Server-Sent Events (SSE) transport layer of djust, an open-source framework that implements Phoenix LiveView-style reactive server-side rendering for Django applications. Before version 1.0.7, a lack of origin verification on the SSE stream endpoint, combined with @csrf_exempt decorators on message POST endpoints, allowed an attacker to hijack active client sessions through cross-origin interactions.
An untrusted search path vulnerability (CWE-426) in the OpenTelemetry.Resources.Host NuGet package on macOS allows a local attacker to execute arbitrary code with elevated privileges by hijacking standard system commands such as sh and ioreg.
CVE-2026-61598 is a high-severity mass-assignment vulnerability (CWE-915) affecting the Python package djust prior to version 1.0.7. An authenticated client can supply arbitrary parameter names to modify public view attributes on the server via WebSocket events, leading to unauthorized state manipulation, authorization bypass, or price tampering.
An uncontrolled resource consumption vulnerability (CVE-2026-69213) in the http4s Ember HTTP/2 server and client implementations allows unauthenticated remote attackers to trigger an OutOfMemoryError (OOM) and cause a Denial of Service (DoS) by exploiting unbounded outbound queues.
CVE-2026-60137 is a critical SQL injection vulnerability in the Core component of WordPress. The flaw occurs within the WP_Query class during the processing of the author__not_in parameter, where user-supplied array inputs are constructed into a SQL string without strict integer type-casting. When chained with CVE-2026-63030, an unauthenticated remote attacker can exploit this SQL injection to read database values, extract administrator credential hashes, or modify administrative options to execute arbitrary PHP code on the server.