[CDRIVER-2819] Heap buffer overflow at libbson Created: 10/Sep/18  Updated: 28/Oct/23  Resolved: 17/Sep/18

Status: Closed
Project: C Driver
Component/s: libbson
Affects Version/s: 1.12.0
Fix Version/s: 1.13.0

Type: Bug Priority: Minor - P4
Reporter: Yibai Assignee: A. Jesse Jiryu Davis
Resolution: Fixed Votes: 0
Labels: None
Remaining Estimate: Not Specified
Time Spent: Not Specified
Original Estimate: Not Specified
Environment:

Ubuntu 14.04



 Description   

We tested the newest libbson from master at mongo-c-driver and found crash when parsing corrupted bson buffer.

base64 encoded payload: GAAAAAUOGS4ABAAAAAIAAAAAAAAFDgAAGAAAAAUOGS4ABAAAAAIAAAAAAAAFDgAAGAAAAAUAAAAAAAAFDhkuAAQAAAACAAAA

ASAN report:
==39647==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x607000070068 at pc 0x000000530cc2 bp 0x7ffdde442010 sp 0x7ffdde442008
READ of size 4 at 0x607000070068 thread T0
#0 0x530cc1 in _bson_iter_next_internal /home/xm1994/dive-test/sources/mongo-c-driver-1.12.0/src/libbson/src/bson/bson-iter.c:632:10
#1 0x536986 in bson_iter_visit_all /home/xm1994/dive-test/sources/mongo-c-driver-1.12.0/src/libbson/src/bson/bson-iter.c:1934:11
#2 0x51fcfd in _bson_as_json_visit_all /home/xm1994/dive-test/sources/mongo-c-driver-1.12.0/src/libbson/src/bson/bson.c:3158:8
#3 0x513076 in LLVMFuzzerTestOneInput /home/xm1994/dive-test/sources/mongo-c-driver-1.12.0/fuzz/fuzzer.c:42:13
#4 0x42d6ac in fuzzer::Fuzzer::ExecuteCallback(unsigned char const*, unsigned long) /local/mnt/workspace/clang_nightly/plain/llvm/utils/release/final/llvm.src/projects/compiler-rt/lib/fuzzer/FuzzerLoop.cpp:515:13
#5 0x42cf0b in fuzzer::Fuzzer::RunOne(unsigned char const*, unsigned long, bool, fuzzer::InputInfo*, bool*) /local/mnt/workspace/clang_nightly/plain/llvm/utils/release/final/llvm.src/projects/compiler-rt/lib/fuzzer/FuzzerLoop.cpp:440
:3
#6 0x42e8bd in fuzzer::Fuzzer::MutateAndTestOne() /local/mnt/workspace/clang_nightly/plain/llvm/utils/release/final/llvm.src/projects/compiler-rt/lib/fuzzer/FuzzerLoop.cpp:648:19
#7 0x42f505 in fuzzer::Fuzzer::Loop(std::vector<std::string, fuzzer::fuzzer_allocator<std::string> > const&) /local/mnt/workspace/clang_nightly/plain/llvm/utils/release/final/llvm.src/projects/compiler-rt/lib/fuzzer/FuzzerLoop.cpp:77
5:5
#8 0x424363 in fuzzer::FuzzerDriver(int*, char**, int (unsigned char const, unsigned long)) /local/mnt/workspace/clang_nightly/plain/llvm/utils/release/final/llvm.src/projects/compiler-rt/lib/fuzzer/FuzzerDriver.cpp:754:6
#9 0x445c82 in main /local/mnt/workspace/clang_nightly/plain/llvm/utils/release/final/llvm.src/projects/compiler-rt/lib/fuzzer/FuzzerMain.cpp:20:10
#10 0x7fa8b4624f44 in __libc_start_main /build/eglibc-ripdx6/eglibc-2.19/csu/libc-start.c:287
#11 0x41ce7b in _start (/home/xm1994/dive-test/sources/mongo-c-driver-1.12.0/fuzz/fuzzer+0x41ce7b)

0x607000070068 is located 0 bytes to the right of 72-byte region [0x607000070020,0x607000070068)
allocated by thread T0 here:
#0 0x4e62e3 in __interceptor_malloc /local/mnt/workspace/clang_nightly/plain/llvm/utils/release/final/llvm.src/projects/compiler-rt/lib/asan/asan_malloc_linux.cc:88:3
#1 0x7fa8b5580697 in operator new(unsigned long) (/usr/lib/x86_64-linux-gnu/libstdc++.so.6+0x6c697)
#2 0x42cf0b in fuzzer::Fuzzer::RunOne(unsigned char const*, unsigned long, bool, fuzzer::InputInfo*, bool*) /local/mnt/workspace/clang_nightly/plain/llvm/utils/release/final/llvm.src/projects/compiler-rt/lib/fuzzer/FuzzerLoop.cpp:440
:3
#3 0x42e8bd in fuzzer::Fuzzer::MutateAndTestOne() /local/mnt/workspace/clang_nightly/plain/llvm/utils/release/final/llvm.src/projects/compiler-rt/lib/fuzzer/FuzzerLoop.cpp:648:19
#4 0x42f505 in fuzzer::Fuzzer::Loop(std::vector<std::string, fuzzer::fuzzer_allocator<std::string> > const&) /local/mnt/workspace/clang_nightly/plain/llvm/utils/release/final/llvm.src/projects/compiler-rt/lib/fuzzer/FuzzerLoop.cpp:77
5:5
#5 0x424363 in fuzzer::FuzzerDriver(int*, char**, int (unsigned char const, unsigned long)) /local/mnt/workspace/clang_nightly/plain/llvm/utils/release/final/llvm.src/projects/compiler-rt/lib/fuzzer/FuzzerDriver.cpp:754:6
#6 0x445c82 in main /local/mnt/workspace/clang_nightly/plain/llvm/utils/release/final/llvm.src/projects/compiler-rt/lib/fuzzer/FuzzerMain.cpp:20:10
#7 0x7fa8b4624f44 in __libc_start_main /build/eglibc-ripdx6/eglibc-2.19/csu/libc-start.c:287

SUMMARY: AddressSanitizer: heap-buffer-overflow /home/xm1994/dive-test/sources/mongo-c-driver-1.12.0/src/libbson/src/bson/bson-iter.c:632:10 in _bson_iter_next_internal
 



 Comments   
Comment by Githook User [ 17/Sep/18 ]

Author:

{'name': 'Scott Gayou', 'email': 'sgayou@redhat.com'}

Message: Fix for CVE-2018-16790 – Verify bounds before binary length read.

As reported here: https://jira.mongodb.org/browse/CDRIVER-2819,
a heap overread occurs due a failure to correctly verify data
bounds.

In the original check, len - o returns the data left including the
sizeof(l) we just read. Instead, the comparison should check
against the data left NOT including the binary int32, i.e. just
subtype (byte*) instead of int32 subtype (byte*).

Added in test for corrupted BSON example.
Branch: master
https://github.com/mongodb/mongo-c-driver/commit/0d9a4d98bfdf4acd2c0138d4aaeb4e2e0934bd84

Comment by Scott Gayou [ 14/Sep/18 ]

Took a stab at a fix here: https://github.com/mongodb/mongo-c-driver/pull/537

Parsing code is dense, so this may be incorrect. Could use some additional eyes.

Fix removes Valgrind/ASAN errors on my box.

Comment by Scott Gayou [ 14/Sep/18 ]

Looks like we're running off the end of a binary buffer.

Reproducer that triggers the ASAN error can be reduced to:

{0x11, 0x0, 0x0, 0x0, 0x5, 0xe, 0x19, 0x2e, 0x0, 0x4, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0}

which I believe decodes to:

0x11, 0x0, 0x0, 0x0 – Length of document is 0x11 (17) bytes
0x5, – binary data
0xe, 0x19, 0x2e, 0x0 – cstr
0x4, 0x0, 0x0, 0x0 – length of binary element (4)
0x2, – binary subtype (Binary (Old))
0x0, 0x0, 0x0, (Missing final byte of duplicated length)

Thus, if we change the final binary length to a 3 instead of a 4, we no longer trigger the out of bounds read.

Looking at the code now...

 

Comment by Petr Pisar [ 14/Sep/18 ]

Yes. I can reproduce it with libbson-1.9.5 https://bugzilla.redhat.com/show_bug.cgi?id=1627923#c3.

Comment by A. Jesse Jiryu Davis [ 13/Sep/18 ]

Probably affects libbson before 1.12, since the bson_iter_next logic hasn't changed in a while.

Comment by A. Jesse Jiryu Davis [ 10/Sep/18 ]

Thanks for your report!

Comment by Yibai [ 10/Sep/18 ]

 

LLVM Fuzzer Code here:

#include <bson/bson.h>
#include <stdio.h>
 
int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size)
{
   bson_reader_t *reader;
   const bson_t *b;
   bson_error_t error;
   const char *filename;
   char *str;
   int i;   reader = bson_reader_new_from_data (Data, Size);
   while ((b = bson_reader_read (reader, NULL))) {
      str = bson_as_canonical_extended_json (b, NULL);
      //fprintf (stdout, "%s\n", str);
      bson_free (str);
   }
   bson_reader_destroy (reader);   return 0;
}

 

Backtrace and ASAN info Here:

==243070==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x607000000228 at pc 0x000000530b26 bp 0x7ffcd695fdd0 sp 0x7ffcd695fdc8
READ of size 4 at 0x607000000228 thread T0
    #0 0x530b25 in _bson_iter_next_internal /home/xm1994/dive-test/sources/mongo-c-driver-master/src/libbson/src/bson/bson-iter.c:637:10
    #1 0x536788 in bson_iter_visit_all /home/xm1994/dive-test/sources/mongo-c-driver-master/src/libbson/src/bson/bson-iter.c:1904:11
    #2 0x51fcfd in _bson_as_json_visit_all /home/xm1994/dive-test/sources/mongo-c-driver-master/src/libbson/src/bson/bson.c:3158:8
    #3 0x513076 in LLVMFuzzerTestOneInput /home/xm1994/dive-test/sources/mongo-c-driver-master/fuzz/fuzzer.c:42:13
 
0x607000000228 is located 0 bytes to the right of 72-byte region [0x6070000001e0,0x607000000228)
allocated by thread T0 here:
    #0 0x4e62e3 in __interceptor_malloc /local/mnt/workspace/clang_nightly/plain/llvm/utils/release/final/llvm.src/projects/compiler-rt/lib/asan/asan_malloc_linux.cc:88:3
    #1 0x7f61eba74697 in operator new(unsigned long) (/usr/lib/x86_64-linux-gnu/libstdc++.so.6+0x6c697)
    #2 0x41d692 in fuzzer::RunOneTest(fuzzer::Fuzzer*, char const*, unsigned long) /local/mnt/workspace/clang_nightly/plain/llvm/utils/release/final/llvm.src/projects/compiler-rt/lib/fuzzer/FuzzerDriver.cpp:280:6
 
SUMMARY: AddressSanitizer: heap-buffer-overflow /home/xm1994/dive-test/sources/mongo-c-driver-master/src/libbson/src/bson/bson-iter.c:637:10 in _bson_iter_next_internal
Shadow bytes around the buggy address:
  0x0c0e7fff7ff0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c0e7fff8000: fa fa fa fa 00 00 00 00 00 00 00 00 07 fa fa fa
  0x0c0e7fff8010: fa fa 00 00 00 00 00 00 00 00 00 fa fa fa fa fa
  0x0c0e7fff8020: fd fd fd fd fd fd fd fd fd fa fa fa fa fa 00 00
  0x0c0e7fff8030: 00 00 00 00 00 00 00 fa fa fa fa fa 00 00 00 00
=>0x0c0e7fff8040: 00 00 00 00 00[fa]fa fa fa fa fa fa fa fa fa fa
  0x0c0e7fff8050: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c0e7fff8060: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c0e7fff8070: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c0e7fff8080: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c0e7fff8090: 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
==243070==ABORTING
 
// code placeholder

Generated at Wed Feb 07 21:16:26 UTC 2024 using Jira 9.7.1#970001-sha1:2222b88b221c4928ef0de3161136cc90c8356a66.