本文记录了一次完整的华夏ERP代码审计过程,其中使用了CodeQL来进行漏洞利用链的挖掘
一、环境搭建
使用的版本为:v2.3
下载源码:https://github.com/jishenghua/jshERP/tree/v2.3
先导入IDEA,让他自动导入依赖
创建数据库jsh_erp,docs/jsh_erp.sql 导入数据库
运行src\main\java\com\jsh\erp\ErpApplication.java
进入登录页面 jsh 123456
二、第三方组件扫描
使用我自己开发的PomEye进行第三方组件扫描
PomEye介绍:https://www.freebuf.com/sectool/366383.html
PomEye下载地址:https://github.com/feiweiliang/PomEye
发现引用了有漏洞的依赖fastjson
待会儿使用CodeQL找到调用fastjson的parse或parseObject方法处,并且parseObject的参数可控
三、架构分析
对一些关键的类进行分析
config:Swagger配置类、Mapper路径配置、分页拦截器
constants:一些常量,例如SERVICE_SUCCESS_CODE = 200、USER_IS_MANAGER = 0
filter:用户登录校验过滤器 (requestUrl.startsWith(url))
controller:
AccountController:账户类
findBySelect:查找下拉框中所有账户的id和用户名,根据id排序
getAccount:获取所有的账户
findAccountInOutList: 查找主表出入库涉及的账户
updateAmountIsDefault:
DepotController:对仓库操作的类
FunctionController: 操作用户的功能相关类
InOutItemController: 查找收支项目、删除收支项目
UserController:用户登录、退出、重置密码、修改密码、获得所有用户信息、新增用户
SystemConfigController: 删除系统配置信息、
SupplierController:更新供应商的预付款、查找客户信息、查找供应商信息、导入导出excel表格
exception:自定义异常类,方便调试
utils:工具类,稍后分析
四、白盒审计
1. 越权(任意密码重置、删除)
UserController类的resetPwd方法
@PostMapping(value = "/resetPwd")
public String resetPwd(@RequestParam("id") Long id,
HttpServletRequest request) throws Exception {
Map<String, Object> objectMap = new HashMap<String, Object>();
String password = "123456";
String md5Pwd = Tools.md5Encryp(password);
int update = userService.resetPwd(md5Pwd, id);
}
UserController类的deleteUser方法
@PostMapping("/deleteUser")
@ResponseBody
public Object deleteUser(@RequestParam("ids") String ids)throws Exception{
JSONObject result = ExceptionConstants.standardSuccess();
userService.batDeleteUser(ids);
return result;
}
2. 信息泄露
UserController类的getUserList
@GetMapping(value = "/getUserList")
public JSONArray getUserList(HttpServletRequest request)throws Exception {
JSONArray dataArray = new JSONArray();
try {
List<User> dataList = userService.getUser();
if (null != dataList) {
for (User user : dataList) {
JSONObject item = new JSONObject();
item.put("id", user.getId());
item.put("userName", user.getUsername());
dataArray.add(item);
}
}
} catch(Exception e){
e.printStackTrace();
}
return dataArray;
}
3. 鉴权绕过(未授权)
在LogCostFilter类中的doFilter方法
//getRequestURI可以被绕过,绕过方式:/../../
String requestUrl = servletRequest.getRequestURI();
if (requestUrl != null && (requestUrl.contains("/doc.html") ||
requestUrl.contains("/register.html") || requestUrl.contains("/login.html"))) {
chain.doFilter(request, response);
return;
}
可以构造/register.html/../../user/getUserList
还有一种利用静态资源绕
initParams = {@WebInitParam(name = "ignoredUrl", value = ".css#.js#.jpg#.png#.