GHSA-6xgx-v7gw-qjph: Masked WebSocket frame spanning an NX_PACKET chain can overwrite packet metadata and hijack control flow in NetX Duo
GHSA ID |
|
|---|---|
CVE ID |
CVE‑2026‑102761 |
Severity |
Critical |
CWE |
|
Published |
2026‑10‑02 |
Last updated |
2026‑10‑02 |
Affected packages
| Package | Vulnerable versions | Patched versions |
|---|---|---|
NetX Duo |
>= 6.2.0, ⇐ 6.5.1.202602 |
6.5.2.202603 |
Description
Summary
NetX Duo’s WebSocket client resets the unmasking cursor to the first NX_PACKET each time it advances through a chained packet, while the loop’s upper bound belongs to the current packet. With the standard contiguous packet-pool layout, a masked server frame split across two packets therefore drives the XOR loop through the first packet’s unused payload area and on through the second packet’s NX_PACKET control block.
The four-byte WebSocket masking key controls the bytes written, so the corruption is attacker-chosen rather than incidental.
Details
In _nx_websocket_client_data_process:
data_packet = (*packet_ptr);
while (data_packet && frame_header_found)
{
data_ptr = (*packet_ptr) -> nx_packet_prepend_ptr;
while (data_ptr < data_packet -> nx_packet_append_ptr)
{
*data_ptr = (UCHAR)(*data_ptr ^
client_ptr -> nx_websocket_client_mask[received % 4]);
data_ptr++;
received++;
}
data_packet = data_packet -> nx_packet_next;
}
After data_packet advances, data_ptr still starts at the head packet, because the inner loop dereferences *packet_ptr rather than the packet being processed. The upper bound advances with data_packet. For contiguous packet-pool entries the resulting range crosses packet metadata.
The parser also accepts a masked server-to-client frame. A conforming WebSocket server does not mask its frames, but a malicious or compromised endpoint can deliberately send one, and NetX Duo already contains a regression input exercising a masked server response.
Once the second packet’s nx_packet_next has been overwritten, the unmodified parser follows that pointer as though it were another NX_PACKET. The subsequent write is made by production code, not by any harness: with a watchpoint on a modeled adjacent dispatch pointer, the write that changed it was data_packet→nx_packet_append_ptr -= packet_length; inside _nx_websocket_client_data_process, reached through the public receive API with data_packet equal to the attacker-selected fake packet. The second-stage pointer change is therefore attributable to the parser following corrupted metadata.
Impact
An attacker-controlled or compromised WebSocket endpoint can trigger this when all of the following hold:
1\. the device uses the NetX Duo WebSocket client;
2\. the incoming frame spans an NX_PACKET chain — the WebSocket add-on rejects builds defining NX_DISABLE_PACKET_CHAIN;
3\. an incoming masked data frame is split across at least two NX_PACKET objects; and
4\. the target’s packet-pool and executable layout allow the corrupted metadata to be turned into a useful write target.
No WebSocket authentication is required once the client has connected to the hostile endpoint. An on-path attacker can exercise the same path wherever the deployment lacks effective transport authentication.
The demonstrated primitive is attacker-controlled packet-chain pointer corruption followed by an attacker-directed function-pointer rewrite and an indirect call — a remote code-execution primitive rather than only a crash. Confidentiality, integrity and availability impacts can all be high after code execution. Whether a particular firmware image is exploitable depends on its packet-pool layout, linker addresses and enabled mitigations; the modeled dispatch table used to demonstrate the final call is test-only, so the evidence establishes a deterministic control-flow primitive rather than claiming an equivalent callback object exists in every integration.
Across both unoptimized and optimized builds and four packet-pool payload sizes, the affected control-flow transfer succeeded 1,600 times out of 1,600. The fixed build blocked it 1,600 times out of 1,600.
Affected configurations
Builds using the NetX Duo WebSocket client add-on with packet chaining enabled. The add-on rejects builds defining NX_DISABLE_PACKET_CHAIN, so chaining is always available where the client is used.
NetX Duo releases from 6.2.0 up to and including 6.5.1.202602. The statement was introduced in the 6.2.0 release.
Severity
CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N — 9.3, Critical.
AT:N and AC:L reflect that the condition is not probabilistic: it held across every build configuration and packet-pool size exercised, so neither compiler optimization nor ordinary pool sizing makes it intermittent.
Patches
Fixed in NetX Duo 6.5.2.202603. The unmasking loop starts from the packet it is processing rather than from the head of the chain, so the cursor and the bound always belong to the same packet and the XOR never leaves the current packet’s payload.