Details
-
Bug
-
Resolution: Works as Designed
-
Unknown
-
None
-
1.9.0
-
None
Description
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; |
}
|
Attachments
Issue Links
- is related to
-
CDRIVER-2455 Off by one error calculated required size with bson_append_regex
-
- Closed
-