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.
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).
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 — serious impact or readily exploitable. Prioritize remediation.
Affected dataease Versions
dataease · github versions <= v2.10.20 are affected.
How to Reproduce CVE-2026-40901
pruva-verify REPRO-2026-00167 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 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
Root Cause and Exploit Chain for CVE-2026-40901
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-backendmodule) - 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_DETAILStable (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 theJOB_DATABLOB. 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:
- Generates a
ysoserialCommonsCollections6 payload that executestouch /tmp/pruva-cve-2026-40901.txt - Starts the actual DataEase v2.10.20 Docker container with a MySQL backend
- Waits for DataEase and Quartz to fully initialize (Flyway migrations + scheduler startup)
- Injects the payload into
QRTZ_JOB_DETAILS.JOB_DATAvia MySQL UPDATE - Waits for the Quartz
Datasourcetrigger to fire (every 6 minutes) - Verifies that
/tmp/pruva-cve-2026-40901.txtwas created inside the running DataEase container - Repeats the same payload against a v2.10.21 container classpath, verifying
ClassNotFoundExceptionforTiedMapEntry
Expected evidence:
- Vulnerable v2.10.20: Marker file
/tmp/pruva-cve-2026-40901.txtexists inside the container; Quartz logs showJobPersistenceException: 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: ShowsBOOT-INF/lib/commons-collections-3.2.1.jarpresent in v2.10.20app.jarrepro/evidence/classpath-fixed.log: Showscommons-collections-3.2.1.jarabsent in v2.10.21app.jarrepro/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 bytouchinside the running DataEase v2.10.20 container, provingRuntime.exec()executed during Quartz deserializationrepro/evidence/vulnerable-service-container.log: Full DataEase container logs showing initialization and Quartz activityrepro/evidence/vulnerable-quartz-evidence.log: Key log excerpt:
This proves Quartz'sorg.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.MapStdJDBCDelegate.getObjectFromBlob()deserialized the CC6 payload (aHashSet), the gadget chain fired (touchexecuted), and then the subsequent cast toMapfailed.
Fixed Classpath Test
repro/evidence/fixed-container-test.log: ShowsClassNotFoundException: org.apache.commons.collections.keyvalue.TiedMapEntrywhen the same payload is deserialized with the v2.10.21 classpath. The marker file is NOT created.
Recommendations / Next Steps
- Upgrade to v2.10.21 or later: The fix removes the vulnerable
commons-collections-3.2.1.jardependency. - Add deserialization allowlist/filter: Even with the dependency removed, consider adding an
ObjectInputFilterto theObjectInputStreamused by Quartz'sStdJDBCDelegate.getObjectFromBlob()to prevent future gadget chains. - 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.
- Regression test: After upgrade, verify that
commons-collections-3.2.1.jaris not present in theapp.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.20andv2.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
touchcommand 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.
Artifacts and Evidence for CVE-2026-40901
Scripts, logs, diffs, and output captured during the reproduction.
How to Fix CVE-2026-40901
Upgrade dataease · github to v2.10.21 or later.
FAQ: CVE-2026-40901
How does the CVE-2026-40901 Quartz deserialization RCE work?
Which DataEase versions are affected by CVE-2026-40901, and where is it fixed?
How severe is CVE-2026-40901?
How can I reproduce CVE-2026-40901?
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.