-
Type: Bug
-
Resolution: Works as Designed
-
Priority: Unknown
-
None
-
Affects Version/s: 1.9.0
-
Component/s: BSON
-
None
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; }
- is related to
-
CDRIVER-2455 Off by one error calculated required size with bson_append_regex
- Closed