一、简介
FreeMarker是一款模板引擎,通过Java类库引入,模板文件简称为FTL(后缀可能也为这个)。输出方式为MVC(模型,视图,控制器)模式,适用于Web开发框架生成html页面。所以此类库经常应用于MVC开发模式的Java Web程序。
二、利用与发掘
既然简介为模板引擎,那么就一定有可以动态利用的地方。FreeMarker动态处理变量为${}格式,当然还有标签格式,这个稍后讲解。看到这里,是不是很像el、spel引擎模板的解析风格,不过确实有相似部分,当然也有区别。接下来,通过代码分析利用方式或常见格式:
Configuration configuration = new Configuration(); String templateContent = "${1+1}"; Template tpl = new Template(null, templateContent, configuration); StringWriter writer = new StringWriter(); tpl.process(null, writer); System.out.println(writer);
可以看到成功执行表达式并输出结果,FreeMarker与el、spel的区别这时就体现了,虽然检测漏洞时都可以利用类似表达式,但是执行命令时又有不同,FreeMarker在process执行时必须继承freemarker.template.TemplateModel,如下图报错所示:
那么FreeMarker执行命令的方式有哪些呢,可以直接通过TemplateModel反推继承链,看看哪些类中的方法存在可以利用的方式,从上图报错中可以回溯执行的调用过程
at freemarker.core.Expression.eval(Expression.java:78) at freemarker.core.MethodCall._eval(MethodCall.java:55) at freemarker.core.Expression.eval(Expression.java:78) at freemarker.core.Expression.evalAndCoerceToString(Expression.java:82) at freemarker.core.DollarVariable.accept(DollarVariable.java:41) at freemarker.core.Environment.visit(Environment.java:324) at freemarker.core.Environment.process(Environment.java:302) at freemarker.template.Template.process(Template.java:325) at com.cc.demo.spel.main(spel.java:37)
跟踪到MethodCall中_eval方法,在继承了TemplateModel类时最终会执行到对应类的exec方法中,这里直接贴出可利用的类(既继承TemplateModel类又存在exec方法,方法中存在执行命令)得到:
freemarker.template.utility.Execute freemarker.template.utility.ObjectConstructor freemarker.template.utility.JythonRuntime
当然这是一种执行方式,还有官方
(https://freemarker.apache.org/docs/ref_builtins_expert.html)的标签执行。
当然还有一些变量参考
(https://freemarker.apache.org/docs/ref_specvar.html)。官方文档还是很管用的。
三、限制
既然官方已知含有危险方法并可执行,那么就一定会有限制解决的方案,在2.3.17版本中加入了setNewBuiltinClassResolver方法,此方法可以限制最后的执行类,有多种格式可以选择,例如
configuration = new Configuration(Configuration.DEFAULT_INCOMPATIBLE_IMPROVEMENTS); configuration.setDefaultEncoding(CommonConstants.DEFAULT_CHARSET_NAME); configuration.setTemplateUpdateDelayMilliseconds(0); configuration.setAPIBuiltinEnabled(false); configuration.setNewBuiltinClassResolver(TemplateClassResolver.ALLOWS_NOTHING_RESOLVER); configuration.setLogTemplateExceptions(false);
第一行设置config版本(可以忽略不设置),第二行设置编码格式,第三行设置模板更新缓存延迟,第四行设置API权限,第五行设置可以加载类,第六行设置异常log输出。重点在第四行与第五行,第四行中的api可访问Java Api FreeMarker中object_wrapper(根据环境不同,可能存在利用方式),第五行中
ALLOWS_NOTHING_RESOLVER
为禁止解析任何类,对应还有
UNRESTRICTED_RESOLVE
等于
ClassUtil.forName(className)
和SAFER_RESOLVER禁止解析上述命令执行的三个类。
四、突破
上述讲解了限制,既然有限制,必然就要想到应对之策,从官方的变量参考中,找到了一些可以利用的点,例如get_optional_template特殊变量,官方文档中描述:
This method returns a hash that contains the following entries:
exists: A boolean that tells if the template was found.
include: A directive that, when called, includes the template. Calling this directive is similar to calling the include directive, but of course with this you spare looking up the template again. This directive has no parameters, nor nested content. If exists is false, this key will be missing; see later how can this be utilized with the exp!default operator.
import: A method that, when called, imports the template, and returns the namespace of the imported template. Calling this method is similar to calling the import directive, but of course with this you spare looking up the template again, also, it doesn't assign the namespace to anything, just returns it. The method has no parameters. If exists is false, this key will be missing; see later how can this be utilized with the exp!default operator.
变量可以包含模板文件以及import method,类似PHP中的include方法,这时利用方式就有很多了,可以发散思维到PHP文件包含漏洞等等,利用方式可以参考官方,也可更新一下使用
<#assign optTemp = .get_optional_template('some.ftl')> <#if optTemp.exists> Template was found: <@optTemp.include /> <#else> Template was missing. </#if>