YAML Metadata Warning:empty or missing yaml metadata in repo card
Check out the documentation for more information.
OpenCV FileStorage nd-matrix stack buffer overflow (CWE-787) PoC
A crafted OpenCV FileStorage JSON model/data file (poc.json) that triggers a stack-based
buffer overflow with attacker-controlled write values in OpenCV's persistence parser, reached from
a plain cv2.FileStorage(path, cv2.FILE_STORAGE_READ) read (and from any cv2.*::load /
cv2.ml.*_load that deserializes a matrix member).
This repository is gated so the crafted file is not freely downloadable. Access has been granted
to protectai-bot for huntr review.
Affected
- opencv-python 4.13.0 (latest), verified on Linux x86_64 / Python 3.13.
- The vulnerable code is unchanged on current OpenCV
master.
Root cause
modules/core/src/persistence_types.cpp, read(const FileNode&, Mat&, const Mat&) (current master
lines 129-134; identical twin in the SparseMat reader at 156-163):
int sizes[CV_MAX_DIM] = {0}, dims; // CV_MAX_DIM == 32 -> int sizes[32] on the stack
FileNode sizes_node = node["sizes"];
dims = (int)sizes_node.size(); // attacker-controlled count, NO upper bound
sizes_node.readRaw("i", sizes, dims*sizeof(sizes[0])); // writes `dims` ints into the 32-int buffer
m.create(dims, sizes, elem_type); // the CV_MAX_DIM check is inside create() -- runs too late
For a node with type_id opencv-nd-matrix, dims is taken straight from the number of entries in
the file's "sizes" array, and readRaw then writes that many ints into the fixed 32-int stack
buffer. There is no check that dims <= CV_MAX_DIM before the write. More than 32 entries overflows
the buffer; the written values are the "sizes" entries themselves, so the attacker controls the
bytes that land on the saved registers and return address.
Files
poc.json- the crafted file: anopencv-nd-matrixnode whose"sizes"array has 200 entries, each0x41414141.make_poc.py- regeneratespoc.jsonand documents the construction.verify.py- offline, deterministic check. Loads the file in a child process and runs a differential (a benign 32-dim matrix loads; the 200-dim file crashes). Only needsopencv-python.
Reproduce
pip install opencv-python
python verify.py
Expected:
[benign 32-dim nd-matrix] exit=0 loaded: (1, 1, ... 32 ones ...)
[crafted poc.json] exit=-11
RESULT: VULNERABLE - benign loads, crafted file crashes the parser (stack overflow)
Under a debugger the crafted file overwrites the saved return addresses with 0x4141414141414141
(the attacker-supplied "sizes" value), confirming the write is controlled rather than a blind DoS:
Thread 1 "python" received signal SIGSEGV, Segmentation fault.
#7 0x4141414141414141 in ?? ()
#8 0x4141414141414141 in ?? ()
...
Impact
Loading an untrusted OpenCV .json (or .xml/.yml) model/data file via FileStorage corrupts the
stack with attacker-controlled bytes that reach the saved return address. At minimum this is a
reliable native crash (denial of service) at load time; because the overwrite is attacker-controlled
it is a memory-corruption primitive (CWE-787) whose ceiling is control-flow hijack, subject to the
platform mitigations in effect.
Remediation
Validate dims against CV_MAX_DIM before the readRaw write in both read(FileNode, Mat&) and
read(FileNode, SparseMat&), e.g. CV_Assert(0 <= dims && dims <= CV_MAX_DIM); immediately after
dims = (int)sizes_node.size();.