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:

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.