Skip to content

CVE-2026-23958: Verified Reproduction

CVE-2026-23958: DataEase: authentication bypass via password-derived HMAC JWT signing key

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

REPRO-2026-00168 dataease · github RCE May 25, 2026 CVE entry .txt
Severity
CRITICAL
CVSS
9.8
Reproduced in
100m 11s
Tool calls
630
Spend
$9.70
01 · Overview

What Is CVE-2026-23958?

CVE-2026-23958 is a high-severity authentication-bypass vulnerability in DataEase, a Spring Boot data-visualization platform, caused by signing authentication JWTs with a key derived from the admin password. Pruva reproduced it (reproduction REPRO-2026-00168).

02 · Severity & CVSS

CVE-2026-23958 Severity & CVSS Score

CVE-2026-23958 is rated critical severity, with a CVSS base score of 9.8 out of 10.

CRITICAL threat level
9.8 / 10 CVSS base
Weakness CWE-522 — Insufficiently Protected Credentials

Critical — the most severe class — typically remotely exploitable with severe impact. Treat as an emergency.

03 · Affected Versions

Affected dataease Versions

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

How to Reproduce CVE-2026-23958

$ pruva-verify REPRO-2026-00168
or curl -O https://www.pruva.dev/api/v1/reproductions/REPRO-2026-00168/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-23958

Reproduced
Vulnerable v2.10.10
Fixed v2.10.21
How the agent worked 2,455 events · 630 tool calls · 1h 40m
1h 40mDuration
630Tool calls
605Reasoning steps
2,455Events
5Dead-ends
Agent activity over 1h 40m
Support
18
Repro
1,917
Variant
515
0:00100:11

Root Cause and Exploit Chain for CVE-2026-23958

Versions: Docker images up to and including v2.10.10 are demonstrably vulnerable. Git commit analysis shows the fix (getPwd → getSecret) was already merged by v2.10.20, but the ticket incorrectly labels v2.10.20 as vulnerable.

DataEase signs its authentication JWTs with an HMAC-SHA256 key derived from the admin password. In vulnerable versions, the CommunityTokenFilter uses getPwd() (which returns the raw password hash) as the JWT verification secret. Because the default admin password is the well-known constant DataEase@123456, an unauthenticated attacker can compute MD5("DataEase@123456"), forge a JWT with claims {uid:1, oid:1}, and present it in the X-DE-TOKEN header to access any protected REST endpoint as the admin user.

  • Package/Component: io.dataease.auth.filter.CommunityTokenFilter (sdk/common) and io.dataease.xpack.permissions.login.bo.LoginUserCacheBO (xpack-permission)
  • Affected versions: Docker images up to and including v2.10.10 are demonstrably vulnerable. Git commit analysis shows the fix (getPwdgetSecret) was already merged by v2.10.20, but the ticket incorrectly labels v2.10.20 as vulnerable.
  • Risk level: High — unauthenticated remote attacker can impersonate the admin user.
  • Consequences: Full admin takeover via forged JWT, enabling subsequent exploitation of authenticated endpoints.

Root Cause

In the CommunityTokenFilter.doFilter method, when the application is running with an active loginServer bean (the standard Docker image configuration), the filter reaches the else branch and derives the JWT secret from the user cache object:

Object apisixCacheManage = CommonBeanFactory.getBean("apisixCacheManage");
Method method = DeReflectUtil.findMethod(apisixCacheManage.getClass(), "userCacheBO");
Object o = ReflectionUtils.invokeMethod(method, apisixCacheManage, userId);
Method pwdMethod = DeReflectUtil.findMethod(o.getClass(), "getPwd");  // vulnerable
Object pwdObj = ReflectionUtils.invokeMethod(pwdMethod, o);
secret = pwdObj.toString();

In the vulnerable code (getPwd), secret is simply the user's password hash (504c8c8dfcbbe5b50d676ad65ef43909 for the default admin). This is trivially derivable by anyone who knows the default password. The fix changes getPwd to getSecret, which concatenates the password hash with the per-installation RSA public key, making the secret unpredictable and no longer derivable from public information alone.

Fix commit: cac165ee84bb296184b9be6f5fa695af0344fa05 ("fix: JWT Token 漏洞", 2025-12-25). This commit is already present in git tag v2.10.20 and in Docker images v2.10.20+.

Reproduction Steps

  1. Run repro/reproduction_steps.sh
  2. The script:
    • Starts a MySQL 8 container and a DataEase v2.10.10 container (vulnerable)
    • Waits for the API to respond on http://127.0.0.1:8100
    • Baselines an anonymous request to /de2api/user/personInfo → expects 401
    • Forges a JWT with secret = MD5("DataEase@123456") and sends it as X-DE-TOKEN → expects 200
    • Stops the vulnerable app but preserves the MySQL data
    • Starts a DataEase v2.10.21 container against the same MySQL data
    • Replays the identical forged JWT → expects 401 with DE-GATEWAY-FLAG header
  3. Expected evidence:
    • logs/vulnerable_attack_response.txt shows HTTP 200
    • logs/fixed_attack_response.txt shows HTTP 401 and DE-GATEWAY-FLAG: The Token's Signature resulted invalid...

Evidence

  • logs/repro_run1.log — first successful execution of reproduction_steps.sh
  • logs/repro_run2.log — second successful execution (idempotency confirmed)
  • logs/vulnerable_transcript.txt — summary of v2.10.10 test results
  • logs/fixed_transcript.txt — summary of v2.10.21 test results
  • logs/vulnerable_attack_response.txt — raw HTTP response showing 200 on forged JWT
  • logs/fixed_attack_response.txt — raw HTTP response showing 401 + DE-GATEWAY-FLAG

Key excerpts from v2.10.10 (vulnerable):

HTTP/1.1 200
X-DE-EXECUTE-VERSION: 2.10.10
...
{"code":60003,"msg":"缺少许可证","data":null}

(The 200 status proves the JWT signature was accepted; the downstream "missing license" error is irrelevant to the auth bypass.)

Key excerpts from v2.10.21 (fixed):

HTTP/1.1 401
X-DE-EXECUTE-VERSION: 2.10.21
DE-GATEWAY-FLAG: The%20Token%27s%20Signature%20resulted%20invalid%20when%20verified%20using%20the%20Algorithm%3A%20HmacSHA256

Recommendations / Next Steps

  1. Upgrade to DataEase v2.10.20 or later. The fix commit is already present in those builds.
  2. Rotate secrets: If running an older vulnerable build, change the admin password and restart the application so that any cached JWT secret is regenerated.
  3. Additional hardening: Remove the fallback MD5-based secret derivation in SubstituleLoginConfig entirely, or enforce a randomly generated community-edition signing key at first boot.
  4. Regression testing: Add an integration test that attempts to authenticate with a JWT signed using only MD5(default_password) and asserts 401.

Additional Notes

  • Idempotency: reproduction_steps.sh was executed twice consecutively with identical results (HTTP 200 on v2.10.10, HTTP 401 on v2.10.21).
  • Version discrepancy: The ticket specifies v2.10.20 as vulnerable and v2.10.21 as fixed. However, binary analysis of the official Docker images shows that v2.10.20 already contains the getSecret() fix. The last vulnerable official Docker image we could identify is v2.10.10, which still uses getPwd(). The reproduction script therefore uses v2.10.10 as the vulnerable baseline and v2.10.21 as the fixed baseline to ensure the vulnerability is actually demonstrated at runtime.
  • Environment: Docker 27.x, Ubuntu 22.04 sandbox, images pulled from registry.cn-qingdao.aliyuncs.com/dataease/.

CVE-2026-23958 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:58
0:00
session startedaccounts/fireworks/models/kimi-k2p6 · cve-2026-23958 · cve-2026
0:22
0:22
web search
0:22
error

Unknown error

0:24
0:24
0:24
error

Unknown error

0:44
0:44
extract_facts
no facts extracted
0:45
0:45
0:45
supportrepro
0:47
0:47
0:47
0:47
0:47
0:48
0:48
0:51
0:57
$git clone --depth=50 https://github.com/dataease/dataease.git /tmp/dataease && cd /tmp/dataease && git log --oneline --all | head -30
5.9s
{"cwd":"/root/.pruva/runs/cve-2026-23958_20260525-214431","duration_ms":5886,"exit_code":0,"stderr":"Cloning into '/tmp/dataease'...\nUpdating files:  35% (2296/6378)\rUpdating files:  36% (2297/6378)\rUpdating files:  37% (2360/6378)\rUpdating files:  38% (2424/6378)\rUpdating files:  39% (2488/6378)\rUpdating files:  40% (2552/6378)\rUpdating files:  41% (2615/6378)\rUpdating files:  42% (2679/6378)\rUpdating files:  43% (2743/6378)\rUpdating files:  44% (2807/6378)\rUpdating files:  45% (2871/6378)\rUpdating files:  46% (2934/6378)\rUpdating files:  47% (2998/6378)\rUpdating files:  48% (3062/6378)\rUpdating files:  49% (3126/6378)\rUpdating files:  50% (3189/6378)\rUpdating files:  51% (3253/6378)\rUpdating files:  52% (3317/6378)\rUpdating files:  53% (3381/6378)\rUpdating files:  54% (3445/6378)\rUpdating files:  55% (3508/6378)\rUpdating files:  56% (3572/6378)\rUpdating files:  57% (3636/6378)\rUpdating files:  58% (3700/6378)\rUpdating files:  59% (3764/6378)\rUpdating files:  60% (3827/6378)\rUpdating files:  61% (3891/6378)\rUpdating files:  62% (3955/6378)\rUpdating files:  63% (4019/6378)\rUpdating files:  64% (4082/6378)\rUpdating files:  65% (4146/6378)\rUpdating fil… [truncated]
0:58

Artifacts and Evidence for CVE-2026-23958

Scripts, logs, diffs, and output captured during the reproduction.

bundle/context.json6.8 KB
bundle/metadata.json0.8 KB
bundle/ticket.md5.8 KB
bundle/repro/my.cnf0.6 KB
bundle/repro/mysql-init/01-init.sql0.1 KB
bundle/repro/rca_report.md5.6 KB
bundle/repro/patch_analysis.md3.7 KB
bundle/repro/application.yml0.9 KB
bundle/repro/reproduction_steps.sh7.1 KB
bundle/repro/validation_verdict.json1.4 KB
bundle/repro/docker-compose-fix.yml1.2 KB
bundle/repro/docker-compose-vuln.yml1.2 KB
bundle/logs/v2.10.21_forged_status.txt0.0 KB
bundle/logs/full_bypass_headers.txt0.2 KB
bundle/logs/v2.10.18_forged_status.txt0.0 KB
bundle/logs/v2.10.21_baseline_status.txt0.0 KB
bundle/logs/fixed_transcript.txt0.3 KB
bundle/logs/v2.10.18_baseline_headers.txt0.2 KB
bundle/logs/v2.10.18_baseline_status.txt0.0 KB
bundle/logs/vulnerable_transcript.txt0.3 KB
bundle/logs/no_xpack_baseline_headers.txt0.2 KB
bundle/logs/no_xpack_baseline_body.txt0.1 KB
bundle/logs/fixed_attack_response.txt0.3 KB
bundle/logs/full_bypass_body.txt0.1 KB
bundle/logs/no_xpack_bypass_body.txt2.7 KB
bundle/logs/no_xpack_bypass_headers.txt0.3 KB
bundle/logs/docker_build_no_xpack.log1.4 KB
bundle/logs/variant_test.log10.0 KB
bundle/logs/evidence_fixed_headers.txt0.2 KB
bundle/logs/v2.10.21_baseline_headers.txt0.2 KB
bundle/logs/repro_run1.log1.1 KB
bundle/logs/full_baseline_body.txt0.1 KB
bundle/logs/jwt_details.txt0.2 KB
bundle/logs/vulnerable_attack_response.txt0.4 KB
bundle/logs/v2.10.18_baseline_body.json0.1 KB
bundle/logs/v2.10.18_forged_headers.txt0.3 KB
bundle/logs/v2.10.21_baseline_body.json0.1 KB
bundle/logs/v2.10.18_forged_body.json0.0 KB
bundle/logs/repro_run2.log1.1 KB
bundle/logs/v2.10.21_forged_headers.txt0.2 KB
bundle/logs/evidence_vulnerable_headers.txt0.3 KB
bundle/logs/full_baseline_headers.txt0.2 KB
bundle/logs/evidence_fixed_body.json0.1 KB
bundle/logs/evidence_vulnerable_body.json0.0 KB
bundle/logs/v2.10.21_forged_body.json0.1 KB
08 · How to Fix

How to Fix CVE-2026-23958

Upgrade dataease · github to v2.10.21 or later.

Coming soon

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

10 · FAQ

FAQ: CVE-2026-23958

How does the DataEase JWT forgery attack work?

An unauthenticated attacker computes secret = MD5("DataEase@123456"), forges a JWT with header {alg: HS256, typ: JWT} and payload {uid: 1, oid: 1}, and presents it in DataEase's X-DE-TOKEN authentication header. The server verifies the forged token with the same password-derived secret and accepts it, granting the attacker admin-level access to any protected REST endpoint. The advisory notes this is the first link in a 4-CVE chain leading to unauthenticated RCE.

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

The ticket lists versions <= v2.10.20 as affected, fixed in v2.10.21. However, this run's own commit analysis found the fix (getPwd -> getSecret) already merged by v2.10.20, and could only demonstrably reproduce the bypass on Docker images up to and including v2.10.10.

How severe is CVE-2026-23958?

High — an unauthenticated attacker who forges the default-password-derived JWT gains full admin impersonation, enabling subsequent exploitation of authenticated endpoints.

How can I reproduce CVE-2026-23958?

Download the verified script from this page and run it in an isolated environment against a DataEase build still using the default admin password. It computes the MD5 of the default password, forges an HS256 JWT with uid=1/oid=1, presents it as X-DE-TOKEN, and confirms the server grants admin access.
11 · References

References for CVE-2026-23958

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