YAML Metadata Warning:empty or missing yaml metadata in repo card

Check out the documentation for more information.

Caffe .caffemodel InnerProductLayer OOB Read PoC

Vulnerability Summary

Field Value
Library Caffe (huntr MFV $1,500)
Format .caffemodel (Protocol Buffers)
Bug class Heap-buffer-overflow READ (CWE-125)
Root cause InnerProductLayer::Forward_cpu accesses blobs_[1] (bias) when bias_term=true (default), but the .caffemodel only provides 1 blob (weights). LayerSetUp skips initialization if blobs_.size() > 0 without verifying blob count matches layer requirements.
Sink blobs_[1]->cpu_data() at inner_product_layer.cpp:88 โ€” reads past end of blobs_ vector
Impact Heap OOB read โ†’ info leak or crash; attacker-controlled .caffemodel file triggers it

Root Cause Analysis

Caffe's layer loading flow (include/caffe/layer.hpp:44-49):

// Layer constructor โ€” loads ALL blobs from protobuf without count validation
if (layer_param.blobs_size() > 0) {
    blobs_.resize(layer_param.blobs_size());
    for (int i = 0; i < layer_param.blobs_size(); ++i) {
        blobs_[i].reset(new Blob<Dtype>());
        blobs_[i]->FromProto(layer_param.blobs(i));
    }
}

Then InnerProductLayer::LayerSetUp (inner_product_layer.cpp:23):

// If ANY blobs were provided, skip initialization entirely
if (this->blobs_.size() > 0) {
    return;  // Does NOT verify blobs_.size() == (bias_term ? 2 : 1)
}

Then Forward_cpu (inner_product_layer.cpp:88):

if (bias_term_) {
    const Dtype* bias = blobs_[1]->cpu_data();  // OOB when blobs_.size() == 1
    // ...
}

The gap: Caffe trusts that the .caffemodel protobuf provides the correct number of blobs for each layer's configuration. No validation bridges the gap between "some blobs present" and "the right number of blobs present."

Build & Run

Requirements: g++ (C++17), protoc, libprotobuf-dev, python3, AddressSanitizer.

chmod +x build_and_run.sh
./build_and_run.sh

Or manually:

# 1. Compile caffe.proto
protoc --proto_path=../caffe-real/src --cpp_out=. ../caffe-real/src/caffe/proto/caffe.proto
g++ -std=c++17 -I. -c caffe/proto/caffe.pb.cc -o caffe.pb.o

# 2. Build PoC with ASan
g++ -std=c++17 -DNDEBUG -O1 -fsanitize=address -fno-omit-frame-pointer -g \
    -I. poc.cc caffe.pb.o -lprotobuf -o poc_asan

# 3. Run
./poc_asan

ASan Trace (key lines)

==PID==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x... at pc 0x...
READ of size 8 at 0x... thread T0
    #0 InnerProductLayer<float>::Forward_cpu(...) poc.cc:170
    #1 LoadAndProcessCaffemodel(...) poc.cc:304
    #2 main poc.cc:337

0x... is located 0 bytes after 8-byte region [0x...,0x...)
allocated by thread T0 here:
    #0 operator new(unsigned long)
    ...
    #5 std::vector<Blob<float>*>::resize(...)
    #6 InnerProductLayer<float>::InnerProductLayer(...) poc.cc:87

The 8-byte allocation = 1 Blob<float>* pointer. ASan reports the read at byte 8 (0 bytes past end) when accessing blobs_[1].

Files

File Purpose
poc.cc Self-contained PoC: creates malicious protobuf in-memory, loads it, triggers OOB
load_poc.cc File-loading variant: reads a pre-crafted .caffemodel from disk
gen_malicious_caffemodel.py Python script to generate malicious .caffemodel using raw protobuf wire format
build_and_run.sh One-command build and execution
Makefile Alternative build system
caffe/ Compiled protobuf C++ bindings

Fix Recommendation

In InnerProductLayer::LayerSetUp, validate blob count against layer requirements:

if (this->blobs_.size() > 0) {
    const int expected = bias_term_ ? 2 : 1;
    if (static_cast<int>(this->blobs_.size()) != expected) {
        LOG(FATAL) << "InnerProductLayer: expected " << expected
                   << " blobs but got " << this->blobs_.size();
    }
    return;
}

This pattern likely affects other Caffe layer types with similar "skip init if blobs provided" logic.

Downloads last month

-

Downloads are not tracked for this model. How to track
Inference Providers NEW
This model isn't deployed by any Inference Provider. ๐Ÿ™‹ Ask for provider support