一.什么是FreeMarker
FreeMarker 是一款模板引擎,即一种基于模板和需要改变的数据, 并用来生成输出文本( HTML 网页,电子邮件,配置文件,源代码等)的通用工具,其模板语言为 FreeMarker Template Language (FTL)。它不是面向最终用户的,而是一个Java类库,是一款程序员可以嵌入他们所开发产品的组件。

常用的几种类型:
文本
包括HTML标签与静态文本等静态内容,会原样输出;
插值
这部分的输出会被计算的数据来替换,使用${}这种语法;
标签
给FreeMarker的指示,可以简单与指令等同,不会打印在内容中,比如<#assign name='bob'>;
注释:由<#–和–>表示,不会被freemarker处理
二. 原理及漏洞演示
2.1 注入原理
服务端接收了用户的恶意输入以后,未经任何处理就将其作为 Web 应用模板内容的一部分,模板引擎在进行目标编译渲染的过程中,执行了用户插入的可以破坏模板的语句,因而可能导致了敏感信息泄露、代码执行、GetShell 等问题。其影响范围主要取决于模版引擎的复杂性。
2.2 漏洞演示
演示代码:
Hello.ftl:

FreeMarkerController.java

由上述代码可以看到服务端接收用户传入的参数,遍历模板内容,并将其加入StringTemplateLoader模板加载器。
于是我们可以将恶意模板内容发送给服务端
{"hello.ftl": "<#assign ex=\"freemarker.template.utility.Execute\"?new()> ${ ex(\"calc.exe\") }"}

三. 发现漏洞与利用
3.1 发现漏洞
1、使用${7*7}或#{7*7},如果返回49,则基本可以确认存在模板注入漏洞。
2、使用特殊字符等方式去尝试报错:
${{<%[%’”}}%\
如果引发了报错,则可能存在问题,同时还可能爆出模板引擎是什么,有时甚至是哪个版本。
3、可以参考portswigger提供的模板引擎的识别方法。

绿色和红色箭头分别代表“成功”和“失败”响应。在某些情况下,单个有效负载可以有多个不同的成功响应——例如,探测{{7*'7'}}将导致 Twig 中的 49,Jinja2 中的 7777777,如果没有使用模板语言,则两者都不会。
3.2 漏洞利用
内建函数new:
这是用来创建一个确定的 TemplateModel 实现变量的内建函数。
在 ? 的左边你可以指定一个字符串, 是 TemplateModel 实现类的完全限定名。结果是调用构造方法生成一个方法变量,然后将新变量返回。
用户可以通过实现 TemplateModel 来用 new 创建任意 Java 对象。此外,模板作者甚至还可以为没有实现TemplateModel的类触发静态初始化。
这里列举freemarker.template.utility包中存在三个可利用的类,分别为Execute类、ObjectConstructor类、JythonRuntime类。
参考payload:
1.<#assign value="freemarker.template.utility.Execute"?new()>${value("calc.exe")}
2.[#ftl][#assign value= "freemarker.template.utility.Execute"?new()]${ value("calc.exe")}
3.${"freemarker.template.utility.Execute"?new()("calc.exe")}
4.<#assign value="freemarker.template.utility.ObjectConstructor"?new()>${value("java.lang.ProcessBuilder","calc.exe").start()}
5.<#assign value="freemarker.template.utility.JythonRuntime"?new()><@value>import os;os.system("calc.exe")
内建函数api:
如果value本身支持这个额外的特性, value?api 提供访问 value 的API (通常是 Java API),比如 value?api.someJavaMethod()
常见的两种利用方式:
1、通过内建函数api获取类的classloader然后加载恶意类
2、通过Class.getResource的返回值来访问URI对象。URI对象包含toURL和create方法,通过这两个方法创建任意URI,然后用toURL访问任意URL。
参考payload:
(返回为10进制)
沙箱绕过(仅适用于版本低于2.3.30):
通过对象ProtectionDomain的getClassLoader方法,加载freemarker.template.ObjectWrapper类的DEFAULT_WRAPPER字段来获取远程代码执行。
四. FreeMarker安全机制
上述三种利用方式,FreeMarker也作出了安全措施。
l 从2.3.17版本开始使用Configuration.setNewBuiltinClassResolver(TemplateClassResolver)或者new_builtin_class_resolver设置来限制内置函数new对类的访问。此处官方提供了三个预定义的解析器:
UNRESTRICTED_RESOLVER:简单地调用ClassUtil.forName(String)。
SAFER_RESOLVER:和第一个类似,但禁止解析ObjectConstructor,Execute和freemarker.template.utility.JythonRuntime。
ALLOWS_NOTHING_RESOLVER:禁止解析任何类。
当然用户自身也可以自定义解析器以拓展对危险类的限制,只需要实现TemplateClassResolver接口就好了。
l api内建函数并不能随意使用,必须在配置项api_builtin_enabled为true时才有效,而该配置在2.3.22版本之后默认为false。
l 同时为了防御通过其他方式调用恶意方法,FreeMarker内置了一份危险方法名单unsafeMethods.properties[3],诸如getClassLoader、newInstance等危险方法都被禁用了。
l 在Freemarker 2.3.30 中引入了一个基于MemberAccessPolicy 的新沙箱。默认策略改进了黑名单并禁止通过反射访问ClassLoader方法和公共字段。
五. FreeMarker模板注入防御
1、防止服务器端模板注入的最佳方法是不允许任何用户修改或提交新模板。
2、避免引入服务器端模板注入漏洞的最简单方法之一是,除非绝对必要,否则始终使用“无逻辑”模板引擎,例如Mustache等。
3、仅在已完全删除潜在危险模块和功能的沙盒环境中执行用户的代码。
如需授权、对文章有疑问或需删除稿件,请联系 FreeBuf 客服小蜜蜂(微信:freebee1024)