SpringBoot框架华夏ERP源码审计
环境搭建
华夏ERP基于SpringBoot框架和SaaS模式,可以算作是国内人气较高的一款ERP项目,看网上已经公开了漏洞,本次对此框架代码进行源码审计。
源码下载路径:https://github.com/jishenghua/jshERP,这里的版本为v2.3
直接拖入IDEA加载,Maven下载所需的jar包,点击run即可
审计准备
典型的spring框架,先来看pom.xml加载了哪些威胁组件:
点根烟不慌,看到了这个熟悉的组件,版本透露出shell的感觉。
再来看一下Filter过滤器具体过滤了哪些内容,Servlet3.0提供了@WebFilter用于将一个类声明为过滤器,搜索@WebFilter即可定位:
使用@WebInitParam配置多个name,对.css#.js#.jpg#.png#.gif#.ico,/user/login#/user/registerUser#/v2/api-docs资源请求的时候不会进行拦截,doFilter方法是具体的实现:
看到这里我们要明确:
- requestUrl中如果存在/doc.html,/register.html,/login.html字段就可以绕过认证请求
- 传入verify()方法进行判断的时候,正确的使用应该选择endsWith()来判断url是否以.css、.png这些资源后缀结尾,但是上图代码只是使用正则表达式判断.css,.png名字存在即可,导致传入诸如:../a.css/../,也可以绕过认证请求
- 使用startsWith()方法来判断url是否以/user/login,/user/registerUser等字符开头的时候,可以使用目录穿越来骗过判断,导致可以绕过认证请求
- 在Filter过滤器中没有看到xss过滤,sql注入等恶意字符的过滤。
SQL注入
在之前的pom.xml文件中发现该框架使用的是Mybits的数据库,找这类数据库的注入,直接在*Mapper.xml文件中全局搜索 ${
点一个进去查看:
这种like型的模糊查询使用了不安全的拼接,会直接参与SQL语句的编译,恶意构造会导致SQL注入
回溯判断对应的参数是否可控,搜索" selectByConditionUser ":
上面是一个数据处理层的接口类,继续搜索" UserMapperEx.selectByConditionUser " 看具体在哪里调用:
看到service层的调用,继续搜索" UserService.select( ":
可以看到参数userName,loginName是从search字段中获取,判断参数应该是可控的。下面的目的就是找前端接口,也就是Controller层的方法接口映射。
UserComponent类实现了ICmmonQuery这个接口,调用了此接口下的select方法
在同目录下发现文件InterfaceContainer.java这个接口文件:
这里调用初始化函数init(),将service组件压入configComponentMap中,后续调用getCommonQuery方法根据传进来的apiName获取对应的service组件(具体apiName跟对应的service组件映射如下:user->UserComponent)
InterfaceContainer对接口进行统一实例化处理,当apiName为user的时候,就会去实例化对应的UserComponent类,UserComponent类实现了ICmmonQuery这个接口,就会调用此接口ICmmonQuery类下的对应的方法。
所以InterfaceContainer类起到的是一个中转分发的作用,关键在于哪个类调用getCommonQuery方法,并传递相应的apiName进去,搜一下" getCommonQuery( ":
这里判断传进来的apiName是否为控,继续搜索 " CommonQueryManager ":
终于到Controller层了,上图已经很清楚,实例化CommonQueryManager类的一个对象configResourceManager去调用select方法,传入apiName,与参数集合paramterMap。
在环境上验证一下:
在IDEA的Console控制台可以看到相应的sql语句打印:
当然还有:
粗滤看了一下,SQL注入太多了,这里不再演示:
权限校验绕过
在审计准备阶段对拦截器@WebFilter进行分析的时候,发现存在权限绕过的情况,来验证第一种情况:
requestUrl中如果存在/doc.html,/register.html,/login.html字段就可以绕过认证请求
只要我们的请求url中有/doc.html,/register.html,/login.html字就可以绕过认证,payload可以这样写:/doc.html/../,/register.html/../,/login.html/../
正常无Sessionid请求:
会被重定向到登陆界面
加入payload请求:
成功绕过权限认证去访问接口。
来验证第二种情况:
传入verify()方法进行判断的时候,正确的使用应该选择endsWith()来判断url是否以.css、.png这些资源后缀结尾,但是上图代码只是使用正则表达式判断.css,.png名字存在即可,导致传入诸如:../a.css/../,也可以绕过认证请求
只要我们的请求url中有.css/.png/.jpg/.ico就可以绕过认证,payload可以这样写:/a.css/../,/a.jpg/../
正常无Sessionid请求:
会被重定向到登陆界面
加入payload请求:
成功绕过权限认证去访问接口。
来验证第三种情况:
使用startsWith()方法来判断url是否以/user/login,/user/registerUser等字符开头的时候,可以使用目录穿越来骗过判断,导致可以绕过认证请求
这个跟上面的利用方式一样,使用穿越来绕过,payload可以这样写:/user/login/../../,/user/registerUser/../../,/v2/api-docs../../
正常无Sessionid请求:
会被重定向到登陆界面
加入payload请求:
成功绕过权限认证去访问接口。
Fastjson反序列化命令执行
在审计准备的时候发现pom.xml加载了fastjson的组件,并且1.2.55确实是存在漏洞的版本。fastjson的核心在于调用了fastjson.JSON的parseObject函数将json字符串反序列化成对象,搜一下" parseObject( "调用:
调用的地方太多,这里有一个Util的文件,怀疑是不是通用的处理接口,跟进:
直接将search的内容传入parseObject方法中进行解析,继续搜索" StringUtil.getInfo( ",判断传进去的参数是否可控
看到这里就很舒服了,在SQL注入的时候已经分析过这里,search是从前端传进来的参数,来构造payload:
search={"@type":"java.net.Inet4Address","val":"yvnan3.dnslog.cn"}
url编码一下:
Dnslog平台已经收到了相应的请求
根据上面查找的结果,search接口全部都存在fastjson反序列化漏洞。
利用上面的登录权限绕过,可未授权命令执行。
存储型XSS
在之前对拦截器@WebFilter进行分析的时候,发现没有对传入的参数进行统一的xss过滤,代码中也并未看到相关的转义字符,前端接受恶意字符无过滤直接存入数据库中,如下在某收入单的备注参数处插入xss payload:
看一下数据库的语句:
可以看到无任何防护措施直接存入数据库。
在代码层看一下在从数据库读取数据的时候有没有对数据进行转义输出:
直接将查询出的结果以json的形式输出,也没有看到相关的过滤操作,导致存在存储型XSS漏洞
其他参数也同样存在此漏洞
越权密码重置
在系统管理 - 用户管理处 - 选择一个用户 - 重置密码
抓取数据包
传进去一个用户id,就可以重置该用户的密码
进入代码查看是否可以越权重置:
跟进service层具体查看一下resetPwd函数:
明显看到这里只禁止重置admin超管密码,但是并没有对当前重置密码的用户身份做判断,导致存在越权重置他人密码,这里来验证一下:
jsh用户对应的用户id为63
joker用户对应的用户id为132
jsh的用户权限>joker的权限
使用joker用户的身份去重置jsh用户密码:
利用上面的登录权限绕过,可未授权重置根据用户id重置用户密码(遍历id重置所有用户密码)。
越权删除用户
这个漏洞的利用和上一个漏洞很像
在系统管理 - 用户管理处 - 选择一个用户 - 删除用户信息
抓取数据包:
传进去一个用户ids,就可以删除该用户信息
进入代码查看是否可以越权删除:
跟进service层具体查看一下batDeleteUser函数:
接收参数ids,使用逗号,分割,调用userMapperEx.batDeleteOrUpdateUser()方法将ids参数拼接进sql语句进行删除,这里没有对当前执行删除用户操作的用户身份做判断,甚至没有禁止删除超级管理员,导致存在越权删除任意用户信息(包括管理员),这里来验证一下:
ceshi用户对应的用户id为135
joker跟ceshi是平级账户
经过测试可以垂直越权删除jsh等账号
利用上面的登录权限绕过,可未授权重置根据用户ids删除任意用户
越权修改用户信息
在系统管理 - 用户管理处 - 选择一个用户 - 编辑用户信息
抓取数据包:
关键参数是这个id跟info信息
跟进代码:
跟进service层具体查看一下updateUserAndOrgUserRel函数:
跟进checkUserNameAndLoginName()方法:
由于用户名和登录名的校验逻辑一样,这里只拿登录名检查来说,首先通过getLoginName()获取id对应的登录名,再通过getUserListByUserNameOrLoginName()方法,将登录名作为参数拼接进sql语句中获取user列表,从列表中取出id与前端传入的id进行比较,相同就可以更新数据。同样没有对当前执行更新用户数据的操作的用户身份做判断,尝试越权利用:
test123用户对应的用户id为131
joker跟test123是平级账户
成功越权修改他人的用户信息
当然这个ERP系统的漏洞还有很多漏洞没有列出来,包括越权查看邮件信息,未授权访问敏感界面等,期待后续大佬们的挖掘。
如果有其他不错的框架审计请在下面留言!