CVE-2026-10099
Description
XX-Net V5.16.6 contains a WebSocket frame parsing vulnerability in the WebSocket_receive_worker routine of simple_http_server.py that allows attackers to cause corrupted application data by sending unmasked WebSocket frames. The server unconditionally reads 4 bytes as a masking key regardless of whether the MASK bit is set in the frame header, causing the first 4 bytes of payload to be consumed as a mask key and the remaining payload to be incorrectly XOR-decoded, resulting in data corruption alongside missing RSV bit, opcode, and FIN fragmentation validations.
AI Insight
LLM-synthesized narrative grounded in this CVE's description and references.
XX-Net V5.16.6 WebSocket parser unconditionally reads a 4-byte masking key, corrupting unmasked frames; fix requires checking MASK bit per RFC 6455.
Vulnerability
[1][2] XX-Net V5.16.6 in simple_http_server.py has a WebSocket frame parsing flaw. The WebSocket_receive_worker routine unconditionally reads 4 bytes as a masking key regardless of the MASK bit in the frame header (byte 2, bit 7). This violates RFC 6455 §5.2. Additionally, RSV bits, opcode, and FIN fragmentation validations are missing.
Exploitation
[2][4] An attacker can send an unmasked WebSocket frame (MASK=0) to the server. No authentication is required if the server is reachable. By default, XX-Net binds to 127.0.0.1, limiting attack vector to localhost; however, enabling "Allow Remote" exposes the service on 0.0.0.0, making it accessible from the network. The exploit involves sending a frame with MASK bit cleared, causing the server to treat the first 4 payload bytes as a mask key.
Impact
[2][4] The server consumes the first 4 payload bytes as a mask key, discarding them, and then XOR-decodes the remaining payload using that key, producing corrupted data. Combined with missing RSV, opcode, and FIN checks, this can lead to application data corruption and misoperation. The vulnerability is rated Low severity due to the default localhost binding.
Mitigation
[1][3] The fix is available in pull request #14170 and commit a68b972a, which adds a check of the MASK bit before reading the masking key. Users should update to a version containing this fix. As a workaround, avoid enabling "Allow Remote" to restrict access to localhost. No KEV listing is known at this time.
- fix: check WebSocket MASK bit before reading masking key (RFC 6455 §5.2) by AAtomical · Pull Request #14170 · XX-net/XX-Net
- WebSocket parser ignores MASK bit, always reads 4-byte masking key
- Merge pull request #14170 from AAtomical/fix/websocket-mask-bit-rfc6455 · XX-net/XX-Net@a68b972
- XX-Net V5.16.6 WebSocket Frame Parsing Data Corruption via simple_http_server.py
AI Insight generated on May 29, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.
Affected products
2Patches
1a68b972a84edMerge pull request #14170 from AAtomical/fix/websocket-mask-bit-rfc6455
1 file changed · +15 −5
code/default/lib/noarch/simple_http_server.py+15 −5 modified@@ -281,15 +281,25 @@ def WebSocket_receive_worker(self): if h is None or len(h) == 0: break - length = ord(h[1]) & 127 + # RFC 6455 §5.2: bit 7 of byte 2 is the MASK flag. + # If MASK=1 a 4-byte masking key follows the (extended) length; + # if MASK=0 the payload begins immediately — no key is present. + is_masked = bool(ord(h[1]) & 0x80) + length = ord(h[1]) & 0x7F if length == 126: length = struct.unpack(">H", self.rfile.read(2))[0] elif length == 127: length = struct.unpack(">Q", self.rfile.read(8))[0] - masks = [ord(byte) for byte in self.rfile.read(4)] - decoded = "" - for char in self.rfile.read(length): - decoded += chr(ord(char) ^ masks[len(decoded) % 4]) + + if is_masked: + masks = [ord(byte) for byte in self.rfile.read(4)] + decoded = "".join( + chr(ord(char) ^ masks[i % 4]) + for i, char in enumerate(self.rfile.read(length)) + ) + else: + decoded = "".join(chr(ord(c)) for c in self.rfile.read(length)) + try: self.WebSocket_on_message(decoded) except Exception as e:
Vulnerability mechanics
Root cause
"Missing MASK bit check causes unconditional reading of 4-byte masking key, consuming and corrupting payload data."
Attack vector
An attacker sends an unmasked WebSocket frame (MASK bit = 0) to the server. Because the server unconditionally reads 4 bytes as a masking key, the first 4 bytes of the actual payload are consumed as the mask key and discarded [ref_id=1]. The remaining payload bytes are XOR-decoded against this wrong key, producing corrupted data that is passed to `WebSocket_on_message` [ref_id=1]. The default binding is 127.0.0.1, limiting exposure; the severity increases when "Allow Remote" is enabled (0.0.0.0) [ref_id=1].
Affected code
The bug is in `WebSocket_receive_worker` within `code/default/lib/noarch/simple_http_server.py` [ref_id=1]. The server unconditionally reads 4 bytes as a masking key via `masks = [ord(byte) for byte in self.rfile.read(4)]` without first checking the MASK bit (bit 7 of the second header byte) [ref_id=1]. The patch modifies lines 281–301 of that file [patch_id=3102122].
What the fix does
The patch [patch_id=3102122] adds a check `is_masked = bool(ord(h[1]) & 0x80)` before reading the masking key, as required by RFC 6455 §5.2 [ref_id=2]. When `is_masked` is true, the original 4-byte read and XOR decoding is performed; when false, the payload is read directly without any masking key [patch_id=3102122]. This closes the data corruption bug and brings the parser into compliance with the WebSocket protocol specification.
Preconditions
- networkThe attacker must be able to send raw WebSocket frames to the server (network access to the listening port).
- inputThe attacker must send a frame with the MASK bit set to 0 (unmasked frame).
Generated on May 29, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
4News mentions
0No linked articles in our index yet.