Skip to content

Buffer overflow (read) in WebSocketParser masked payload decoding #769

Description

@hgarrereyn

Hi, there is a buffer overflow (read) reachable with a crafted websocket partial payload in WebSocketParser.

Tested on e2ba81ba.

(found via automated fuzzing)

The following testcase demonstrates the issue:

testcase.cpp

#include <string>
#include "hv/WebSocketParser.h"

int main(){
    WebSocketParser p;
    // Optional: install a no-op callback to match typical usage
    p.onMessage = [](int, const std::string&){};

    // Minimal crashing buffer (first 49 bytes from the original input)
    unsigned char data[49] = {
        0,0,241,255,255,255,255,0,0,0,0,0,221,0,0,241,255,255,255,255,0,0,0,0,
        255,0,255,0,243,243,243,243,243,243,243,0,241,255,255,255,255,255,255,255,
        0,0,0,0,255
    };

    // Feeding 48 bytes causes websocket_parser_decode to read 1 byte past the end
    p.FeedRecvData((const char*)data, 48);
    return 0;
}

crash report

{
  "Date": "2025-09-27T00:09:32.587003+00:00",
  "Uname": "Linux 82d20f773412 5.15.0-140-generic #150-Ubuntu SMP Sat Apr 12 06:00:09 UTC 2025 x86_64 x86_64 x86_64 GNU/Linux",
  "OS": "Ubuntu",
  "OSRelease": "22.04",
  "Architecture": "amd64",
  "ExecutablePath": "/tmp/tmpl2ejhjjc/reproducer",
  "ProcEnviron": [
    "LIBAFL_EDGES_MAP_SIZE=800000",
    "PWD=/fuzz/workspace",
    "CXX=gf_libafl_cxx",
    "GRAPHFUZZ_USE_ASAN=1",
    "HOME=/root",
    "ASAN_OPTIONS=hard_rss_limit_mb=1024:detect_leaks=0",
    "TERM=xterm-256color",
    "SHLVL=1",
    "LD_LIBRARY_PATH=/fuzz/install/lib",
    "PATH=/root/.cargo/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
    "CC=gf_libafl_cc",
    "DEBIAN_FRONTEND=noninteractive",
    "OLDPWD=/fuzz/src",
    "_=/usr/local/bin/agfi"
  ],
  "ProcCmdline": "/tmp/tmpl2ejhjjc/reproducer",
  "Stdin": "",
  "ProcStatus": [],
  "ProcMaps": [],
  "ProcFiles": [],
  "NetworkConnections": [],
  "CrashSeverity": {
    "Type": "NOT_EXPLOITABLE",
    "ShortDescription": "stack-buffer-overflow(read)",
    "Description": "Stack buffer overflow",
    "Explanation": "The target reads data past the end, or before the beginning, of the intended stack buffer."
  },
  "Stacktrace": [
    "    #0 0x55555565dde1 in websocket_parser_decode /fuzz/src/http/websocket_parser.c:176:18",
    "    #1 0x55555565c079 in on_frame_body(websocket_parser*, char const*, unsigned long) /fuzz/src/http/WebSocketParser.cpp:33:9",
    "    #2 0x55555565cce3 in websocket_parser_execute /fuzz/src/http/websocket_parser.c:146:25",
    "    #3 0x55555565b691 in main /tmp/tmpl2ejhjjc/reproducer.cpp:17:7",
    "    #4 0x7ffff7a6dd8f in __libc_start_call_main csu/../sysdeps/nptl/libc_start_call_main.h:58:16",
    "    #5 0x7ffff7a6de3f in __libc_start_main csu/../csu/libc-start.c:392:3",
    "    #6 0x555555580344 in _start (/tmp/tmpl2ejhjjc/reproducer+0x2c344) (BuildId: daac15d9abbe898ffb7df81cf847401973489f50)"
  ],
  "Registers": {},
  "Disassembly": [],
  "Package": "",
  "PackageVersion": "",
  "PackageArchitecture": "",
  "PackageDescription": "",
  "AsanReport": [
    "==156==ERROR: AddressSanitizer: stack-buffer-overflow on address 0x7ffff5f00101 at pc 0x55555565dde2 bp 0x7fffffffe930 sp 0x7fffffffe928",
    "READ of size 1 at 0x7ffff5f00101 thread T0",
    "    #0 0x55555565dde1 in websocket_parser_decode /fuzz/src/http/websocket_parser.c:176:18",
    "    #1 0x55555565c079 in on_frame_body(websocket_parser*, char const*, unsigned long) /fuzz/src/http/WebSocketParser.cpp:33:9",
    "    #2 0x55555565cce3 in websocket_parser_execute /fuzz/src/http/websocket_parser.c:146:25",
    "    #3 0x55555565b691 in main /tmp/tmpl2ejhjjc/reproducer.cpp:17:7",
    "    #4 0x7ffff7a6dd8f in __libc_start_call_main csu/../sysdeps/nptl/libc_start_call_main.h:58:16",
    "    #5 0x7ffff7a6de3f in __libc_start_main csu/../csu/libc-start.c:392:3",
    "    #6 0x555555580344 in _start (/tmp/tmpl2ejhjjc/reproducer+0x2c344) (BuildId: daac15d9abbe898ffb7df81cf847401973489f50)",
    "",
    "Address 0x7ffff5f00101 is located in stack of thread T0 at offset 257 in frame",
    "    #0 0x55555565b44f in main /tmp/tmpl2ejhjjc/reproducer.cpp:4",
    "",
    "  This frame has 3 object(s):",
    "    [32, 64) 'ref.tmp.i'",
    "    [96, 176) 'p' (line 5)",
    "    [208, 257) 'data' (line 10) <== Memory access at offset 257 overflows this variable",
    "HINT: this may be a false positive if your program uses some custom stack unwind mechanism, swapcontext or vfork",
    "      (longjmp and C++ exceptions *are* supported)",
    "SUMMARY: AddressSanitizer: stack-buffer-overflow /fuzz/src/http/websocket_parser.c:176:18 in websocket_parser_decode",
    "Shadow bytes around the buggy address:",
    "  0x7ffff5effe80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00",
    "  0x7ffff5efff00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00",
    "  0x7ffff5efff80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00",
    "  0x7ffff5f00000: f1 f1 f1 f1 f8 f8 f8 f8 f2 f2 f2 f2 00 00 00 00",
    "  0x7ffff5f00080: 00 00 00 00 00 00 f2 f2 f2 f2 00 00 00 00 00 00",
    "=>0x7ffff5f00100:[01]f3 f3 f3 f3 f3 f3 f3 00 00 00 00 00 00 00 00",
    "  0x7ffff5f00180: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00",
    "  0x7ffff5f00200: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00",
    "  0x7ffff5f00280: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00",
    "  0x7ffff5f00300: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00",
    "  0x7ffff5f00380: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00",
    "Shadow byte legend (one shadow byte represents 8 application bytes):",
    "  Addressable:           00",
    "  Partially addressable: 01 02 03 04 05 06 07",
    "  Heap left redzone:       fa",
    "  Freed heap region:       fd",
    "  Stack left redzone:      f1",
    "  Stack mid redzone:       f2",
    "  Stack right redzone:     f3",
    "  Stack after return:      f5",
    "  Stack use after scope:   f8",
    "  Global redzone:          f9",
    "  Global init order:       f6",
    "  Poisoned by user:        f7",
    "  Container overflow:      fc",
    "  Array cookie:            ac",
    "  Intra object redzone:    bb",
    "  ASan internal:           fe",
    "  Left alloca redzone:     ca",
    "  Right alloca redzone:    cb",
    "==156==ABORTING"
  ],
  "MsanReport": [],
  "UbsanReport": [],
  "LuaReport": [],
  "PythonReport": [],
  "GoReport": [],
  "JavaReport": [],
  "RustReport": [],
  "JsReport": [],
  "CSharpReport": [],
  "CrashLine": "/fuzz/src/http/websocket_parser.c:176:18",
  "Source": [
    "    172    ",
    "    173    void websocket_parser_decode(char * dst, const char * src, size_t len, websocket_parser * parser) {",
    "    174        size_t i = 0;",
    "    175        for(; i < len; i++) {",
    "--->176            dst[i] = src[i] ^ parser->mask[(i + parser->mask_offset) % 4];",
    "    177        }",
    "    178    ",
    "    179        parser->mask_offset = (uint8_t) ((i + parser->mask_offset) % 4);",
    "    180    }",
    "    181    "
  ]
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions