基于codeql的newbee-mall漏洞挖掘
字数 1188 2025-09-23 19:27:46
基于CodeQL的NewBee-Mall漏洞挖掘技术文档
一、项目背景
NewBee-Mall是一个拥有11.4k star的开源商城项目(GitHub地址:https://github.com/newbee-ltd/newbee-mall)。本文重点介绍如何使用CodeQL自动化挖掘其ABAC权限漏洞。
二、垂直权限管理分析
2.1 配置机制
项目采用SpringMVC拦截器实现垂直权限管理,核心配置位于:
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(adminLoginInterceptor)
.addPathPatterns("/admin/**")
.excludePathPatterns("/admin/login");
}
2.2 历史漏洞CVE-2020-23448
漏洞成因:
- 原始代码使用
uri.startsWith("/admin")进行路径匹配 - 可通过
//admin进行路径解析绕过(利用HTTP解析特性)
修复方案:
// 修复前:存在绕过风险
uri.startsWith("/admin")
// 修复后:使用servlet路径判断
request.getServletPath().startsWith("/admin")
三、水平权限管理分析
3.1 鉴权机制特征
典型水平权限判断代码模式:
// 从session获取用户身份
Long userId = (Long) httpSession.getAttribute(Constants.MALL_USER_SESSION_KEY);
// 传入服务层进行ABAC比较
newBeeMallShoppingCartService.updateNewBeeMallCartItem(cartItem, userId);
3.2 CodeQL检测策略
3.2.1 核心检测逻辑
// 检测调用httpSession.getAttribute的方法
predicate callsHttpSessionGetAttribute(Method m) {
exists(MethodAccess call |
call.getMethod().getName() = "getAttribute" and
call.getQualifier().getType().getName() = "HttpSession" and
call.getEnclosingCallable() = m
)
}
// 判断是否为控制器方法
predicate isController(Method m) {
exists(Annotation a |
a.getType().getName() = "RequestMapping" and
m.getAnAnnotation() = a
)
}
3.2.2 过滤规则
需要排除以下情况:
- Admin控制器:类包含
@RequestMapping("/admin")注解 - VO层方法:仅负责渲染,不处理业务逻辑
- GET请求路由:渲染模板的方法
3.2.3 完整QL查询
from Method m
where
isController(m) and
not callsHttpSessionGetAttribute(m) and
not m.getDeclaringType().getAnAnnotation().getType().getName() = "RequestMapping" and
not m.getName().matches("%GET%")
select m
四、漏洞实例分析
4.1 支付漏洞+水平越权(paySuccess方法)
漏洞位置:MallOrderController.paySuccess
@PostMapping(value = "/paySuccess")
public Result paySuccess(@RequestBody PaySuccessVO paySuccessVO, HttpSession httpSession) {
// 缺失用户身份验证
String payResult = newBeeMallOrderService.paySuccess(paySuccessVO);
return ResultGenerator.genSuccessResult(payResult);
}
服务层代码:
public String paySuccess(PaySuccessVO paySuccessVO) {
NewBeeMallOrder order = newBeeMallOrderMapper.selectByOrderNo(paySuccessVO.getOrderNo());
// 直接修改订单状态,无用户权限校验
order.setOrderStatus((byte) 1);
newBeeMallOrderMapper.updateByPrimaryKey(order);
return "success";
}
利用方式:
- 任意用户可通过传入他人订单号修改订单状态为"已支付"
- 数据库验证确认漏洞存在
4.2 用户信息修改漏洞(CVE-2023-30216)
漏洞位置:updateInfo方法
@PostMapping("/updateInfo")
public Result updateInfo(@RequestBody MallUserVO mallUserVO, HttpSession httpSession) {
// 缺失用户身份验证
String updateResult = newBeeMallUserService.updateUserInfo(mallUserVO);
return ResultGenerator.genSuccessResult(updateResult);
}
服务层代码:
public String updateUserInfo(MallUserVO mallUserVO) {
NewBeeMallUser user = new NewBeeMallUser();
// 直接根据传入JSON中的ID修改用户信息
user.setUserId(mallUserVO.getUserId());
user.setNickName(mallUserVO.getNickName());
newBeeMallUserMapper.updateByPrimaryKeySelective(user);
return "success";
}
漏洞影响:
- 可通过修改JSON中的userId参数任意修改用户信息
- 已获得CVE-2023-30216编号
4.3 验证码爆破漏洞
漏洞机理:
@GetMapping("/kaptcha")
public void defaultKaptcha(HttpSession httpSession) {
// 生成验证码存入session
String verifyCode = producer.createText();
BufferedImage image = producer.createImage(verifyCode);
httpSession.setAttribute("verifyCode", verifyCode);
// 但验证码更换依赖前端访问此路由
}
漏洞特征:
- 验证码生成后存储于Session
- 但验证码更换需要前端主动访问
/kaptcha路由 - 攻击者可重复使用同一验证码进行爆破
五、CodeQL实战技巧
5.1 精确方法过滤
// 排除Admin控制器
not m.getDeclaringType().getAnAnnotation().getType().getName() = "RequestMapping"
// 排除GET方法
not m.getName().matches("%GET%")
// 排除VO层方法
not m.getDeclaringType().getName().matches("%VO%")
5.2 结果优化策略
- 初始检测55个结果,经过滤后聚焦关键漏洞点
- 需结合业务逻辑理解排除误报
六、总结与建议
6.1 漏洞挖掘经验
- 不同项目权限实现方式各异,需定制化分析
- CodeQL可显著降低人工审计复杂度
- 需结合业务逻辑理解优化检测规则
6.2 修复建议
- 所有操作需进行用户身份验证
- 验证码应设置有效期和强制更新机制
- 建议采用统一的权限校验框架
6.3 参考资源
- https://github.com/newbee-ltd/newbee-mall/issues/100
- https://github.com/newbee-ltd/newbee-mall/issues/101
本文档基于实际漏洞挖掘经验整理,所有漏洞均已提交至VulDB。希望为业务安全漏洞挖掘提供有效技术参考。