TheMrEvil's picture
Upload README.md with huggingface_hub
2f3ef6f verified
|
Raw
History Blame Contribute Delete
3.95 kB
# OpenCV TFLite importer out-of-bounds read in parseTensor
This repository contains a proof-of-concept malicious TFLite model that crashes OpenCV's
`cv2.dnn.readNetFromTFLite` 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. Notably, the file is a **structurally valid** TFLite flatbuffer: it passes
OpenCV's FlatBuffers verifier, because the verifier checks structure, not the consistency between a
tensor's declared shape and its buffer length.
## 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.readNetFromTFLite(path)`.
## Root cause
`modules/dnn/src/tflite/tflite_importer.cpp`, `TFLiteImporter::parseTensor`. The importer first runs
the FlatBuffers verifier (`VerifyModelBuffer`), then for each tensor reads its shape and wraps the
tensor's buffer in a `cv::Mat` without validating that the buffer byte size matches the declared
element count:
```cpp
std::vector<int> shape(tensor.shape()->begin(), tensor.shape()->end());
const Buffer* buffer = model->buffers()->Get(tensor.buffer());
const void* data = buffer->data()->data();
...
Mat res = Mat(shape, dtype, const_cast<void*>(data)); // no shape-vs-buffer-size check
```
The verifier only validates flatbuffer structure (offsets/vectors are internally consistent), not
that a tensor's declared shape matches its buffer length. A constant tensor whose shape declares far
more elements than its buffer holds therefore passes verification, and when the tensor is consumed
during layer construction the `Mat` is read/copied over its declared (huge) element count, reading
off the end of the small buffer.
## Proof of concept
`poc.tflite` is a valid one-Dense-layer model whose weight tensor shape is changed from `[8, 4]` to
`[134217728, 4]` while its buffer still holds only 32 floats. The change is a single int32 value
inside the shape vector, so the flatbuffer stays structurally valid and the verifier passes. Loading
the model makes the importer read ~134M*4 elements from the 32-float buffer.
```
pip install opencv-python # to reproduce (verify.py)
python verify.py # loads poc.tflite in a child process; reports the crash
# to regenerate poc.tflite from scratch you also need tensorflow:
pip install tensorflow && python make_poc.py
```
Observed on opencv-python 4.13.0 (Linux): the child process terminates with SIGSEGV (exit -11)
inside `readNetFromTFLite`, before any inference. On Windows it terminates with an access violation
(`0xC0000005`). A non-vulnerable build would load the model or reject it cleanly.
## Impact
Any application that loads an untrusted or attacker-supplied `.tflite` through OpenCV is exposed.
Loading third-party TFLite models is a normal, documented use of this API, and the malicious file
passes the importer's own verifier, so a "verify before use" defense does not help. A ~1 KB 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 read into the tensor's Mat, the read can also disclose adjacent
process heap memory.
## Fix
In `parseTensor`, after reading the shape and buffer, validate that
`buffer->data()->size() == shape_product * elemSize` (and reject the model otherwise) before
constructing the `Mat`.
## Relation to the other OpenCV importer issues
This is a distinct vulnerability from the OpenCV Caffe importer (`caffe_importer.cpp` `blobFromProto`)
and ONNX importer (`onnx_graph_simplifier.cpp` `getMatFromTensor`) out-of-bounds issues: a different
importer, a different function (`parseTensor` in `tflite_importer.cpp`), and a different model format
(TFLite). It is also notable in that the malicious model passes the importer's FlatBuffers verifier.