Skip to content

Out-of-bounds write in htp_base64_decode #458

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

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions