Skip to content

Out-of-bounds write in htp_base64_decode #458

@hgarrereyn

Description

@hgarrereyn

Hi, there is a potential bug in htp_base64_decode reachable when provided an undersized output buffer.

This bug was reproduced on 314ca73.

Description

Currently although htp_base64_decode takes an output buffer and output size, it performs size validation after writing beyond the buffer, leading to the potential for a partial out-of-bounds write.

See the provided POC.

POC

The following testcase demonstrates the bug:

testcase.cpp

#include <vector>
extern "C" {
#include "/fuzz/install/include/htp/htp_base64.h"
}
int main() {
    htp_base64_decoder dec;
    htp_base64_decoder_init(&dec);
    const char *in = "AAAA"; // valid base64, decodes to 3 bytes
    int inlen = 4;
    std::vector<unsigned char> out(1); // intentionally too small
    (void)htp_base64_decode(&dec, in, inlen, out.data(), (int)out.size());
    return 0;
}

stdout


stderr

=================================================================
==1==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x502000000011 at pc 0x55872a7df11d bp 0x7fffb45a07e0 sp 0x7fffb45a07d8
WRITE of size 1 at 0x502000000011 thread T0
    #0 0x55872a7df11c in htp_base64_decode /fuzz/src/htp/htp_base64.c:119:28
    #1 0x55872a7dd631 in main /fuzz/testcase.cpp:11:11
    #2 0x7efccc8e5d8f in __libc_start_call_main csu/../sysdeps/nptl/libc_start_call_main.h:58:16
    #3 0x7efccc8e5e3f in __libc_start_main csu/../csu/libc-start.c:392:3
    #4 0x55872a702384 in _start (/fuzz/test+0x2c384) (BuildId: f06805ea7c339de8e579495f6b066dbe3db9980a)

0x502000000011 is located 0 bytes after 1-byte region [0x502000000010,0x502000000011)
allocated by thread T0 here:
    #0 0x55872a7db19d in operator new(unsigned long) (/fuzz/test+0x10519d) (BuildId: f06805ea7c339de8e579495f6b066dbe3db9980a)
    #1 0x55872a7de3a0 in __gnu_cxx::new_allocator<unsigned char>::allocate(unsigned long, void const*) /usr/bin/../lib/gcc/x86_64-linux-gnu/11/../../../../include/c++/11/ext/new_allocator.h:127:27
    #2 0x55872a7de330 in std::allocator_traits<std::allocator<unsigned char>>::allocate(std::allocator<unsigned char>&, unsigned long) /usr/bin/../lib/gcc/x86_64-linux-gnu/11/../../../../include/c++/11/bits/alloc_traits.h:464:20
    #3 0x55872a7de2ef in std::_Vector_base<unsigned char, std::allocator<unsigned char>>::_M_allocate(unsigned long) /usr/bin/../lib/gcc/x86_64-linux-gnu/11/../../../../include/c++/11/bits/stl_vector.h:346:20
    #4 0x55872a7de110 in std::_Vector_base<unsigned char, std::allocator<unsigned char>>::_M_create_storage(unsigned long) /usr/bin/../lib/gcc/x86_64-linux-gnu/11/../../../../include/c++/11/bits/stl_vector.h:361:33
    #5 0x55872a7ddbd1 in std::_Vector_base<unsigned char, std::allocator<unsigned char>>::_Vector_base(unsigned long, std::allocator<unsigned char> const&) /usr/bin/../lib/gcc/x86_64-linux-gnu/11/../../../../include/c++/11/bits/stl_vector.h:305:9
    #6 0x55872a7dd838 in std::vector<unsigned char, std::allocator<unsigned char>>::vector(unsigned long, std::allocator<unsigned char> const&) /usr/bin/../lib/gcc/x86_64-linux-gnu/11/../../../../include/c++/11/bits/stl_vector.h:511:9
    #7 0x55872a7dd5da in main /fuzz/testcase.cpp:10:32
    #8 0x7efccc8e5d8f in __libc_start_call_main csu/../sysdeps/nptl/libc_start_call_main.h:58:16

SUMMARY: AddressSanitizer: heap-buffer-overflow /fuzz/src/htp/htp_base64.c:119:28 in htp_base64_decode
Shadow bytes around the buggy address:
  0x501ffffffd80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x501ffffffe00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x501ffffffe80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x501fffffff00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x501fffffff80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0x502000000000: fa fa[01]fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x502000000080: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x502000000100: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x502000000180: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x502000000200: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x502000000280: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
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
==1==ABORTING

Steps to Reproduce

The crash was triaged with the following Dockerfile:

Dockerfile

# Ubuntu 22.04 with some packages pre-installed
FROM hgarrereyn/stitch_repro_base@sha256:3ae94cdb7bf2660f4941dc523fe48cd2555049f6fb7d17577f5efd32a40fdd2c

RUN git clone https://github.com/OISF/libhtp.git /fuzz/src && \
    cd /fuzz/src && \
    git checkout 314ca7360e141a1e40be58707b3abeefe32258c9 && \
    git submodule update --init --remote --recursive

ENV LD_LIBRARY_PATH=/fuzz/install/lib
ENV ASAN_OPTIONS=hard_rss_limit_mb=1024:detect_leaks=0

RUN echo '#!/bin/bash\nexec clang-17 -fsanitize=address -O0 "$@"' > /usr/local/bin/clang_wrapper && \
    chmod +x /usr/local/bin/clang_wrapper && \
    echo '#!/bin/bash\nexec clang++-17 -fsanitize=address -O0 "$@"' > /usr/local/bin/clang_wrapper++ && \
    chmod +x /usr/local/bin/clang_wrapper++

RUN apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
    autoconf automake libtool pkg-config zlib1g-dev \
 && rm -rf /var/lib/apt/lists/*

WORKDIR /fuzz/src

# Generate autotools, configure with fuzzing compilers, build static if possible, install
RUN chmod +x autogen.sh && \
    ./autogen.sh && \
    CC=clang_wrapper CXX=clang_wrapper++ \
    ./configure --prefix=/fuzz/install --enable-static --disable-shared && \
    make -j"$(nproc)" && \
    make install

Build Command

clang++-17 -fsanitize=address -g -O0 -o /fuzz/test /fuzz/testcase.cpp -I/fuzz/install/include -L/fuzz/install/lib -lhtp -lz && /fuzz/test

Reproduce

  1. Copy Dockerfile and testcase.cpp into a local folder.
  2. Build the repro image:
docker build . -t repro --platform=linux/amd64
  1. Compile and run the testcase in the image:
docker run \
    -it --rm \
    --platform linux/amd64 \
    --mount type=bind,source="$(pwd)/testcase.cpp",target=/fuzz/testcase.cpp \
    repro \
    bash -c "clang++-17 -fsanitize=address -g -O0 -o /fuzz/test /fuzz/testcase.cpp -I/fuzz/install/include -L/fuzz/install/lib -lhtp -lz && /fuzz/test"


Additional Info

This testcase was discovered by STITCH, an autonomous fuzzing system. All reports are reviewed manually (by a human) before submission.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions