Project

General

Profile

Actions

Bug #7285

closed

Websocket compression mishandling

Added by Hans Vermeer 7 months ago. Updated 5 days ago.

Status:
Closed
Priority:
Normal
Target version:
Affected Versions:
Effort:
Difficulty:
Label:

Description

There are a number of problems with the decompression implementation of WebSockets. Firstly, in the example PCAP every individual transaction is comprised of a single frame, which fails (https://github.com/OISF/suricata/blob/089d2b1/rust/src/websocket/websocket.rs#L202) because the buffer is empty and the fin flag is set, therefore the path is never executed and for the same reason its not marked as compressed.

Now the other problem is with the LZ77 sliding window (https://datatracker.ietf.org/doc/html/rfc7692#section-7.2.2). In this case the Sec-WebSocket-Extensions negotiate a client_max_window_bits=15, but this is not followed in the decompression, resulting in the situations in the screenshot where blocks of data are missing (only after marking every frame as compressed).
Lastly, it doesn't seem like there is any handling on decompression limits with the DeflateDecoder, despite the default WEBSOCKET_MAX_PAYLOAD_SIZE of size 0xffff (which can be increased by users), 65kb is probably enough to have a decent deflate bomb (it seems the limit is ~1032 * 0xffff ~ 67mb).

A python example that handles the decompression properly:

import zlib

leader = "0000ffff" 
pkts = ["f2768db435304f4eb23435b54c4d4b33324c354e353649334e4a4a31b4303231484a4c360600", "0af17777f771b5354ab1304832324a4d49324e4c4b0600", "820a9a9ba59898a65aa4a6a4261a1ba40100"]
client_max_window_bits = 15
c = zlib.decompressobj(-client_max_window_bits)

for p in pkts:
    print(c.decompress(bytes.fromhex(p + leader)))


Files

example_websocket.pcap (2.82 KB) example_websocket.pcap pcap Hans Vermeer, 09/26/2024 06:30 PM
websocket_fail.png (28.1 KB) websocket_fail.png Hans Vermeer, 09/26/2024 06:39 PM
websocket_fail2.png (36.9 KB) websocket_fail2.png Hans Vermeer, 09/26/2024 06:40 PM
Actions #1

Updated by Philippe Antoine about 2 months ago

  • Assignee changed from OISF Dev to Philippe Antoine
Actions #2

Updated by Philippe Antoine 29 days ago

  • Target version changed from TBD to 8.0.0-rc1
Actions #3

Updated by Philippe Antoine 28 days ago

  • Status changed from New to In Progress

I have a branch websocket-compression-7285-v1.1

Actions #4

Updated by Philippe Antoine 26 days ago

  • Status changed from In Progress to In Review
Actions #5

Updated by Hans Vermeer 16 days ago

It looks like it should catch most websocket connections fine, but is still missing a couple of spec requirements to be fully compliant, for example `permessage-deflate` and `server_max_window_bits`, but these are rarely used from my experience.
Also a small thing;

A client MAY include the "client_max_window_bits" extension parameter
in an extension negotiation offer. This parameter has no value or a
decimal integer value without leading zeroes between 8 to 15 inclusive.

Current code checks for 9 to 15 inclusive

Actions #6

Updated by Philippe Antoine 13 days ago

Current code checks for 9 to 15 inclusive

Because of https://docs.rs/flate2/latest/flate2/struct.Decompress.html#panics

Do you have a test case with 8 ?

Actions #7

Updated by Philippe Antoine 10 days ago

Hans Vermeer wrote in #note-5:

is still missing a couple of spec requirements to be fully compliant, for example `permessage-deflate` and `server_max_window_bits`

Would you have pcaps to illustrate these cases ?
I am not sure what you mean for `permessage-deflate` : what is missing ?

Actions #8

Updated by Philippe Antoine 5 days ago

  • Status changed from In Review to Closed
Actions

Also available in: Atom PDF