| # OpenCV FileStorage `decodeFormat` stack buffer overflow (CWE-787) PoC |
|
|
| Proof of concept for a **stack buffer overflow / out-of-bounds write** in OpenCV's `FileStorage` |
| data-type parser, triggered when a `Mat` is read from a crafted `.json` or `.yml` model file. |
|
|
| This repository is **gated**: the crafted files are weaponized inputs. Access is pre-granted to |
| `protectai-bot` for huntr review. |
|
|
| ## What it is |
|
|
| `modules/core/src/persistence.cpp` β `decodeFormat(const char* dt, int* fmt_pairs, int max_len)` |
| writes one `(count, depth)` int pair per type token of the `dt` string into `fmt_pairs`. It does |
| `max_len *= 2` and only stops when the index reaches `2 * 128 = 256` ints. But two callers declare |
| the destination at the 1x size: |
|
|
| | caller | line | buffer | |
| |---|---|---| |
| | `decodeSimpleFormat()` | persistence.cpp:319 | `int fmt_pairs[CV_FS_MAX_FMT_PAIRS]` (128) | |
| | `calcElemSize()` | persistence.cpp:269 | `int fmt_pairs[CV_FS_MAX_FMT_PAIRS]` (128) | |
| | `writeRawData` / `readRaw` / `parseBase64` | 1140 / 1838 / 2645 | `[CV_FS_MAX_FMT_PAIRS * 2]` (256) β | |
|
|
| `CV_FS_MAX_FMT_PAIRS = 128`. A `dt` with more than 64 type pairs overflows the 128-int stack buffer |
| by up to ~512 bytes. The overflowing values are the pair counts, parsed with `strtol(dt)`, so they |
| are attacker-controlled. |
|
|
| `decodeSimpleFormat()` is reached at load time from the matrix reader |
| `read(const FileNode&, Mat&)` (`persistence_types.cpp:130`) on the file's `dt` field, **before** |
| any validation. |
|
|
| ## Files |
|
|
| - `poc.json`, `poc.yml` β 1x1 matrix node `x` with a 120-pair `dt` (each count = `0x41414141`). |
| - `benign.json`, `benign.yml` β same files with a normal `dt`; load cleanly. |
| - `make_poc.py` β regenerates the files from a cv2-written valid skeleton. |
| - `verify.py` β child-process differential runner. |
| - `asan_repro.cpp`, `asan_report.txt` β standalone AddressSanitizer reproducer (the exact source of |
| `decodeFormat`/`decodeSimpleFormat`) and its report, deterministically showing |
| `stack-buffer-overflow WRITE of size 4`. |
| - `linux_verify.sh` β one-shot driver. |
|
|
| ## Reproduce (Linux, `pip install opencv-python` β€ 4.13.0) |
|
|
| ``` |
| python make_poc.py |
| python verify.py |
| # benign.json rc=0 loaded ok |
| # poc.json rc=-11 CRASH (SIGSEGV) |
| # poc.yml rc=-11 CRASH (SIGSEGV) |
| |
| clang++ -O0 -g -fsanitize=address asan_repro.cpp -o asan_repro && ./asan_repro 120 |
| # AddressSanitizer: stack-buffer-overflow WRITE of size 4 in decodeFormat |
| ``` |
|
|
| ## Impact |
|
|
| Reading a `Mat` from an untrusted FileStorage file (directly, or via the `cv2.ml` model loaders |
| that persist as FileStorage) performs a controllable out-of-bounds stack write. Reliable outcome is |
| a process crash (DoS); ceiling is memory corruption / control-flow hijack subject to stack-protector |
| and ASLR. |
|
|