TheMrEvil's picture
Upload README.md with huggingface_hub
432eda1 verified
|
Raw
History Blame Contribute Delete
3.58 kB
# OpenCV ONNX importer out-of-bounds read in getMatFromTensor
This repository contains a proof-of-concept malicious ONNX model that crashes OpenCV's
`cv2.dnn.readNetFromONNX` with an out-of-bounds read at model-load time (SIGSEGV).
It is a security PoC for a huntr Model File Format report. The model is intentionally malformed
and the repo is gated.
## Affected
- `opencv-python` / OpenCV `cv2.dnn`, confirmed on **4.13.0** (latest at time of writing), on Linux.
- The vulnerable code is unchanged on `master`.
- Entry point: `cv2.dnn.readNetFromONNX(path)` (also reachable via `readNetFromONNX` from a buffer).
## Root cause
`modules/dnn/src/onnx/onnx_graph_simplifier.cpp`, function `getMatFromTensor`. It reads a tensor's
dims into a `sizes` vector and copies the tensor bytes into a `cv::Mat` without validating that
`raw_data` actually holds that many elements:
```cpp
std::vector<int> sizes; // = tensor dims, attacker controlled
for (int i = 0; i < tensor_proto.dims_size(); i++)
sizes.push_back(tensor_proto.dims(i));
...
// FLOAT path
Mat(sizes, CV_32FC1, (void*)tensor_proto.raw_data().c_str()).copyTo(blob);
```
The source `Mat` is a header over the (tiny) `raw_data` buffer but carries the declared element
count. `copyTo` allocates the destination for the declared count and `memcpy`s that many bytes from
the source, reading far past the end of `raw_data`. There is no size check, and no
`CV_Assert(blob.isContinuous())` guard (the TensorFlow and Darknet importers have such guards; this
ONNX path does not, and it has no length assertion at all).
## Proof of concept
`poc.onnx` is an 80-byte model whose single FLOAT initializer declares `dims = [10000000]` but
carries only 16 bytes of `raw_data`. Loading it makes `getMatFromTensor` allocate a 40 MB
destination and copy ~40 MB from the 16-byte source, reading off the end of the heap buffer.
```
pip install opencv-python onnx numpy
python make_poc.py # writes poc.onnx
python verify.py # loads poc.onnx in a child process; reports the crash
```
Observed on opencv-python 4.13.0 (Linux): the child process terminates with SIGSEGV (exit -11)
inside `readNetFromONNX`, before any inference. On Windows it terminates with an access violation
(`0xC0000005`). A non-vulnerable build would load the model or raise an ordinary exception.
## Impact
Any application that loads an untrusted or attacker-supplied `.onnx` through OpenCV is exposed.
Loading third-party ONNX models is a normal, documented use of this API. An 80-byte file reliably
crashes the process via an out-of-bounds read in native code (a denial of service), and because the
over-read bytes are copied into the model's weight blob, the read can also disclose adjacent process
heap memory (information leak).
## Fix
In `getMatFromTensor`, validate the tensor's declared element count against the actual size of the
data source before constructing the source `Mat` / calling `copyTo`, for every dtype path (FLOAT,
DOUBLE, FP16, INT32, INT64, raw_data). For example require
`raw_data.size() == elementCount * elemSize` (and the analogous check for the typed `*_data`
fields), and reject the model otherwise.
## Relation to the Caffe importer issue
This is a distinct vulnerability from the OpenCV Caffe importer out-of-bounds issue in
`caffe_importer.cpp` `blobFromProto`: a different importer, a different function
(`getMatFromTensor` in `onnx_graph_simplifier.cpp`), and a different model format (ONNX). It also
manifests differently, faulting on Linux via the source over-read rather than only on Windows.