Skip to content

CVE-2026-40901: Verified Repro With Script Download

CVE-2026-40901: DataEase: Quartz JobStore Java deserialization RCE via QRTZ JOB DETAILS

CVE-2026-40901 is verified against dataease · github. Affected versions: <= v2.10.20. Fixed in v2.10.21. Vulnerability class: RCE. This high reproduction includes runnable sandbox proof, artifacts, and a plain-text agent view under REPRO-2026-00167.

REPRO-2026-00167 dataease · github RCE May 25, 2026 CVE entry ↗ .txt
Severity
HIGH
CVSS
8.8
Reproduced in
179m 9s
Tool calls
555
Spend
$10.97
01 · Overview

What Is CVE-2026-40901?

CVE-2026-40901 is a high-severity Java deserialization remote code execution vulnerability (CWE-502) in DataEase's Quartz scheduler integration, reached via the QRTZ_JOB_DETAILS table. Pruva reproduced it (reproduction REPRO-2026-00167).

02 · Severity & CVSS

CVE-2026-40901 Severity & CVSS Score

CVE-2026-40901 is rated high severity, with a CVSS base score of 8.8 out of 10.

HIGH threat level
8.8 / 10 CVSS base
Weakness CWE-502 (Deserialization of Untrusted Data) — Deserialization of Untrusted Data

High — serious impact or readily exploitable. Prioritize remediation.

03 · Affected Versions

Affected dataease Versions

dataease · github versions <= v2.10.20 are affected.

How to Reproduce CVE-2026-40901

$ pruva-verify REPRO-2026-00167
or curl -O https://pruva.dev/api/v1/reproductions/REPRO-2026-00167/artifacts/bundle/repro/reproduction_steps.sh && chmod +x reproduction_steps.sh && ./reproduction_steps.sh
Run in a VM or disposable container. This exploits a real vulnerability.
06 · Proof of Reproduction

Proof of Reproduction for CVE-2026-40901

Reproduced by Pruva's autonomous agents — 555 tool calls over 2h 59m. Full root-cause analysis and the complete transcript are below.

How the agent worked 2,050 events · 555 tool calls · 2h 59m
2h 59mDuration
555Tool calls
492Reasoning steps
2,050Events
10Dead-ends
Agent activity over 2h 59m
Support
77
Repro
1,970
0:00179:09

Root Cause and Exploit Chain for CVE-2026-40901

Versions: DataEase community edition ≤ v2.10.20Fixed: v2.10.21

CVE-2026-40901 is a deserialization remote code execution (RCE) vulnerability in DataEase's Quartz scheduler integration. DataEase v2.10.20 ships commons-collections-3.2.1.jar inside its Spring Boot application JAR (app.jar). The Quartz JDBC JobStore persists JobDataMap objects as serialized BLOBs in the QRTZ_JOB_DETAILS.JOB_DATA column. When the Quartz scheduler polls for triggers, it calls StdJDBCDelegate.getObjectFromBlob(), which uses a raw ObjectInputStream.readObject() on the attacker-controlled BLOB. With commons-collections-3.2.1.jar on the classpath, a ysoserial CommonsCollections6 payload can trigger Runtime.exec() during deserialization, achieving RCE as the DataEase process user (root in the default container image). DataEase v2.10.21 fixed this by removing commons-collections-3.2.1.jar from the application JAR.

  • Package/component affected: DataEase community edition Quartz scheduler (core-backend module)
  • Affected versions: DataEase community edition ≤ v2.10.20
  • Fixed versions: v2.10.21
  • Risk level: High (CVSS 3.1: 8.8)
  • Consequences: An attacker with write access to the QRTZ_JOB_DETAILS table (obtainable via the stacked SQL injection in CVE-2026-40900) can achieve arbitrary code execution inside the DataEase container as root by injecting a malicious serialized Java object into the JOB_DATA BLOB. The next time Quartz fires the trigger, the payload is deserialized and the gadget chain executes.

Root Cause

DataEase uses the Quartz scheduler with a JDBC JobStore backend. Quartz stores each job's JobDataMap in the QRTZ_JOB_DETAILS.JOB_DATA column as a Java-serialized BLOB. The scheduler thread deserializes this BLOB every time it acquires triggers using StdJDBCDelegate.getObjectFromBlob():

ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(bytes));
return ois.readObject();  // No class allowlist, no deserialization filter

DataEase v2.10.20's app.jar includes commons-collections-3.2.1.jar, which contains the InvokerTransformer class. This class is the core of the well-known CommonsCollections gadget chain. The ysoserial CommonsCollections6 payload creates a HashSet containing a TiedMapEntry wrapping a LazyMap with an InvokerTransformer. During ObjectInputStream.readObject(), the HashSet.readObject() method calls hashCode() on all elements. For TiedMapEntry, hashCode() triggers getValue()LazyMap.get()InvokerTransformer.transform()Runtime.exec().

Fix in v2.10.21: The commons-collections-3.2.1.jar dependency was removed from the application JAR. Without this JAR on the classpath, the TiedMapEntry class cannot be resolved during deserialization, causing a ClassNotFoundException and safely aborting the attack before the gadget chain can execute.

Reproduction Steps

The reproduction is orchestrated by repro/reproduction_steps.sh, which:

  1. Generates a ysoserial CommonsCollections6 payload that executes touch /tmp/pruva-cve-2026-40901.txt
  2. Starts the actual DataEase v2.10.20 Docker container with a MySQL backend
  3. Waits for DataEase and Quartz to fully initialize (Flyway migrations + scheduler startup)
  4. Injects the payload into QRTZ_JOB_DETAILS.JOB_DATA via MySQL UPDATE
  5. Waits for the Quartz Datasource trigger to fire (every 6 minutes)
  6. Verifies that /tmp/pruva-cve-2026-40901.txt was created inside the running DataEase container
  7. Repeats the same payload against a v2.10.21 container classpath, verifying ClassNotFoundException for TiedMapEntry

Expected evidence:

  • Vulnerable v2.10.20: Marker file /tmp/pruva-cve-2026-40901.txt exists inside the container; Quartz logs show JobPersistenceException: Couldn't acquire next trigger: class java.util.HashSet cannot be cast to class java.util.Map
  • Fixed v2.10.21: Marker file is NOT created; logs show ClassNotFoundException: org.apache.commons.collections.keyvalue.TiedMapEntry

Evidence

Classpath Analysis
  • repro/evidence/classpath-vulnerable.log: Shows BOOT-INF/lib/commons-collections-3.2.1.jar present in v2.10.20 app.jar
  • repro/evidence/classpath-fixed.log: Shows commons-collections-3.2.1.jar absent in v2.10.21 app.jar
  • repro/evidence/cc3-vulnerable.count: 1 (commons-collections 3.x present)
  • repro/evidence/cc3-fixed.count: 0 (commons-collections 3.x absent)
Vulnerable Service Test
  • repro/evidence/marker-vulnerable.txt: Empty file created by touch inside the running DataEase v2.10.20 container, proving Runtime.exec() executed during Quartz deserialization
  • repro/evidence/vulnerable-service-container.log: Full DataEase container logs showing initialization and Quartz activity
  • repro/evidence/vulnerable-quartz-evidence.log: Key log excerpt:
    org.quartz.JobPersistenceException: Couldn't acquire next trigger: class java.util.HashSet cannot be cast to class java.util.Map (java.util.HashSet and java.util.Map are in module java.base of loader 'bootstrap')
    Caused by: java.lang.ClassCastException: class java.util.HashSet cannot be cast to class java.util.Map
    
    This proves Quartz's StdJDBCDelegate.getObjectFromBlob() deserialized the CC6 payload (a HashSet), the gadget chain fired (touch executed), and then the subsequent cast to Map failed.
Fixed Classpath Test
  • repro/evidence/fixed-container-test.log: Shows ClassNotFoundException: org.apache.commons.collections.keyvalue.TiedMapEntry when the same payload is deserialized with the v2.10.21 classpath. The marker file is NOT created.

Recommendations / Next Steps

  1. Upgrade to v2.10.21 or later: The fix removes the vulnerable commons-collections-3.2.1.jar dependency.
  2. Add deserialization allowlist/filter: Even with the dependency removed, consider adding an ObjectInputFilter to the ObjectInputStream used by Quartz's StdJDBCDelegate.getObjectFromBlob() to prevent future gadget chains.
  3. Input validation on QRTZ_JOB_DETAILS: Restrict direct database write access to Quartz tables. The vulnerability is typically chained from CVE-2026-40900 (stacked SQL injection), so patching the SQL injection entrypoint is also critical.
  4. Regression test: After upgrade, verify that commons-collections-3.2.1.jar is not present in the app.jar (e.g., jar tf app.jar | grep commons-collections-3).

Additional Notes

  • Idempotency: The reproduction script is idempotent — it cleans up all containers and networks before each run. Running it twice produces the same results.
  • Environment: Tests were performed using the official DataEase Docker images (registry.cn-qingdao.aliyuncs.com/dataease/dataease:v2.10.20 and v2.10.21) with MySQL 8.4.5.
  • Limitations: The full service test for the vulnerable version requires ~7 minutes (DataEase initialization + Quartz trigger wait). The fixed version test uses a classpath-level deserialization harness for speed, but both tests use the actual DataEase application JAR and the same ObjectInputStream.readObject() code path that Quartz uses.
  • No reverse shells: The reproduction uses only a benign touch command as evidence. No network egress is required.

CVE-2026-40901 Reproduction Transcript

The agent's step-by-step process — every tool call, every handoff, the moment the exploit fired.

Full session Replay every step — scrub the timeline or play it back.

Event 1/40
0:000:40
0:00
session startedaccounts/fireworks/models/kimi-k2p6 · cve-2026-40901 · cve-2026
0:03
0:05
web search
0:07
0:08
0:10
0:11
web search
0:13
0:14
web search
0:19
0:20
0:21
web search
0:30
0:31
web search
0:32
0:33
0:34
0:36
web search
0:38
0:39
web search
0:40
08 · How to Fix

How to Fix CVE-2026-40901

Upgrade dataease · github to v2.10.21 or later.

Coming soon

Step-by-step mitigation and hardening guidance for CVE-2026-40901 — configuration checks, workarounds where no patch exists, and how to verify you're protected — is on the way.

10 · FAQ

FAQ: CVE-2026-40901

How does the CVE-2026-40901 Quartz deserialization RCE work?

An attacker with write access to a single row of QRTZ_JOB_DETAILS — obtainable via the stacked SQL injection in CVE-2026-40900, the preceding step in this 4-CVE chain (auth bypass -> blocklist bypass -> stacked SQLi -> Quartz deserialization RCE) — injects a ysoserial CommonsCollections6 payload into the JOB_DATA blob. The next time the scheduler fires that trigger, deserialization runs Runtime.exec(), achieving RCE as the DataEase process user (root in the default container image).

Which DataEase versions are affected by CVE-2026-40901, and where is it fixed?

DataEase community edition <= v2.10.20 is affected; it is fixed in v2.10.21, which removes commons-collections-3.2.1.jar from the application JAR.

How severe is CVE-2026-40901?

It is rated high severity (CVSS 3.1: 8.8), representing the terminal, code-execution step of a 4-CVE exploitation chain in DataEase's Quartz scheduler.

How can I reproduce CVE-2026-40901?

Download the verified script from this page and run it in an isolated environment against DataEase <= v2.10.20. With write access to a QRTZ_JOB_DETAILS row (as obtained via the chained stacked SQL injection), it injects a CommonsCollections6 gadget-chain payload and shows the next scheduler tick executing it as the DataEase process user, then confirms v2.10.21 is not exploitable.
11 · References

References for CVE-2026-40901

Authoritative sources for CVE-2026-40901 — official vulnerability databases and the upstream advisory. Pruva's reproduction verifies the issue firsthand; these are the primary records to corroborate it.