Every CVE Search/ Get API key

CVE-2026-68904

node-opcua: TCP Socket Leak (FIN-WAIT-2) via keepalive reconnection cycle - Resource Exhaustion

7.0 HighFix available

node-opcua is an OPC UA implementation for TypeScript and Node.js. From 2.0.0 until 2.170.0, node-opcua clients using the default keepSessionAlive setting can enter a repeated reconnection cycle when an OPC UA server's clock skew causes BadInvalidTimestamp responses. ClientSessionKeepAliveManager._ping_server treated the server-originated ServiceFault as a network outage and forced a transport reconnect, while ClientTCP_transport._on_ACK_response used socket.end() after failed HEL/ACK negotiation and could leave the connection in FIN-WAIT-2 when the peer did not close. Repetition at the keepAliveInterval accumulates file descriptors and memory until the client process or container can be terminated by resource exhaustion. This issue is fixed in version 2.170.0.

Attack profile

How easy it is to exploit

Attack vector
Network
Complexity
High
Privileges needed
None
User interaction
None

What an attacker can affect

Confidentiality
Low
Integrity
Low
Availability
High

Exploit likelihood

0.44%Unchanged since Sep 17, 2026
90%50%10%1%

What each source reports

CVE List Score used 7.0

node-opcua: TCP Socket Leak (FIN-WAIT-2) via keepalive reconnection cycle - Resource Exhaustion

node-opcua is an OPC UA implementation for TypeScript and Node.js. From 2.0.0 until 2.170.0, node-opcua clients using the default keepSessionAlive setting can enter a repeated reconnection cycle when an OPC UA server's clock skew causes BadInvalidTimestamp responses. ClientSessionKeepAliveManager._ping_server treated the server-originated ServiceFault as a network outage and forced a transport reconnect, while ClientTCP_transport._on_ACK_response used socket.end() after failed HEL/ACK negotiation and could leave the connection in FIN-WAIT-2 when the peer did not close. Repetition at the keepAliveInterval accumulates file descriptors and memory until the client process or container can be terminated by resource exhaustion. This issue is fixed in version 2.170.0.

Record
CVE-2026-68904
CVSS 3.1
7.0 HighCVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:L/A:H
Weakness
CWE-400
Updated
2026-11-02
EUVDEUVD-2026-80869 7.0

node-opcua is an OPC UA implementation for TypeScript and Node.js. From 2.0.0 until 2.170.0, node-opcua clients using the default keepSessionAlive setting can enter a repeated reconnection cycle when an OPC UA server's clock skew causes BadInvalidTimestamp responses. ClientSessionKeepAliveManager._ping_server treated the server-originated ServiceFault as a network outage and forced a transport reconnect, while ClientTCP_transport._on_ACK_response used socket.end() after failed HEL/ACK negotiation and could leave the connection in FIN-WAIT-2 when the peer did not close. Repetition at the keepAliveInterval accumulates file descriptors and memory until the client process or container can be terminated by resource exhaustion. This issue is fixed in version 2.170.0.

Record
EUVD-2026-80869
CVSS 3.1
7.0 HighCVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:L/A:H
Updated
2026-11-02
GitHub AdvisoriesGHSA-r2pf-9cw4-5j65 7.0

node-opcua: TCP Socket Leak (FIN-WAIT-2) via keepalive reconnection cycle - Resource Exhaustion

SUMMARY

A combination of bugs in node-opcua causes unlimited TCP socket accumulation (FIN-WAIT-2 state) during automatic reconnection, leading to memory exhaustion and eventual container/process crash (OOM kill). The issue is triggered by the default configuration (keepSessionAlive: true) when the OPC UA server has clock skew relative to the client.

Affected version: Tested on 2.169.0 (latest as of April 2026).

ENVIRONMENT

  • Node.js: v24.11.0
  • node-opcua: 2.169.0
  • OS: Linux (containerized via Podman, slirp4netns networking)
  • OPC UA Server: Industrial PLC (opc.tcp endpoint), clock skew of ~50 minutes ahead of client
  • Client config: keepSessionAlive: true (default), keepAliveInterval: 3000, securityMode: None, securityPolicy: None

ROOT CAUSE ANALYSIS

Bug #1 - ClientTCP_transport._on_ACK_response() uses socket.end() instead of socket.destroy()

File: node-opcua-transport/src/client_tcp_transport.ts, _on_ACK_response() method

When the HEL/ACK handshake fails during a reconnection attempt, the error handler calls socket.end():

if (err || !data) {
    externalCallback(err || new Error("no data"));
    if (this._socket) {
        this._socket.end();   // <- sends TCP FIN, leaves socket in FIN-WAIT-2
    }
}

socket.end() sends a TCP FIN and waits for the peer to close its side. If the peer doesn't respond (common with PLCs), the socket remains in FIN-WAIT-2 state indefinitely, leaking file descriptors and memory. During rapid reconnection cycles (triggered by Bug #2 below), every failed HEL/ACK creates a new leaked socket.


Bug #2 - ClientSessionKeepAliveManager._ping_server() treats BadInvalidTimestamp as network outage

File: node-opcua-client/src/client_session_keepalive_manager.ts, _ping_server() method

The keepalive manager reads Server.ServerStatus.CurrentTime on each ping cycle. If the server responds with BadInvalidTimestamp (because the client's RequestHeader.timestamp falls outside the server's tolerance window due to clock skew), the manager treats this as a fatal network error:

// Any error -> emit("failure") -> terminateConnection() -> forceConnectionBreak()

This triggers a full transport-level reconnection on every keepalive cycle (every keepAliveInterval ms). Combined with Bug #1, each reconnection attempt leaks one TCP socket in FIN-WAIT-2. Impact amplification: With keepAliveInterval: 3000 (3 seconds), the client leaks ~20 sockets/minute, ~1200/hour, exhausting resources in hours.

REPRODUCTION STEPS

  1. Set up an OPC UA server with a clock skewed more than the server's timestamp tolerance ahead of the client.
  2. Connect using node-opcua with default settings (keepSessionAlive: true).
  3. Monitor TCP sockets: ss -antp | grep FIN-WAIT-2 | wc -l
  4. Observe FIN-WAIT-2 count growing continuously (approximately one per keepalive interval).
  5. Eventually the process runs out of file descriptors or memory and crashes.

SUGGESTED FIXES

For Bug #1 (_on_ACK_response):

// Replace socket.end() with socket.destroy()
if (this._socket) {
    this._socket.destroy();
}

For Bug #2 (_ping_server): Distinguish between transport-level errors (actual network outage) and application-level OPC UA status codes like BadInvalidTimestamp. The latter indicates the server is reachable and the session is alive - only the timestamp validation failed. The keepalive should not trigger reconnection.

REPORTER

Marco Velluso @Velluso [email protected] Requesting CVE assignment and credit as reporter upon fix publication.

Record
GHSA-r2pf-9cw4-5j65
CVSS 3.1
7.0 HighCVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:L/A:H
Weakness
CWE-400
Updated
2026-11-02
NVD 7.0

node-opcua is an OPC UA implementation for TypeScript and Node.js. From 2.0.0 until 2.170.0, node-opcua clients using the default keepSessionAlive setting can enter a repeated reconnection cycle when an OPC UA server's clock skew causes BadInvalidTimestamp responses. ClientSessionKeepAliveManager._ping_server treated the server-originated ServiceFault as a network outage and forced a transport reconnect, while ClientTCP_transport._on_ACK_response used socket.end() after failed HEL/ACK negotiation and could leave the connection in FIN-WAIT-2 when the peer did not close. Repetition at the keepAliveInterval accumulates file descriptors and memory until the client process or container can be terminated by resource exhaustion. This issue is fixed in version 2.170.0.

Record
CVE-2026-68904
CVSS 3.1
7.0 HighCVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:L/A:H
Weakness
CWE-400
Updated
2026-11-02
OSV 7.0

node-opcua: TCP Socket Leak (FIN-WAIT-2) via keepalive reconnection cycle - Resource Exhaustion

node-opcua is an OPC UA implementation for TypeScript and Node.js. From 2.0.0 until 2.170.0, node-opcua clients using the default keepSessionAlive setting can enter a repeated reconnection cycle when an OPC UA server's clock skew causes BadInvalidTimestamp responses. ClientSessionKeepAliveManager._ping_server treated the server-originated ServiceFault as a network outage and forced a transport reconnect, while ClientTCP_transport._on_ACK_response used socket.end() after failed HEL/ACK negotiation and could leave the connection in FIN-WAIT-2 when the peer did not close. Repetition at the keepAliveInterval accumulates file descriptors and memory until the client process or container can be terminated by resource exhaustion. This issue is fixed in version 2.170.0.

Record
CVE-2026-68904
CVSS 3.1
7.0 HighCVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:L/A:H
Weakness
CWE-400
Updated
2026-11-02
OSVGHSA-r2pf-9cw4-5j65 7.0

node-opcua: TCP Socket Leak (FIN-WAIT-2) via keepalive reconnection cycle - Resource Exhaustion

SUMMARY

A combination of bugs in node-opcua causes unlimited TCP socket accumulation (FIN-WAIT-2 state) during automatic reconnection, leading to memory exhaustion and eventual container/process crash (OOM kill). The issue is triggered by the default configuration (keepSessionAlive: true) when the OPC UA server has clock skew relative to the client.

Affected version: Tested on 2.169.0 (latest as of April 2026).

ENVIRONMENT

  • Node.js: v24.11.0
  • node-opcua: 2.169.0
  • OS: Linux (containerized via Podman, slirp4netns networking)
  • OPC UA Server: Industrial PLC (opc.tcp endpoint), clock skew of ~50 minutes ahead of client
  • Client config: keepSessionAlive: true (default), keepAliveInterval: 3000, securityMode: None, securityPolicy: None

ROOT CAUSE ANALYSIS

Bug #1 - ClientTCP_transport._on_ACK_response() uses socket.end() instead of socket.destroy()

File: node-opcua-transport/src/client_tcp_transport.ts, _on_ACK_response() method

When the HEL/ACK handshake fails during a reconnection attempt, the error handler calls socket.end():

if (err || !data) {
    externalCallback(err || new Error("no data"));
    if (this._socket) {
        this._socket.end();   // <- sends TCP FIN, leaves socket in FIN-WAIT-2
    }
}

socket.end() sends a TCP FIN and waits for the peer to close its side. If the peer doesn't respond (common with PLCs), the socket remains in FIN-WAIT-2 state indefinitely, leaking file descriptors and memory. During rapid reconnection cycles (triggered by Bug #2 below), every failed HEL/ACK creates a new leaked socket.


Bug #2 - ClientSessionKeepAliveManager._ping_server() treats BadInvalidTimestamp as network outage

File: node-opcua-client/src/client_session_keepalive_manager.ts, _ping_server() method

The keepalive manager reads Server.ServerStatus.CurrentTime on each ping cycle. If the server responds with BadInvalidTimestamp (because the client's RequestHeader.timestamp falls outside the server's tolerance window due to clock skew), the manager treats this as a fatal network error:

// Any error -> emit("failure") -> terminateConnection() -> forceConnectionBreak()

This triggers a full transport-level reconnection on every keepalive cycle (every keepAliveInterval ms). Combined with Bug #1, each reconnection attempt leaks one TCP socket in FIN-WAIT-2. Impact amplification: With keepAliveInterval: 3000 (3 seconds), the client leaks ~20 sockets/minute, ~1200/hour, exhausting resources in hours.

REPRODUCTION STEPS

  1. Set up an OPC UA server with a clock skewed more than the server's timestamp tolerance ahead of the client.
  2. Connect using node-opcua with default settings (keepSessionAlive: true).
  3. Monitor TCP sockets: ss -antp | grep FIN-WAIT-2 | wc -l
  4. Observe FIN-WAIT-2 count growing continuously (approximately one per keepalive interval).
  5. Eventually the process runs out of file descriptors or memory and crashes.

SUGGESTED FIXES

For Bug #1 (_on_ACK_response):

// Replace socket.end() with socket.destroy()
if (this._socket) {
    this._socket.destroy();
}

For Bug #2 (_ping_server): Distinguish between transport-level errors (actual network outage) and application-level OPC UA status codes like BadInvalidTimestamp. The latter indicates the server is reachable and the session is alive - only the timestamp validation failed. The keepalive should not trigger reconnection.

REPORTER

Marco Velluso @Velluso [email protected] Requesting CVE assignment and credit as reporter upon fix publication.

Record
GHSA-r2pf-9cw4-5j65
CVSS 3.1
7.0 HighCVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:L/A:H
Weakness
CWE-400
Updated
2026-11-02

Affected products

Affected packages

Affected packages
PackageAffectedFixed
node-opcua-transport
npm
2.0.0 up to 2.170.0
2.170.0
node-opcua-client
npm
2.0.0 up to 2.170.0
2.170.0
node-opcua
npm
2.0.0 up to 2.170.0
2.170.0

History

History
RecordedChangedSources
first indexedCVE List, EUVD, GitHub Advisories, NVD, OSV

Dates are when Every CVE recorded each change. Upstream dates may differ.

In short

CVE-2026-68904 is a high vulnerability (CVSS 7.0) in Node-opcua, published September 16, 2026. No source lists public exploit code. EPSS puts the chance of exploitation in the next 30 days at 0.44%, the 38th percentile. Fixed in node-opcua-transport 2.170.0.

References

Show 5 moreShow less
MoveEnter Open