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

Check out the documentation for more information.

Eclipse Deeplearning4j (.zip model) — unsafe Java deserialization (CWE-502) in ModelSerializer restore path: loading a malicious model executes attacker-controlled ObjectInputStream.readObject

Summary

Eclipse Deeplearning4j model files are .zip archives loaded with org.deeplearning4j.util.ModelSerializer.restoreMultiLayerNetwork(...) / restoreComputationGraph(...). On the standard, default load path, the restorer reads the archive entry preprocessor.bin and passes its bytes straight to new ObjectInputStream(...).readObject() with no ObjectInputFilter, no class allowlist. A malicious model .zip therefore drives Java native deserialization of fully attacker-controlled bytes the moment a victim loads it — a textbook CWE-502 untrusted-deserialization sink, executing attacker code inside readObject (demonstrated below). Neither picklescan nor modelscan inspects Java-serialized data, so the malicious model passes both scanners.

Affected / verified versions

  • deeplearning4j-nn / deeplearning4j-core 1.0.0-M2.1 (latest published release, Jan 2023) on OpenJDK 21
  • Sink confirmed identical and unpatched on master (the restore helpers still call ObjectInputStream.readObject on preprocessor.bin)

Root cause

ModelSerializer.class (bytecode, both restoreMultiLayerNetwork and restoreComputationGraph helpers):

319: ldc           "preprocessor.bin"
347: new           java/io/ObjectInputStream
353: invokespecial java/io/ObjectInputStream.<init>(InputStream)
360: invokevirtual java/io/ObjectInputStream.readObject()    // <-- unfiltered deser of attacker bytes

The archive member preprocessor.bin is deserialized via a raw ObjectInputStream with no setObjectInputFilter, no whitelist, no validation. This runs on every standard restore call, independent of the loadUpdater flag (only updaterState.bin is flag-gated, and it goes through Nd4j.read, not readObject). Java deserialization runs the graph's readObject/readResolve/finalize chains during reconstruction, so attacker-reachable gadget classes execute code before any type check on the result.

Proof of concept

poc/ contains the malicious model, a benign control, and the Java harness.

  • poc/malicious_model.zip — a valid DL4J model (configuration.json + coefficients.bin) with one added preprocessor.bin holding a serialized object whose readObject runs Runtime.exec.
  • poc/Evil.java — the demonstration gadget class (its readObject executes a command and writes a marker).
  • poc/Load.javaModelSerializer.restoreMultiLayerNetwork(new File(argv[0]), true) (the standard API call).
  • poc/benign_control.zip — same model without preprocessor.bin.

Observed (reproduced)

$ java -cp out:libs/* Load malicious_model.zip
MARKER_DL4J_DESER_7Q3X note=served-as-preprocessor.bin whoami=uid=1002(hacnho) gid=1002(hacnho) groups=...,docker,kvm
Exception in thread "main" java.lang.ClassCastException: class Evil cannot be cast to ...DataSetPreProcessor

The command executed inside readObject (marker written with the running uid) before the cosmetic ClassCastException — i.e. attacker code runs to completion during deserialization; the cast failure afterward is irrelevant. A real gadget chain returns a value of the expected type (or the exception is simply swallowed by the caller) and leaves no trace.

Control: java -cp out:libs/* Load benign_control.zipLOADED_OK params=23, exit 0, no marker — isolating the sink to the attacker-added preprocessor.bin.

Impact

Untrusted Java deserialization (CWE-502) on the default model-load path → remote code execution when a victim loads a malicious DL4J .zip model. This is the Java analog of the untrusted-pickle RCE class: the model file is attacker-controlled (the MFV threat model), and loading it deserializes attacker bytes through an unfiltered ObjectInputStream.

Honest scope on the gadget precondition: full host RCE requires a Serializable gadget chain reachable on the victim's classpath. The bare deeplearning4j-core dependency set ships commons-collections4-4.1, whose InvokerTransformer is not Serializable without -Dorg.apache.commons.collections4.enableUnsafeSerialization=true, and no other classic serializable-gadget library (commons-collections 3.x, commons-beanutils, c3p0, Spring, groovy, …) is in the default 172-jar set; JDK-21 serialization filters neutralize the JRE-internal chains. So on a bare library install the demonstrated impact is an arbitrary-deserialization primitive (CWE-502) that executes attacker code when a gadget is present (shown above with a class on the classpath). In practice DL4J is embedded in JVM applications/serving stacks that routinely pull gadget-bearing libraries (Spring, c3p0, commons-beanutils, etc.), making the precondition commonly satisfiable → RCE. The vulnerability is the unfiltered readObject sink itself; gadget availability is environmental and continually growing.

Remediation

  • Wrap the preprocessor.bin (and any other readObject) deserialization in a strict ObjectInputFilter allowlisting only the expected org.nd4j…DataSetPreProcessor types (and primitives), or replace native Java serialization with a safe format.
  • Reject/serialize preprocessors via a schema-validated, non-ObjectInputStream mechanism.

Dedup / novelty

  • No CVE / GHSA for ModelSerializer Java deserialization at any version; no huntr per-repo report (huntr.com/repos/…/deeplearning4j shows only generic NPE/EOF bug reports). The sink is present and unpatched on master.
  • Distinct from the other model-deserialization CWEs (pickle, etc.) — this is Java native ObjectInputStream on a .zip DL4J model, an in-scope huntr MFV format (DL4J – Eclipse).
  • Scanner blind spot (bonus): modelscan → "No issues found"; picklescan → "Scanned files: 0" — neither understands Java ObjectOutputStream data.

Artifacts

  • poc/malicious_model.zip, poc/benign_control.zip, poc/Evil.java, poc/Load.java, poc/MakeEvil2.java.
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