Tensorizer .tensors Cross-Tensor OOB Read / Uninitialized Heap Disclosure β€” PoC

Security research PoC. Defensive disclosure only.

This repository hosts the proof-of-concept artifact for an information-disclosure vulnerability in coreweave/tensorizer (TensorDeserializer), submitted via huntr.com.

  • Weakness: CWE-125 (Out-of-bounds Read) + CWE-908 (Use of Uninitialized Resource)
  • Affected: tensorizer 2.12.1 (latest on PyPI), tensorizer/serialization.py, TensorDeserializer._bulk_load (~lines 3135–3180)
  • Impact: A malicious .tensors file makes the deserializer read past one tensor's real data into the adjacent tensor's header/weights and into uninitialized process heap, then returns that data as the loaded tensor.

⚠️ This PoC only demonstrates the over-read (it prints leaked floats). It contains no system-harming payload β€” no code execution, no file writes beyond the local working dir, no network access. The malicious file leak2.tensors is a benign two-tensor file whose size metadata has been patched to trigger the over-read.


What's in this repo

File Purpose
leak2.tensors The malicious artifact. Loading it returns a 27-element tensor for a (declared as 4 zeros) β€” its own 4 zeros, then secret's header bytes, then secret's real weights.
build_baseline.py Builds a clean two-tensor two.tensors baseline with TensorSerializer.
make_leak.py Parses two.tensors and patches 4 fields to produce leak2.tensors. Offset-independent (it re-parses the layout).
load_poc.py Loads leak2.tensors with TensorDeserializer and prints the over-read.
leak_noO.py Original hard-offset version of the generator (for this exact baseline).

Reproduce

pip install tensorizer==2.12.1 torch numpy

# Option 1: load the shipped malicious file directly
python load_poc.py

# Option 2: rebuild from scratch (deterministic)
python build_baseline.py     # -> two.tensors  (clean: a=4 zeros, secret=4x 1.2345)
python make_leak.py          # -> leak2.tensors (patched)
python load_poc.py           # -> a leaks 27 floats ending in secret's 1.2345

Expected output

a (27,) torch.float32 [0.0, 0.0, 0.0, 0.0, ... , 9.9063796e-24, 9.9063796e-24, 9.9063796e-24]
secret (4,) torch.float32 [1.2345, 1.2345, 1.2345, 1.2345]

Tensor a was serialized as 4 zeros. After loading the patched file it returns 27 floats: its own 4 zeros, then the raw bytes of secret's header, then secret's actual weights reinterpreted as float32. The tensor boundary has been read straight through. verify_hash defaults to False, so this is silent.


Root cause (short)

Each tensor's size is recorded twice β€” once in the file metadata (deserialized_length / data_length) and again in the per-tensor header (data_length). Both are attacker-controlled file data, and the only guard is a single assert needed_buffer_size == header.data_length. Setting both to the same inflated value passes the assert; torch.empty() then allocates an attacker-sized uninitialized buffer and file_.readinto() over-reads. Pushing the requested size past EOF leaves the uninitialized torch.empty() tail exposed β†’ uninitialized process-heap disclosure.

Remediation (short)

Replace the lone assert with an always-on check; cross-check header data_length against metadata deserialized_length and against shape Γ— dtype.itemsize; bound data_length by bytes remaining in the stream; verify the readinto return value; and avoid torch.empty() (or zero the unread tail) so a short read can never expose process memory.


Reported responsibly via Huntr. No third-party systems were targeted.

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