[CDRIVER-4421] Heap-Buffer-Overflow Bug Report, Fuzzing suggestion Created: 05/Jul/22  Updated: 27/Oct/23  Resolved: 29/Jul/22

Status: Closed
Project: C Driver
Component/s: BSON
Affects Version/s: 1.9.0
Fix Version/s: None

Type: Bug Priority: Unknown
Reporter: osscontact af Assignee: Ezra Chung
Resolution: Works as Designed Votes: 0
Labels: None
Remaining Estimate: Not Specified
Time Spent: Not Specified
Original Estimate: Not Specified

Issue Links:
Related
is related to CDRIVER-2455 Off by one error calculated required ... Closed

 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;
} 

 

 

 



 Comments   
Comment by Ezra Chung [ 29/Jul/22 ]

After further investigation, given this bug was addressed by CDRIVER-2455, patched in version 1.9.1, and libbson version 1.9 is not being maintained, a CVE number will not be created for this bug as requested.

Comment by Ezra Chung [ 14/Jul/22 ]

Apologies: I understand that the commit reference was to the archived mongodb/libbson repository which is used by libmongoc version 1.9.0, which you've assigned under the "Affects Version/s" field.

Comment by Ezra Chung [ 14/Jul/22 ]

Hello, afosscontact@gmail.com.

The stated commit reference "ffc8d983ecf6b46d5404f5cc20e756a85dfcbfd2" does not exist in the mongo-c-driver.

Can you please confirm what version of libbson you are testing again that produces this behavior?

Comment by Kevin Albertson [ 10/Jul/22 ]

Thank you for the report afosscontact@gmail.com. We will investigate this soon.

Comment by osscontact af [ 06/Jul/22 ]

Fuzzer Code with License:

// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
 
/*
 * This fuzzer is generated by UTopia project based on TEST(Test_Tensorflow, read_inception).
 * (UTopia Project: https://github.com/Samsung/UTopia)
 */
#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]);
  } 

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