Views
No views yet
preprocessor.bin Java-Deserialization RCE (CWE-502)org.deeplearning4j.util.ModelSerializer.restoreMultiLayerNetwork(file) (and three
sibling restore methods) when fed an attacker-controlled .zip model file.org.deeplearning4j:deeplearning4j-core 1.0.0-M2.1 (current Maven Central release)
and master @ commit 4c22ac5 (Feb 23 2026)deeplearning4j/deeplearning4j-nn/src/main/java/org/deeplearning4j/util/ModelSerializer.javaObjectInputStream.readObject() sinks on attacker bytes at lines
393–396, 618–621, 876–877, 971–973ObjectInputFilter, no resolveClass override → arbitrary Java code runs
inside the JVM during model load(DataSetPreProcessor) cast happens after readObject() returns, so a
bespoke Serializable class with a hostile readObject works without any
ysoserial gadget chainObjectInputStream directly on the
preprocessor.bin zip entry — a code path Eclipse's zip-bomb hardening
commits (loadZipData lines 1001-1115) did not touch.| File | Purpose |
|---|---|
pom.xml | Maven build, pulls deeplearning4j-core 1.0.0-M2.1 + nd4j-native-platform |
src/main/java/poc/EvilPayload.java | Single Serializable class with hostile readObject |
src/main/java/poc/BuildMalModel.java | Writes a benign MultiLayerNetwork via ModelSerializer.writeModel, then appends a preprocessor.bin zip entry containing the serialized EvilPayload |
src/main/java/poc/LoadMalModel.java | Victim — calls ModelSerializer.restoreMultiLayerNetwork("mal-model.zip") |
mal-model.zip | Pre-built malicious model from a prior run (lazy reviewers can unzip -l to confirm preprocessor.bin entry) |
benign-model.zip | Reference benign model from same build pipeline for diff |
repro.sh | One-shot: mvn package, build malicious zip, run the victim, show RCE markers |
nd4j so an Apple-Silicon /
Linux-x86_64 host is fine.1git lfs install # optional, the zip files are < 2KB so vanilla clone works too
2git clone https://huggingface.co/tonydav41/dl4j-poc-modelserializer-rce-2026-05-12
3cd dl4j-poc-modelserializer-rce-2026-05-12
4bash repro.sh[*] mvn package ...
[*] dumping runtime classpath ...
[*] attacker: build mal-model.zip (legit MultiLayerNetwork + extra preprocessor.bin)
[*] victim: ModelSerializer.restoreMultiLayerNetwork(mal-model.zip)
[EvilPayload] CODE EXECUTION: wrote /tmp/DL4J_PWNED_<ts>.txt
[victim] post-readObject exception (expected; cast fails after payload runs):
ClassCastException: class poc.EvilPayload cannot be cast to
class org.nd4j.linalg.dataset.api.DataSetPreProcessor
[victim] /tmp/DL4J_RCE_TOUCH exists? true
[victim] RCE marker found: /tmp/DL4J_PWNED_<ts>.txt
[+] RCE evidence:
-rw-r--r-- ... /tmp/DL4J_RCE_TOUCH
-rw-r--r-- ... /tmp/DL4J_PWNED_<ts>.txtClassCastException happens after the payload runs, which is the entire
point — RCE is unconditional, the cast failure is cosmetic.4c22ac5)| Line | Method | Trigger entry |
|---|---|---|
| 393–396 | restoreMultiLayerNetworkHelper | preprocessor.bin |
| 618–621 | restoreComputationGraphHelper | preprocessor.bin |
| 876–877 | getObjectFromFile | objects/<key> |
| 971–973 | restoreNormalizerFromInputStreamDeprecated | fallback in restoreNormalizerFromFile |
restore* method on a user-supplied .zip gets
arbitrary code execution. This is the same class of bug as
CVE-2024-3660 (TF Keras Lambda) and CVE-2024-27322 (R RDS).omnihub module DL4J ships, MLflow artifact loaders
pointed at DL4J models, KNIME DL4J nodes, model-marketplace and notebook-server
deployments that accept third-party model uploads, and any inference pipeline
that deserializes models from an object store with weak ACLs.ObjectInputFilter (JEP 290) on each ObjectInputStream in
ModelSerializer.java, allowing only the concrete preprocessor / normalizer
classes that DL4J ships, plus primitives. Better: replace ObjectInputStream
with a typed JSON / Protobuf payload — NormalizerSerializer already supports
this, and the deprecated readObject path on line 971 should be removed
outright.tonydav41@huggingface, lendtrain on huntr). Filed 2026-05-12.