审计这个erp框架源码也是一波三折,下载好的源码运行登录后一直刷新卡BUG,idea控制台数据库查询日志框框刷,弄了好久,最后不明所以就好了。

漏洞复现刚开始路由都没整明白,并不是说它难,只是我不了解 Spring MVC 中的可变路径。

有些复现文章分析一半就没了。。

看了 Drunkbaby 审计的文章,感觉其审计的思路非常值得我学习。在代码审计中,除了通过代码了解漏洞产生的原因和利用方式,如何让我们去发现漏洞也至关重要。

师傅文章:Java 代码审计之华夏 ERP CMS v2.3

0x01 前言

之前总在公众号上刷到 华夏ERP出了什么什么漏洞,那它是什么?它是立志为中小企业提供开源好用的ERP软件,国产开源的ERP系统。

那什么是erp系统,ERP(企业资源计划)说太多更容易晕,直接引入网上的例子:

假设你的企业是一家制造业公司,有生产、销售、采购等多个环节。生产部门需要知道销售部门的订单信息,销售部门需要了解采购部门的库存情况,而采购部门则需要协调生产计划。ERP系统就像一位全知全能的管理者,帮助各个部门高效协同,保持信息畅通,让整个企业运转如钟。

本文章主要分析 2.3 版本的漏洞。

0x02 初看代码

拿到源码之后,首先看一下具体的框架结构

目录还是比较清晰,Springboot 框架,然后再瞅一瞅 pom.xml 文件,看看引入了啥样的依赖。

有一个 fastjson 库,该版本也是存在漏洞的,说不定可以打。

采用 Mybatis 作为对数据库操作的方式。可能会有sql注入漏洞存在的风险。比如一个查询请求发送过来,一般的执行流程为:controller–>service–>Mapper–>xml文件映射–>执行数据库操作。

接着一定要看一下 Filter,因为我们提交的数据或者是访问的请求都是要经过拦截器的,了解 filter 的拦截规则才能让我们更好的发现漏洞和利用漏洞。

就一个拦截器 LogCostFilter

声明了一个拦截器,接着就定义了 访问路径白名单。主要看一下 doFilter 方法的处理逻辑。

就是定义了一下基础的拦截配置,主要是针对哪些请求路径做不做处理。verify 方法判断请求的路径是否在这个可忽视的请求列表中,最后一个判断就是请求的路径开头是否存在允许请求路径中,如果是,则放行。

以上说的 ignoredList 和 allowUrls 在 init 方法做了初始化。

其实就是对上面两个变量值以#分别形成白名单数组,处理后的结果就如下所示

String[] allowUrls = {"/user/login", "/user/registerUser", "/v2/api-docs"};
String[] ignoredUrls = {"/user/login", "/user/registerUser", "/v2/api-docs"};

这个地方能不能做权限绕过呢?感觉应该可以。毕竟在最后一个判断中只用 startsWith 方法处理,没有对请求路径的后缀进行验证,加上 ../ 可以越权访问静态资源,至于动态资源,后续再分析。

0x03 漏洞审计

SQL注入

框架使用Mybatis,所以sql语句变量拼接为 ${},还有一种是预编译的写法:#{},所以要全局搜索 ${

就着重看 UserMapperEx.xml 文件,有两处存在sql注入漏洞,一个是 selectByConditionUser

全局搜索这个方法,看在哪些地方进行了调用

定位到了 select 方法,继续回溯,直到能够找到控制器方法调用为止。全局搜索会匹配很多种结果,注意方法的参数类型要保持一致。

追溯到 getUserList 方法,这个search就是从map里拿这个常量键名下的键值。跟进 getInfo 方法看一下逻辑。

search 是一个json格式的数据,反序列化后从指定的key中拿出value。最终定位到控制器方法 getList

这里路由是这个东西:/{apiName}/list,其实就是一个可变路径,可以自行分析一下,目的是为了更加灵活。接收search参数,并没有做过滤,所以此处是存在注入的。

登录到后台,访问 /user/list 路由,写入如下payload

{"userName":"","loginName":"' AND SLEEP(5)--"}

全部URL编码,

/user/list?search=%7b%22%75%73%65%72%4e%61%6d%65%22%3a%22%22%2c%22%6c%6f%67%69%6e%4e%61%6d%65%22%3a%22%27%20%41%4e%44%20%53%4c%45%45%50%28%35%29%2d%2d%22%7d&pageSize=1&currentPage=12

发包后延迟了有个5秒出现响应,在控制台上其实也能够看到执行的sql语句。

刚才看的mapper还有一处

用同样的方式回溯,

但是到控制器方法中参数不可控了,有的文章分析到这判断存在sql注入就没有下文了,这里真的会造成sql注入吗?

存储型XSS

这个需要结合黑盒去看,也可以去看一些静态的html文件。

在后台的财务管理处可以添加收入单

在备注框中添加 javascript 弹窗代码,保存后出现弹窗

抓包看 处理逻辑的路由在 /accountHead/add 其实这里就是一个可变路径。

post 传入 json 数据,然后插入数据表,就不往下跟进函数了,比较简单。然后再看看数据渲染的部分。它这包有点难抓,有好多与身份有关的包

路径为 /accountHead/list,同样的也返回 json 数据。

其实我还想弄清楚是怎么把查询出来的json数据渲染到前端模板上的,但是太懒了,后续有时间再分析。

fastjson 反序列化

之前分析依赖的时候也看到引入了 fastjson 1.2.55 版本的依赖,在分析 SQL 注入的时候也看到了 fastjson 的漏洞函数 parseObject ,

使用DNSLog验证反序列化是否存在。

{"@type":"java.net.Inet4Address","val":"0ce7bb2481.ipv6.1433.eu.org."}

全部URL编码

在 DNSLOG 平台上接收到了URL的解析请求。

至于能不能rce,感觉应该是可以的,但是没有找到合适的payload。

权限绕过

由于 Filter 设置了路径白名单,那么可以利用相对路径访问 没有权限访问的文件,达到越权。比如访问如下路径:

login.html/../home.html

能够访问,但只是静态页面,也没有后台的数据,没什么用。那么能不能访问到后台的路由呢?

应该是没办法绕过了。

水平越权

这类越权漏洞跟业务功能的关联性很高的,比如说登录,修改,重置密码等等。代码中有修改密码的功能。

定位到 重置密码功能的代码处

调用 Service层的 resetPwd 方法来重置密码,除了那个固定密码,还有一个id,继续跟进方法。

通过 id 判断是哪一个用户,id 可控,那不就可以指定任意用户了吗。(除了默认管理员)添加两个管理员账户。

新添加一个 XiLitter 用户,

看样子修改成功了,查看数据库,密码已经成功修改掉了。

除了重置密码,还有修改密码也存在 水平越权,能够修改掉任意用户的密码,默认管理员的不行。

同样是接收用户id 通过用户id获取这个用户信息,然后调用 updateUserByObj 方法修改用户密码,具体操作不再演示。

0x04 结语

审计注重思路。