You need to agree to share your contact information to access this model
This repository is publicly accessible, but you have to accept the conditions to access its files and content.
This repository is a benign security proof-of-concept for the huntr Model File Vulnerability program (Protect AI). It contains no real model and no code execution — only oversized nested Java data used to demonstrate a denial-of-service on model load.
Log in or Sign Up to review the conditions and access this model content.
DL4J ModelSerializer — Denial-of-Service via unbounded, unfiltered Java deserialization of preprocessor.bin
Security proof-of-concept for the huntr Model File Vulnerability (MFV) program. This is not a real model — edgecase.zip is a crafted DL4J model archive that demonstrates an availability (DoS) vulnerability. It contains no code, no external command, no gadget — only a large, valid nested Java data object.
Summary
Eclipse DeepLearning4j's ModelSerializer.restoreMultiLayerNetwork(File) (and restoreComputationGraph) eagerly deserializes the archive entry preprocessor.bin with an unfiltered java.io.ObjectInputStream.readObject() — no ObjectInputFilter/allowlist, and no integrity/size/signature check on the untrusted file before the object graph is built. Loading a crafted .zip whose preprocessor.bin holds a large nested object graph causes uncontrolled heap growth and a deterministic OutOfMemoryError — a DoS on any service that loads an untrusted DL4J model.
- Affected: DL4J
.zipmodel viaorg.deeplearning4j.util.ModelSerializer, tested ondeeplearning4j-nn:1.0.0-M2.1(sink also onmaster, unpatched). - Class: CWE-502 (Deserialization of Untrusted Data) → resource exhaustion / DoS. (The same unfiltered
readObjectsink is the classic Java insecure-deserialization RCE primitive when a gadget lib is on the loader's classpath — not demonstrated here.)
PoC file (edgecase.zip, in this repo)
A valid tiny DL4J model saved with ModelSerializer.writeModel(net, "baseline.zip", true), then copied and given ONE extra entry preprocessor.bin = ObjectOutputStream.writeObject(payload) where payload is a plain nested structure: a List of 2,000 HashMaps × 400 entries + a 300-deep Map→Map chain (~800,300 nodes; ~37 MB serialized, ~5.3 MB on disk after deflate).
edgecase.zip
├── configuration.json (3,076 B) # valid model config
├── coefficients.bin (381 B)
├── updaterState.bin (649 B)
└── preprocessor.bin (39,175,202 B) # large nested Java object graph
Measured impact (JDK 21, DL4J 1.0.0-M2.1, Linux)
| load | -Xmx |
wall | peak heap | outcome |
|---|---|---|---|---|
| baseline.zip | 512 MB | 372 ms | 17 MB | OK |
| edgecase.zip | 512 MB | 816 ms | 304 MB | ClassCastException after full deserialize |
| edgecase.zip | 256 MB | 992 ms | 246 MB | OutOfMemoryError: Java heap space during preprocessor.bin deserialization |
Heap blows up ~25× (12→304 MB) and ~57× vs the on-disk file size (deflate hides the cost). A larger crafted structure exhausts any fixed heap (unbounded). The type check (ClassCastException) fires only after readObject() builds the whole graph — the cost is already paid.
Trigger conditions
Any call to ModelSerializer.restoreMultiLayerNetwork(...) / restoreComputationGraph(...) on an untrusted .zip containing a preprocessor.bin entry. No auth, no host access, no special prerequisites — the entry is deserialized automatically before any type/validity check.
No integrity check (bytecode-verified, javap -c)
checkInputStream(is)= onlyis.available() > 0.loadZipData(is)reads each entry into aMap<String,byte[]>; the only guard isZipEntry.getSize() > Integer.MAX_VALUE(2 GB) — no smaller per-entry cap, no total-size cap, no CRC/digest/signature.preprocessor.bin→new ObjectInputStream(new ByteArrayInputStream(bytes)).readObject()thencheckcast DataSetPreProcessor(type check after graph built). NoObjectInputFilter. Same sink inrestoreMultiLayerNetworkAndNormalizerandrestoreNormalizerFromInputStreamDeprecated.
Reproduce
- Java project with
org.deeplearning4j:deeplearning4j-nn:1.0.0-M2.1+org.nd4j:nd4j-native-platform:1.0.0-M2.1. - Build
baseline.zipviaModelSerializer.writeModel(net, file, true). - Build
edgecase.zip= baseline + appendedpreprocessor.bin=ObjectOutputStreamof the nested structure above (List of 2000 HashMaps×400 + 300-deep Map chain). ModelSerializer.restoreMultiLayerNetwork(new File("edgecase.zip"))with-Xmx256m→OutOfMemoryErrorduring deserialization.- Confirm no integrity gate:
javap -p -c org.deeplearning4j.util.ModelSerializer.
Remediation
Wrap the preprocessor/normalizer deserialization in a strict ObjectInputFilter allowlist (limit to expected DataSetPreProcessor types, with maxdepth/maxrefs/maxbytes); add per-entry and total-size caps in loadZipData; prefer a data-only format over native Java serialization for model metadata; optionally verify a checksum/signature before deserializing.
Note on scanner coverage
Protect AI's ModelScan does not currently cover the DL4J format, so this malicious model file passes the safety scan undetected.