HVV前沿 | 积木报表组件(Jeecg-Boot)权限绕过漏洞分析

2024-08-28 02:20

本文主要是介绍HVV前沿 | 积木报表组件(Jeecg-Boot)权限绕过漏洞分析,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

漏洞概述

JeecgBoot是适用于企业 Web 应用程序的国产Java低代码平台。

近期,网宿安全演武实验室监测到JeecgBoot JimuReport 1.7.8版本及之前版本存在安全漏洞(网宿评分:高危):该漏洞源于权限设置不安全,攻击者在请求中添加特定参数,即可绕过授权机制,从而查看敏感信息并通过 Aviator 表达式注入在1.6.0版本后实现远程代码执行。

目前该漏洞POC状态已在互联网公开,建议用户尽快做好自查及防护。

受影响版本

JimuReport <= 1.7.8

漏洞分析

首先看权限绕过问题的成因,以历史漏洞接口/jeecg-boot/jmreport/queryFieldBySql为例。

显然这里已经做了权限限制,而/jeecg-boot/jmreport/**具体的拦截器实现在

org.jeecg.modules.jmreport.config.firewall.interceptor.JimuReportTokenInterceptor#preHandle()。

这是一个返回值为boolean类型的public方法,主要对用户请求进行了一些预处理和判断,那么只要在校验token前使其返回true,即可实现权限绕过。

当我们请求/jeecg-boot/jmreport/queryFieldBySql时,该方法先进行了xss检测。

String var4 = d.i(request.getRequestURI().substring(request.getContextPath().length()));
log.debug("JimuReportInterceptor check requestPath = " + var4);
int var5 = 500;
if (n.a(var4)) {log.error("请注意,请求地址有xss攻击风险!" + var4);this.backError(response, "请求地址有xss攻击风险!", var5);return false;
}

public class n {private static final String[] a = new String[]{"<", "%3C", ">", "%3E", "\\(", "%28", "\\)", "%29", "'", "eval\\((.*)\\)"};private static Pattern[] b = new Pattern[]{Pattern.compile("<script>(.*?)</script>", 2), Pattern.compile("src[\r\n]*=[\r\n]*\\'(.*?)\\'", 42), Pattern.compile("src[\r\n]*=[\r\n]*\\\"(.*?)\\\"", 42), Pattern.compile("</script>", 2), Pattern.compile("<script(.*?)>", 42), Pattern.compile("eval\\((.*?)\\)", 42), Pattern.compile("e\u00adxpression\\((.*?)\\)", 42), Pattern.compile("javascript:", 2), Pattern.compile("vbscript:", 2), Pattern.compile("onload(.*?)=", 42)};...private static boolean a(String var0, Pattern var1) {Matcher var2 = var1.matcher(var0);return var2.find();}...
}

并且对/jmreport/shareView/和JimuNoLoginRequired注解的路由直接放行。

if (var4.contains("/jmreport/shareView/")) {return true;
}

JimuNoLoginRequired var9 = (JimuNoLoginRequired)var8.getAnnotation(JimuNoLoginRequired.class);
if (j.d(var9)) {return true;
}

接着通过jwt验证用户传入token的合法性。

try {var10 = this.verifyToken(request);
} catch (Exception var14) {
}

public static boolean verifyToken(String token, CommonAPI commonApi, RedisUtil redisUtil) {if (StringUtils.isBlank(token)) {throw new JeecgBoot401Exception("token不能为空!");}// 解密获得username,用于和数据库进行对比String username = JwtUtil.getUsername(token);if (username == null) {throw new JeecgBoot401Exception("token非法无效!");}// 查询用户信息LoginUser user = TokenUtils.getLoginUser(username, commonApi, redisUtil);//LoginUser user = commonApi.getUserByName(username);if (user == null) {throw new JeecgBoot401Exception("用户不存在!");}// 判断用户状态if (user.getStatus() != 1) {throw new JeecgBoot401Exception("账号已被锁定,请联系管理员!");}// 校验token是否超时失效 & 或者账号密码是否错误if (!jwtTokenRefresh(token, username, user.getPassword(), redisUtil)) {throw new JeecgBoot401Exception(CommonConstant.TOKEN_IS_INVALID_MSG);}return true;
}

显然攻击者未持有合法token,继续往下走发现如果访问的路由在白名单内即放行。

if (!var10) {if (this.jimuReportShareService.isSharingEffective(var4, request)) {return true;} else {...}
}

GET_QUERY_INFO("/jmreport/getQueryInfo"),
SHARE_VERIFICATION("/jmreport/share/verification"),
ADD_VIEW_COUNT("/jmreport/addViewCount/"),
SHOW_DATA("/jmreport/show"),
EXPORT_PDF_STREAM("/jmreport/exportPdfStream"),
EXPORT_ALL_EXCEL_STREAM("/jmreport/exportAllExcelStream"),
CHECK_PARAM("/jmreport/checkParam/"),
QUERYMAP_BY_CODE("/jmreport/map/queryMapByCode"),
QUREST_SQL("/jmreport/qurestSql"),
QUREST_API("/jmreport/qurestApi"),
GET_CHAR_DATA("/jmreport/getCharData");

但/jeecg-boot/jmreport/queryFieldBySql未在其中,与此同时我们发现接下来的else分支存在突破点,只要用户在请求中携带previousPage参数,且使isShareingToken方法返回true,即可成功绕过拦截器。

else {String var16 = request.getParameter("previousPage");if (j.d(var16)) {if (this.jimuReportShareService.isShareingToken(var4, request)) {return true;}...
}

步入isShareingToken方法。

public boolean isShareingToken(String requestPath, HttpServletRequest request) {String var3 = request.getHeader("JmReport-Share-Token");String var4 = "";if (j.c(var3)) {var3 = request.getParameter("shareToken");}String var5 = request.getParameter("jmLink");if (j.d(var5)) {try {byte[] var6 = Base64Utils.decodeFromString(var5);String var7 = new String(var6);String[] var8 = var7.split("\\|\\|");if (ArrayUtils.isNotEmpty(var8) && var8.length == 2) {var3 = var8[0];var4 = var8[1];}} catch (IllegalArgumentException var9) {a.error("解密失败:" + var9.getMessage());a.error(var9.getMessage(), var9);return false;}}if (j.c(var3)) {return false;} else {JimuReportShare var10 = this.jimuReportShareDao.getShareByShareToken(var3);if (var10 != null) {var10 = this.compareToDate(var10);if (!"0".equals(var10.getStatus())) {return false;}}if (requestPath.startsWith("/jmreport/view")) {if (!j.d(var4)) {return false;}Long var11 = this.jimuReportLinkDao.selectLinkCountByLinkId(var4);if (null != var11 && var11 > 0L) {return true;}}return true;}
}

不难发现其先提取解码后的jmLink参数值赋给var3、var4,并交由getShareByShareToken判断share_token有效性,但显然攻击者传入的值,在数据库中是查不到对应的 JimuReportShare 对象的,即var10为null。

@Sql("select status,share_token,last_update_time,term_of_validity from jimu_report_share where share_token = :shareToken")
JimuReportShare getShareByShareToken(@Param("shareToken") String var1);

而这里之后又辨别了路由是否以/jmreport/view开头,如果不是则返回true,显然我们请求的路由不包括在内。

所以实际环境验证成功。

接着根据漏洞情报(https://github.com/jeecgboot/JimuReport/issues/2848),可定位至/jeecg-boot/jmreport/save和/jeecg-boot/jmreport/show两个接口。

对应函数分别为:

org.jeecg.modules.jmreport.desreport.service.a.e#saveReport()
org.jeecg.modules.jmreport.desreport.service.a.e#show()

前者的作用是将用户的报表数据保存至数据库,后者则是通过前面传入的id提取数据。

@GetMapping({"/show"})
public Result<?> a(@RequestParam(name = "id") String var1, HttpServletRequest var2) {a.info("--------进入show方法-----------, id = " + var1);String var3 = var2.getParameter("params");try {return this.jmReportDesignService.show(var1, var3, (List)null);} catch (Exception var5) {a.error(var5.getMessage(), var5);return Result.error(var5.getMessage());}
}

至于漏洞字段,则位于org.jeecg.modules.jmreport.desreport.express.a#c(),这里限制了传入格式,即只解析”text”字段中,通过=()包含的值。

String var9 = var8.getString("text");
if (var9 != null && !var9.isEmpty()) {var9 = var9.trim();Pattern var10 = Pattern.compile("^([^=]*)(=[A-Z_.a-z0-9]*\\(.*\\))(([*/+\\-][0-9]+)*)(.*)");Matcher var11 = var10.matcher(var9);String var12;String var15;if (var11.find()) {var12 = var11.group(2);String var13 = var12.substring(var12.startsWith("=") ? 1 : 0, var12.indexOf("("));if (ExpressUtil.f.contains(var13.toLowerCase())) {String var14 = var11.group(1);var15 = var11.group(3);String var16 = var11.group(5);var8.put("jeTempText", var14 + "$expVal$" + var16);var9 = var12.trim() + var15.trim();var8.put("text", var9);}}
...

最终编译攻击者传入的payload。

public Expression compile(String expression, boolean cached) {return this.compile(expression, expression, cached);
}

漏洞复现

首先通过/jeecg-boot/jmreport/save,构造恶意报表数据。

再借助/jeecg-boot/jmreport/show根据id提取数据,即可触发该漏洞。

修复方案

不推荐直接将脚本的执行开放到任何不可信的环境,如果确实需要,可以参考https://www.yuque.com/boyan-avfmj/aviatorscript/ou23gy#elOSu,设置必要的选项。

产品支持

网宿全站防护-WAF已支持对该漏洞利用攻击的防护,并持续挖掘分析其他变种攻击方式和各类组件漏洞,第一时间上线防护规则,缩短防护“空窗期”。

这篇关于HVV前沿 | 积木报表组件(Jeecg-Boot)权限绕过漏洞分析的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1113454

相关文章

Spring Security 基于表达式的权限控制

前言 spring security 3.0已经可以使用spring el表达式来控制授权,允许在表达式中使用复杂的布尔逻辑来控制访问的权限。 常见的表达式 Spring Security可用表达式对象的基类是SecurityExpressionRoot。 表达式描述hasRole([role])用户拥有制定的角色时返回true (Spring security默认会带有ROLE_前缀),去

JS常用组件收集

收集了一些平时遇到的前端比较优秀的组件,方便以后开发的时候查找!!! 函数工具: Lodash 页面固定: stickUp、jQuery.Pin 轮播: unslider、swiper 开关: switch 复选框: icheck 气泡: grumble 隐藏元素: Headroom

性能分析之MySQL索引实战案例

文章目录 一、前言二、准备三、MySQL索引优化四、MySQL 索引知识回顾五、总结 一、前言 在上一讲性能工具之 JProfiler 简单登录案例分析实战中已经发现SQL没有建立索引问题,本文将一起从代码层去分析为什么没有建立索引? 开源ERP项目地址:https://gitee.com/jishenghua/JSH_ERP 二、准备 打开IDEA找到登录请求资源路径位置

如何在页面调用utility bar并传递参数至lwc组件

1.在app的utility item中添加lwc组件: 2.调用utility bar api的方式有两种: 方法一,通过lwc调用: import {LightningElement,api ,wire } from 'lwc';import { publish, MessageContext } from 'lightning/messageService';import Ca

SWAP作物生长模型安装教程、数据制备、敏感性分析、气候变化影响、R模型敏感性分析与贝叶斯优化、Fortran源代码分析、气候数据降尺度与变化影响分析

查看原文>>>全流程SWAP农业模型数据制备、敏感性分析及气候变化影响实践技术应用 SWAP模型是由荷兰瓦赫宁根大学开发的先进农作物模型,它综合考虑了土壤-水分-大气以及植被间的相互作用;是一种描述作物生长过程的一种机理性作物生长模型。它不但运用Richard方程,使其能够精确的模拟土壤中水分的运动,而且耦合了WOFOST作物模型使作物的生长描述更为科学。 本文让更多的科研人员和农业工作者

MOLE 2.5 分析分子通道和孔隙

软件介绍 生物大分子通道和孔隙在生物学中发挥着重要作用,例如在分子识别和酶底物特异性方面。 我们介绍了一种名为 MOLE 2.5 的高级软件工具,该工具旨在分析分子通道和孔隙。 与其他可用软件工具的基准测试表明,MOLE 2.5 相比更快、更强大、功能更丰富。作为一项新功能,MOLE 2.5 可以估算已识别通道的物理化学性质。 软件下载 https://pan.quark.cn/s/57

衡石分析平台使用手册-单机安装及启动

单机安装及启动​ 本文讲述如何在单机环境下进行 HENGSHI SENSE 安装的操作过程。 在安装前请确认网络环境,如果是隔离环境,无法连接互联网时,请先按照 离线环境安装依赖的指导进行依赖包的安装,然后按照本文的指导继续操作。如果网络环境可以连接互联网,请直接按照本文的指导进行安装。 准备工作​ 请参考安装环境文档准备安装环境。 配置用户与安装目录。 在操作前请检查您是否有 sud

线性因子模型 - 独立分量分析(ICA)篇

序言 线性因子模型是数据分析与机器学习中的一类重要模型,它们通过引入潜变量( latent variables \text{latent variables} latent variables)来更好地表征数据。其中,独立分量分析( ICA \text{ICA} ICA)作为线性因子模型的一种,以其独特的视角和广泛的应用领域而备受关注。 ICA \text{ICA} ICA旨在将观察到的复杂信号

多线程解析报表

假如有这样一个需求,当我们需要解析一个Excel里多个sheet的数据时,可以考虑使用多线程,每个线程解析一个sheet里的数据,等到所有的sheet都解析完之后,程序需要提示解析完成。 Way1 join import java.time.LocalTime;public class Main {public static void main(String[] args) thro

【软考】希尔排序算法分析

目录 1. c代码2. 运行截图3. 运行解析 1. c代码 #include <stdio.h>#include <stdlib.h> void shellSort(int data[], int n){// 划分的数组,例如8个数则为[4, 2, 1]int *delta;int k;// i控制delta的轮次int i;// 临时变量,换值int temp;in