Summary
Heap-Buffer-Overflow Bug in libbson.
Recommendations
2 suggestions for security improvements.
[1] Request CVE number for this bug to inform users to prevent potential security vulnerabilities.
[2] Register below fuzzer code to ossfuzz for continuous fuzzing.
Environment
- Commit: ffc8d983ecf6b46d5404f5cc20e756a85dfcbfd2
- Operating System / Platform : ubuntu 18.04
- Compiler : clang++ 10.0.1
How to Reproduce
#include "bson/bson.h"
|
#include <string>const std::string input1 {
|
0x0e, 0x00, 0x00, 0x00, 0x03, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,
|
0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,
|
0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,
|
0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,
|
0x0e, 0x0e, (char)0xff, (char)0xff, (char)0xff, (char)0xff, (char)0xff,
|
(char)0xff, (char)0xff, (char)0xff, (char)0xff, (char)0xff, (char)0xff,
|
(char)0xff, (char)0xff, (char)0xff, (char)0xff, (char)0xff, (char)0xff,
|
(char)0xff, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,
|
0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, (char)0x94, 0x0e, 0x0e, 0x0e,
|
0x0e, 0x0e, 0x0e, 0x0e, 0x0e, (char)0x9a, (char)0xa3, (char)0xa3, (char)0xa3,
|
(char)0xa3, (char)0xa3, 0x2d, (char)0xa3, 0x00, 0x00, 0x00, 0x03 };
|
const std::string input2 = "";
|
const std::string input3 = "";int main(int argc, char * argv[]) {
|
bson_t *b = 0; b = bson_new();
|
if (!b) goto out; if (!bson_append_regex(
|
b, input1.c_str(), input1.size(), input2.c_str(), input3.c_str()))
|
goto out;out:
|
if (b) bson_destroy(b);
|
return 0;
|
}
|
Build Steps
mkdir -p build && cd build
|
cmake -DBUILD_SHARED_LIBS=OFF -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ -DCMAKE_AR=/bin/llvm-ar -DCMAKE_VERBOSE_MAKEFILE=ON -DCMAKE_C_FLAGS=-g -DCMAKE_CXX_FLAGS=-g -DCMAKE_EXE_LINKER_FLAGS=-g -DCMAKE_SHARED_LINKER_FLAGS=-g ..
|
make -j16 V=1
|
Additional Background
Reason
// bson-private.h:57
|
#define BSON_INLINE_DATA_SIZE 120BSON_ALIGNED_BEGIN (128)
|
typedef struct {
|
bson_flags_t flags;
|
uint32_t len;
|
uint8_t data[BSON_INLINE_DATA_SIZE]; // data size is 120
|
} bson_impl_inline_t BSON_ALIGNED_END (128);// bson.c:379
|
static bool
|
_bson_append (bson_t *bson, // bson=0x60c000000100 : {flags = 1, len = 5, padding = "\005\000\000\000\000", '\276' <repeats 115 times>}
|
uint32_t n_pairs, // n_pairs=5
|
uint32_t n_bytes, // n_bytes=115
|
uint32_t first_len, // first_len=1
|
const uint8_t *first_data, // first_data=0x536b60 : '\v'
|
...)
|
{
|
va_list args;
|
bool ok; BSON_ASSERT (n_pairs);
|
BSON_ASSERT (first_len);
|
BSON_ASSERT (first_data); if (BSON_UNLIKELY (n_bytes > (BSON_MAX_SIZE - bson->len))) {
|
return false;
|
} va_start (args, first_data);
|
ok = _bson_append_va (bson, n_bytes, n_pairs, first_len, first_data, args); // Called
|
va_end (args); return ok;
|
}...// bson.c:310
|
static BSON_INLINE bool
|
_bson_append_va (bson_t *bson, // bson=0x60c000000100 : {flags = 1, len = 121, padding = "y\000\000\000\v\016\000\000\000\003", '\016' <repeats 49 times>, '\377' <repeats 18 times>, '\016' <repeats 19 times>, "\224\016\016\016\016\016\016\016\016\232\243\243\243\243\243-\243\000\000\000\003\000\000"}
|
uint32_t n_bytes, // n_bytes=115
|
uint32_t n_pairs, // n_pairs=5
|
uint32_t first_len, // first_len=1
|
const uint8_t *first_data, // first_data=0x536b60 : "\v"
|
va_list args) // args: { gp_offset = 40, fp_offset = 48, overflow_arg_area = 0x7fffffffe3f0, reg_save_area = 0x7fffffffe2b0 }
|
{
|
const uint8_t *data;
|
uint32_t data_len;
|
uint8_t *buf; BSON_ASSERT (!(bson->flags & BSON_FLAG_IN_CHILD));
|
BSON_ASSERT (!(bson->flags & BSON_FLAG_RDONLY)); if (BSON_UNLIKELY (!_bson_grow (bson, n_bytes))) {
|
return false;
|
} data = first_data;
|
data_len = first_len; buf = _bson_data (bson) + bson->len - 1; // _bson_data : "\005\000\000\000\000", '\276' <repeats 115 times>, bson->len : 5 do {
|
n_pairs--;
|
memcpy (buf, data, data_len); // *data : "\v", data_len : 1
|
bson->len += data_len;
|
buf += data_len; if (n_pairs) {
|
data_len = va_arg (args, uint32_t); // 112, 1, 1, 1
|
data = va_arg (args, const uint8_t *);
|
}
|
} while (n_pairs); *buf = '\0'; // Crashed, buf is over the range of bson->data.
|
// Because 5, 112, 1, 1, 1 size of data was assigned, but size of bson->data is 120. return true;
|
}
|
Address Sanitizer Report
=================================================================
|
==237==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x60c000000180 at pc 0x0000004dd932 bp 0x7fff8bf35a50 sp 0x7fff8bf35a48
|
WRITE of size 1 at 0x60c000000180 thread T0
|
#0 0x4dd931 in _bson_append_va /root/fuzz-test-generation/exp/libbson/src/bson/bson.c:348:9
|
#1 0x4c9fcb in _bson_append /root/fuzz-test-generation/exp/libbson/src/bson/bson.c:404:9
|
#2 0x4d1c7b in bson_append_regex /root/fuzz-test-generation/exp/libbson/src/bson/bson.c:1575:9
|
#3 0x4c6acf in main /root/fuzz-test-generation/AutoFuzz_Exp/archive/libbson/poc1.cpp:25:8
|
#4 0x7ff072c51c86 in __libc_start_main /build/glibc-CVJwZb/glibc-2.27/csu/../csu/libc-start.c:310
|
#5 0x41c299 in _start (/root/fuzz-test-generation/exp_musang/libbson/poc1+0x41c299)0x60c000000180 is located 0 bytes to the right of 128-byte region [0x60c000000100,0x60c000000180)
|
allocated by thread T0 here:
|
#0 0x4949dd in malloc (/root/fuzz-test-generation/exp_musang/libbson/poc1+0x4949dd)
|
#1 0x5092cf in bson_malloc /root/fuzz-test-generation/exp/libbson/src/bson/bson-memory.c:68:11
|
#2 0x4d56ac in bson_new /root/fuzz-test-generation/exp/libbson/src/bson/bson.c:2015:11
|
#3 0x4c6a52 in main /root/fuzz-test-generation/AutoFuzz_Exp/archive/libbson/poc1.cpp:22:7
|
#4 0x7ff072c51c86 in __libc_start_main /build/glibc-CVJwZb/glibc-2.27/csu/../csu/libc-start.c:310SUMMARY: AddressSanitizer: heap-buffer-overflow /root/fuzz-test-generation/exp/libbson/src/bson/bson.c:348:9 in _bson_append_va
|
Shadow bytes around the buggy address:
|
0x0c187fff7fe0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
0x0c187fff7ff0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
0x0c187fff8000: fa fa fa fa fa fa fa fa 00 00 00 00 00 00 00 00
|
0x0c187fff8010: 00 00 00 00 00 00 01 fa fa fa fa fa fa fa fa fa
|
0x0c187fff8020: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
=>0x0c187fff8030:[fa]fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
|
0x0c187fff8040: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
|
0x0c187fff8050: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
|
0x0c187fff8060: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
|
0x0c187fff8070: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
|
0x0c187fff8080: 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
|
Shadow gap: cc
|
==237==ABORTING
|
Fuzzer Code
#include "Fuzzer/FuzzedDataProvider.h"
|
#include "bson/bson.h"
|
|
static void test(FuzzedDataProvider &provider) {
|
auto input1 = provider.ConsumeRandomLengthString();
|
auto input2 = provider.ConsumeRandomLengthString();
|
auto input3 = provider.ConsumeRandomLengthString();
|
|
for (int i = 0; i < input1.size(); ++i) {
|
printf("%02x ", input1[i]);
|
}
|
printf("1:\n\n");
|
|
for (int i = 0; i < input2.size(); ++i) {
|
printf("%02x ", input2[i]);
|
}
|
printf("2:\n\n");
|
|
for (int i = 0; i < input3.size(); ++i) {
|
printf("%02x ", input3[i]);
|
}
|
printf("3:\n\n");
|
|
bson_t *b = 0;
|
|
b = bson_new();
|
if (!b) goto out;
|
|
if (!bson_append_regex(
|
b, input1.c_str(), input1.size(), input2.c_str(), input3.c_str()))
|
goto out;
|
|
out:
|
if (b) bson_destroy(b);
|
}
|
|
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, uint32_t size) {
|
FuzzedDataProvider provider(data, size);
|
test(provider);
|
return 0;
|
}
|
|