Ohhnews

分类导航

$ cd ..
foojay原文

深度解析:Thymeleaf 模板注入漏洞 (CVE-2026-40478)

#thymeleaf#安全漏洞#模板注入#java安全#代码审计

目录

Thymeleaf 漏洞的 CVSS 评分为 9.1,这确实值得关注。但在你兴师动众并将其称为下一个 Log4shell 之前,请先阅读本文。

CVE-2026-40478 是一个 Thymeleaf 服务器端模板注入(SSTI)漏洞,由渗透测试人员 Dawid Bakaj 发现。Thymeleaf 是 Java 中用于服务器端网页渲染的模板引擎。该漏洞利用制表符绕过了通常用于防止任意代码执行的沙盒。如果被利用,确实可能导致远程代码执行(RCE)。

但最关键的一点是:该漏洞仅在你的代码本身存在不当操作时才会产生影响。

沙盒的防护范围

Thymeleaf 拥有一个安全沙盒,用于限制 SpEL(Spring 表达式语言)在评估动态内容时的操作权限。沙盒存在的特定场景是:当用户控制的输入以某种方式进入 Thymeleaf 的表达式引擎时。

如果你的代码中从未出现这种情况,那么沙盒机制根本不会介入,此 CVE 也不会对你构成威胁。使用 Thymeleaf 的正确方式很简单:将用户输入放入数据模型中,模板文件则作为静态 HTML 文件存在。

Java 代码示例:

$ java
@GetMapping("/greet/safe")
public String greetSafe(@RequestParam String name, Model model) {
   model.addAttribute("name", name);
   return "greet";
}

Thymeleaf 模板:

$ markup
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<body>
   <p th:text="${name}">Default name</p>
</body>
</html>

Thymeleaf 会渲染 name 的值,而不会将其解析为表达式。攻击者可以随意发送任何载荷作为 name 的值,但不会触发任何异常。这就是设计使然,也是 Thymeleaf 的标准工作方式。

滥用模板引擎

当开发者允许用户输入直接进入表达式引擎时,该漏洞才变得可利用。如果发生这种情况,说明开发者在使用框架时存在误用。尽管这在技术上可行,但在使用 Spring Boot 环境下的 Thymeleaf 时,实现这一点相当困难。

$ java
@ResponseBody
@GetMapping("/greet/unsafe")
public String greetUnsafe(@RequestParam String name, Model model) {
   Context context = new Context();
   SpringTemplateEngine templateEngine = new SpringTemplateEngine();
   templateEngine.setTemplateResolver(new StringTemplateResolver());
   context.setVariable(name, name);
   String template = "<p th:text=\"'Hello ' + ${"+ name + "}\">...</p>";
   return templateEngine.process(template, context);
}

如上所示,我必须手动创建 templateEngine 的实例并设置解析器,而这通常是由 Spring 自动完成的。此外,我还需要在上下文中设置变量才能使正常的业务逻辑运行。

但在上述示例中,用户的输入被传递给了表达式引擎,这正是触发该 CVE 的前提条件。

另一种类似的误用模式是动态视图解析。开发者可能不是直接构建模板字符串,而是根据用户输入来解析视图:

$ java
@GetMapping("/page/unsafe")
@ResponseBody
public String showPageUnsafe(@RequestParam String page) {
   Context context = new Context();
   SpringTemplateEngine templateEngine = new SpringTemplateEngine();
   StringTemplateResolver resolver = new StringTemplateResolver();
   resolver.setTemplateMode(TemplateMode.TEXT);
   templateEngine.setTemplateResolver(resolver);
   context.setVariable(page, page);
   String template = "[[${" + page + "}]]";
   return templateEngine.process(template, context);
}

这个用例在逻辑上是“合法”的,即通过一个端点服务多个页面,但此时用户输入会影响 Thymeleaf 的解析过程。同样的误用模式,导致了同样的潜在漏洞。请注意,为了达到这种“效果”,需要进行大量手动配置。而安全版本仅需三行代码:

$ java
@GetMapping("/page/safe")
public String showPageSafe(@RequestParam String page) {
   return page;
}

制表符(Tab)如何绕过 Thymeleaf 沙盒

在出现误用模式的前提下,漏洞利用过程非常直接。

沙盒会检查 new 关键字(即 new 后面紧跟空格)以阻止对象实例化。而绕过方法是发送 new[TAB]。检查程序因为找不到 new(后跟空格)而放行,但 SpEL 会将制表符视为有效的空白字符并正确解析。

攻击载荷如下所示:

new[TAB]org.springframework.core.io.FileSystemResource('shell.jsp').getOutputStream()

在关键字检查之后,系统还会运行一个类型黑名单,拦截 java.* 类。然而,Spring 类并未包含在黑名单中。因此,不完整的黑名单结合对 new 关键字弱化的过滤,使得 FileSystemResource 等类可以被加载,从而允许攻击者在磁盘上写入文件。理论上,攻击者可以借此上传一个 JSP 文件,通过 ProcessBuilder 执行远程代码。

两道防线同时失效:关键字检查中的空白字符处理缺陷,以及过于狭窄的黑名单。EndorLabs 对此次漏洞利用提供了更详细的分析

你可以访问 GitHub 仓库 查看安全代码与不安全代码的对比示例。

你需要做什么

优先打补丁。 无论你是否认为自己的代码受到影响,都请升级到 Thymeleaf 3.1.4。不要等待审计结果。

如果你通过 Spring Boot 使用 Thymeleaf,只需将 Spring Boot starter parent 更新到最新版本(目前为 3.5.14 或 4.0.6)。如果你的版本落后较多,建议查看 OpenRewrite 方案,迁移至 Spring Boot 3.5Spring Boot 4.0

更新完成后,请检查你的代码,查看是否存在模板字符串或页面解析直接根据用户输入动态生成的情况。如果存在,这就是误用模式。请从根本上修复代码,而不仅仅是升级库版本。

CVSS 9.1 分数真实存在,但有前提条件

高危的 CVSS 评分是基于“前提条件已满足”的假设得出的。如果你的代码确实将用户输入喂给了 Thymeleaf 的表达式引擎,那么 9.1 的评分是准确的,且影响严重。如果你的代码没有这样做,那么你不会受到该 CVE 本身的直接影响。但即便如此,你依然应该打补丁!

团队在进行安全审计时,不应只问“我们运行的是什么版本的 Thymeleaf?”,而应问“我们是否根据请求数据动态构建了视图名称或模板表达式?”

请将 Thymeleaf 升级到 3.1.4 或更高版本,然后确认上述问题的答案。无论结果如何,请务必在开发阶段持续扫描依赖项,并使用 Snyk Open Source 在生产环境中进行监控。最棒的是,你可以免费开始使用。