CVE-2026-40900: Verified Repro With Script Download
CVE-2026-40900: DataEase: stacked-query SQL injection via previewSql with allowMultiQueries
CVE-2026-40900 is verified against dataease · github. Affected versions: <= v2.10.20. Fixed in v2.10.21. Vulnerability class: SQLi. This high reproduction includes runnable sandbox proof, artifacts, and a plain-text agent view under REPRO-2026-00169.
What Is CVE-2026-40900?
CVE-2026-40900 is a high-severity (CVSS 8.8 per the advisory) stacked-query SQL injection vulnerability (CWE-89) in DataEase's previewSql endpoint, letting an attacker execute arbitrary stacked SQL statements against the application database. Pruva reproduced it (reproduction REPRO-2026-00169).
CVE-2026-40900 Severity & CVSS Score
CVE-2026-40900 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-40900
pruva-verify REPRO-2026-00169 curl -O https://pruva.dev/api/v1/reproductions/REPRO-2026-00169/artifacts/bundle/repro/reproduction_steps.sh && chmod +x reproduction_steps.sh && ./reproduction_steps.sh Proof of Reproduction for CVE-2026-40900
Reproduced by Pruva's autonomous agents — 582 tool calls over 58 min. Full root-cause analysis and the complete transcript are below.
PostgreSQL time-based stacked SQL injection through DatasetDataManage.previewSql — same root cause as CVE-2026-40900 (no single-statement validation) but via a PostgreSQL datasource instead of MySQL.
How the agent worked
Root Cause and Exploit Chain for CVE-2026-40900
CVE-2026-40900 is a stacked-query SQL injection vulnerability in DataEase's previewSql endpoint (POST /de2api/datasetData/previewSql). The endpoint accepts arbitrary user-supplied SQL and wraps it inside a subquery (SELECT * FROM ( <USER_SQL> ) AS alias LIMIT 100) without enforcing that the input is a single SELECT statement. When the underlying MySQL JDBC connection is configured with allowMultiQueries=true, an attacker can craft a payload that escapes the wrapping subquery using a closing parenthesis and semicolon, executes a second side-effecting statement (INSERT/UPDATE/DELETE), and uses a MySQL # comment to swallow the remainder of the wrapper. This grants full read/write access to the application database.
- Package/component affected: DataEase (
io.dataease:datasource:type:Mysqlanddataset:manage:DatasetDataManage) - Affected versions:
<= v2.10.20 - Fixed versions:
v2.10.21 - Risk level: High (CVSS 3.1: 8.8)
- Consequences: Authenticated attacker can execute arbitrary stacked SQL against the DataEase application database, including INSERT/UPDATE/DELETE on tables such as
core_msg_typeor Quartz scheduler tables, enabling further privilege escalation or RCE as documented in the Ox Security writeup.
Root Cause
The vulnerability has two contributing factors:
Missing
allowMultiQueriesin MySQL JDBC parameter blocklist: Incore/core-backend/src/main/java/io/dataease/datasource/type/Mysql.java(v2.10.20), theillegalParameterslist did not includeallowMultiQueries. This allowed an admin (or attacker who had compromised admin privileges via the preceding CVEs in the chain) to register a MySQL datasource whose JDBC URL containedallowMultiQueries=true.No single-statement validation in
previewSql:DatasetDataManage.previewSql()wraps the user-provided SQL string in a subquery without parsing or validating that it is a single SELECT statement. WhenallowMultiQueries=true, the MySQL JDBC driver happily executes multiple statements in one call.
The attacker payload:
SELECT 1 FROM dual) AS x; INSERT INTO repro_test (id, name) VALUES (999999999, 'pwned')#
becomes:
SELECT * FROM ( SELECT 1 FROM dual) AS x; INSERT INTO repro_test ... # ) AS alias LIMIT 100
The # comments out ) AS alias LIMIT 100, leaving two valid statements, both of which MySQL executes.
Fix commit: 15611593b3631b5a25528b9cdb2ee517ef27929a — adds "allowMultiQueries" to the illegalParameters list in Mysql.java. When the datasource configuration is validated during save/update, JdbcUrlSecurityPolicy.validate() now rejects any MySQL JDBC URL or extra parameters containing allowMultiQueries, causing the datasource to be saved with status="Error". previewSql refuses to run against datasources with Error status, blocking the exploit path.
Reproduction Steps
See repro/reproduction_steps.sh for the automated end-to-end reproduction. At a high level:
- Start DataEase
v2.10.20+ MySQL via Docker Compose. - Log in as
admin/DataEase@123456(RSA-encrypted credentials fetched via/de2api/dekey). - Create a MySQL datasource with
extraParams=allowMultiQueries=true. - Validate the datasource (succeeds on v2.10.20).
- Send
POST /de2api/datasetData/previewSqlwith a base64-encoded stacked-SQL payload:SELECT 1 FROM dual) AS x; INSERT INTO repro_test (id, name) VALUES (999999999, 'pwned-by-cve-2026-40900')# - Query MySQL: a new row exists in
repro_test— proving the INSERT executed. - Tear down, redeploy with
v2.10.21, repeat the same save request. - On v2.10.21 the datasource is saved but marked
status="Error"becauseallowMultiQueriesis rejected by the updatedillegalParametersblocklist.previewSqlrefuses to run, and no side-effect occurs.
Evidence
logs/v2.10.20_exploit.json— HTTP response frompreviewSqlon the vulnerable build.logs/v2.10.20_validate.json— datasource validation response showingstatus="Success".logs/v2.10.21_create_datasource.json— save response on the fixed build showingstatus="Error".- Console output from the reproduction script shows:
- v2.10.20:
Post-exploit repro_test count: 1 - v2.10.21: datasource unusable,
count: 0
- v2.10.20:
Recommendations / Next Steps
- Upgrade to v2.10.21 or later — the vendor patch is minimal and targeted.
- Additional defense-in-depth: add a server-side SQL parser (e.g., JSqlParser or Calcite SQL validation) to enforce that
previewSqlinput contains exactly one SELECT statement before wrapping it. - Audit existing MySQL datasources for any that have
allowMultiQueries=truein their configuration and remove or reconfigure them. - Regression test: include the stacked-SQL payload in the CI pipeline for
previewSqlto ensure future changes don't re-introduce the bypass.
Additional Notes
- Idempotency: The script was run twice consecutively, both times producing the same results (vulnerable side-effect confirmed on v2.10.20, blocked on v2.10.21).
- Limitations: The reproduction requires running the full DataEase Spring Boot container and MySQL container, so each run takes ~60–90 seconds. The script handles cleanup automatically via
trap cleanup EXIT. - The fix is in the datasource configuration layer (
Mysql.illegalParameters) rather than in thepreviewSqlquery-wrapping logic itself. While this blocks the specificallowMultiQueriesvector, a more robust fix would also validate the SQL structure inpreviewSqlto defend against other JDBC-parameter bypasses.
Variant Analysis & Alternative Triggers for CVE-2026-40900
Three variant hypotheses were tested against DataEase v2.10.20 (vulnerable) and v2.10.21 (fixed). Variant 3 — a PostgreSQL time-based stacked SQL injection through the same previewSql endpoint — was confirmed as an alternate trigger on v2.10.20, proving the root cause (lack of single-statement validation in previewSql) affects non-MySQL datasources as well. Variants 1 and 2 were partially successful on v2.10.20 but blocked or non-exploitable on v2.10.21. No true bypass of the v2.10.21 patch was found, though Variant 2 exposes a validation-logic bypass (double-URL-encoded allowMultiQueries passes the illegalParameters check while the driver does not recognize it).
Fix Coverage / Assumptions
The original fix (commit 15611593b3631b5a25528b9cdb2ee517ef27929a) assumes that:
- Blocking the
allowMultiQueriesJDBC parameter inMysql.javais sufficient to prevent multi-statement execution inpreviewSql. - All MySQL-compatible datasource types (
mysql,mariadb,StarRocks,doris,TiDB) parse their configuration throughMysql.class, inheriting the sameillegalParametersblocklist. - The
previewSqlendpoint's datasource-status check (status != "Error") will block any datasource that fails JDBC validation.
The fix does not cover:
- Other database types whose JDBC drivers support multi-statements natively without requiring a special parameter (e.g., PostgreSQL, SQL Server).
- The
previewSqlSQL-wrapping logic itself — there is still no single-statement parser or;/comment sanitization. - Potential bypasses of the
illegalParametersstring-matching logic (e.g., encoding tricks).
Variant / Alternate Trigger
Variant 1: mariadb type with allowMultiQueries=true
- Surface:
POST /de2api/datasetData/previewSqlwith amariadbdatasource configured withallowMultiQueries=true. - Code path:
DatasetDataManage.previewSql()→ProviderFactory.getProvider("mariadb")→CalciteProvider.parseDatasourceConfiguration()parses asMysql.class→Mysql.getJdbc()validatesillegalParameters. - Result on v2.10.20: Datasource saved with
status="Success". Stacked SQL injection succeeded (confirmed by successfulINSERT INTO repro_test). - Result on v2.10.21: Datasource saved with
status="Error"— theallowMultiQueriesparameter was blocked by the updatedillegalParameterslist. Same root cause, same surface, blocked by the patch.
Variant 2: Double-URL-encoded allowMultiQueries
- Surface: MySQL datasource with
extraParams=allow%254DultiQueries=true. - Code path:
Mysql.getJdbc()→URLDecoder.decode(jdbcUrl).toLowerCase().contains("allowmultiqueries"). - Result on both versions: The validation check is bypassed (
status="Success") becauseURLDecoder.decodeturns%25into%, yieldingallow%4DultiQueries=true, which does not matchallowmultiqueries. However, MySQL Connector/J also performs single-level URL decoding, so the driver seesallow%4DultiQueries=trueand does not enable multi-statements. The stacked SQL exploit fails. This is a validation-logic bypass without vulnerability exploitation.
Variant 3: PostgreSQL time-based stacked query
Surface:
POST /de2api/datasetData/previewSqlwith a PostgreSQL datasource.Code path:
DatasetDataManage.previewSql()builds wrapper SQL viaSQLUtils.buildOriginPreviewSql(), translates dialect withProvider.transSqlDialect(), replaces placeholder with user SQL, then executes viaCalciteProvider.jdbcFetchResultField()→Statement.executeQuery().Result on v2.10.20: Baseline request took 0.03s; stacked payload
SELECT 1) AS x; SELECT pg_sleep(5)--took 5.04s and returnedSQL ERROR: Multiple ResultSets were returned by the query.The 5-second delay provespg_sleep(5)executed as the second statement. This confirms the same root cause (no single-statement validation) is exploitable through PostgreSQL datasources.Result on v2.10.21: No time delay (0.03s–0.05s). The query immediately returned a PostgreSQL syntax error. The stacked query path is blocked on the fixed version, likely due to differences in how the PostgreSQL JDBC driver or Calcite SQL dialect translation handles the malformed wrapper in this build.
Package/component affected: DataEase
DatasetDataManage.previewSql()/CalciteProvider— affects all database types that support multi-statement execution.Affected versions:
<= v2.10.20(confirmed onv2.10.20commitba0052aff05d85b5ae6e81f687b777b242222dd4).Fixed versions:
v2.10.21(commite1085ffb75f42b6aca117edf36b30276bfdfe9aa) blocks the MySQL-specific vector but does not harden thepreviewSqlSQL-wrapping logic.Risk level: High (same CVSS 3.1: 8.8) for unpatched instances using PostgreSQL datasources.
Consequences: Authenticated attacker can execute arbitrary stacked SQL against any datasource whose driver supports multi-statements, potentially affecting tables, permissions, or application state.
Root Cause
The root cause is identical to CVE-2026-40900:
DatasetDataManage.previewSql()wraps raw user SQL in a subquery (SELECT * FROM ( <USER_SQL> ) tmp LIMIT 100) without parsing or validating that the input is a singleSELECTstatement.- When the underlying JDBC driver supports multi-statement execution, the attacker can inject
); <SECOND_STATEMENT> <COMMENT>to break out of the wrapper and execute arbitrary side-effecting SQL.
The fix only addresses the prerequisite (allowMultiQueries=true in MySQL) rather than the root cause (missing single-statement validation). Other database types (e.g., PostgreSQL) whose drivers support multi-statements by default remain potentially vulnerable on unpatched versions.
Reproduction Steps
See vuln_variant/reproduction_steps.sh for the automated end-to-end reproduction.
At a high level:
- Start DataEase
v2.10.20+ MySQL + PostgreSQL via Docker Compose. - Log in as
admin/DataEase@123456(RSA-encrypted credentials fetched via/de2api/dekey). - Create a PostgreSQL datasource (
type=pg, hostpg, port5432, databasedataease). - Send
POST /de2api/datasetData/previewSqlwith a base64-encoded stacked-SQL payload:SELECT 1) AS x; SELECT pg_sleep(5)-- - Observe the response takes ~5 seconds (baseline is <0.1s), confirming the second statement executed.
- On v2.10.21, repeat the same request and observe immediate failure with no time delay, confirming the variant is blocked.
Evidence
logs/variant3_v2.10.20_baseline.json— baselinepreviewSqlresponse against PostgreSQL (elapsed: 0.03s, code: 0).logs/variant3_v2.10.20_exploit.json— stackedpg_sleep(5)payload response (elapsed: 5.04s, error: "Multiple ResultSets were returned by the query.").logs/variant3_v2.10.21_exploit.json— same payload on fixed version (elapsed: 0.03s, error: PostgreSQL syntax error).logs/variant1_v2.10.20_create.json—mariadbdatasource withallowMultiQueries=truesaved asstatus=Success.logs/variant1_v2.10.20_exploit.json— successful stacked SQL injection throughmariadbdatasource.logs/variant2_v2.10.21_create.json— MySQL datasource withallow%254DultiQueries=truesaved asstatus=Success(validation bypassed).
Recommendations / Next Steps
Add server-side SQL-structure validation in
previewSql(defense-in-depth). Before wrapping user SQL, parse it with a SQL parser (e.g., JSqlParser or CalciteSqlParser) and reject any input that:- Contains more than one top-level statement (
;outside quotes/comments). - Contains comment sequences (
--,/*,#, etc.) that could swallow the wrapper suffix. - Is not a
SELECTstatement.
- Contains more than one top-level statement (
Extend
illegalParametersvalidation robustness:- Perform iterative URL decoding (up to 3–5 rounds) before matching, or use a whitelist approach for allowed parameters instead of a blacklist.
- Add
Normalizer.normalize()(NFKC) to defeat Unicode homoglyph attacks.
Audit other datasource types for multi-statement support. PostgreSQL and SQL Server drivers support multi-statements natively. Consider adding driver-specific restrictions or, better, adding the universal SQL-structure validation described above.
Regression test: Include stacked-SQL payloads for multiple database types (MySQL, PostgreSQL, SQL Server) in the CI pipeline for
previewSql.
Additional Notes
- Idempotency: The script was run multiple times (8 iterations) with consistent results. Cleanup removes Docker volumes and containers via
docker compose down -vanddocker volume rm. - Limitations: Variant 2 bypasses the
illegalParameterscheck but does not yield a working exploit because MySQL Connector/J does not double-decode%254D. A custom driver or future driver change could potentially close this gap. - Trust boundary: All variants require authenticated access to the DataEase
previewSqlendpoint (same as the original CVE).
CVE-2026-40900 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.
Unknown error
Unknown error
Unknown error
Artifacts and Evidence for CVE-2026-40900
Scripts, logs, diffs, and output captured during the reproduction.
How to Fix CVE-2026-40900
Upgrade dataease · github to v2.10.21 or later.
FAQ: CVE-2026-40900
How does the CVE-2026-40900 exploit work?
SELECT 1 FROM dual) AS x; INSERT INTO core_msg_type (id, name, pid) VALUES (999999999,'pwned-by-cve-2026-40900',0)#. The closing parenthesis and semicolon escape the wrapping subquery, the INSERT executes as a second stacked statement, and the trailing # comments out the rest of the wrapper.Which versions of DataEase are affected by CVE-2026-40900, and where is it fixed?
How severe is CVE-2026-40900?
How can I reproduce CVE-2026-40900?
References for CVE-2026-40900
Authoritative sources for CVE-2026-40900 — official vulnerability databases and the upstream advisory. Pruva's reproduction verifies the issue firsthand; these are the primary records to corroborate it.