在本节中,咱们将介绍什么是服务端模板注入,并概述利用此漏洞的基本方法,同时也将提供一些避免此漏洞的建议。java
服务端模板注入是指攻击者可以利用模板自身语法将恶意负载注入模板,而后在服务端执行。web
模板引擎被设计成经过结合固定模板和可变数据来生成网页。当用户输入直接拼接到模板中,而不是做为数据传入时,可能会发生服务端模板注入攻击。这使得攻击者可以注入任意模板指令来操纵模板引擎,从而可以彻底控制服务器。顾名思义,服务端模板注入有效负载是在服务端交付和执行的,这可能使它们比典型的客户端模板注入更危险。shell
服务端模板注入漏洞会使网站面临各类攻击,具体取决于所讨论的模板引擎以及应用程序如何使用它。在极少数状况下,这些漏洞不会带来真正的安全风险。然而,大多数状况下,服务端模板注入的影响多是灾难性的。后端
最严重的状况是,攻击者有可能完成远程代码执行,从而彻底控制后端服务器,并利用它对内部基础设施进行其余攻击。安全
即便在不可能彻底执行远程代码的状况下,攻击者一般仍可使用服务端模板注入做为许多其余攻击的基础,从而可能得到服务器上敏感数据和任意文件的访问权限。ruby
当用户输入直接拼接到模板中而不是做为数据传入时,就会出现服务端模板注入漏洞。服务器
简单地提供占位符并在其中呈现动态内容的静态模板一般不会受到服务端模板注入的攻击。典型的例子如提取用户名做为电子邮件的开头,例如如下从 Twig 模板中提取的内容:编辑器
$output = $twig->render("Dear {first_name},", array("first_name" => $user.first_name) );
这不容易受到服务端模板注入的攻击,由于用户的名字只是做为数据传递到模板中的。ide
可是,Web 开发人员有时可能将用户输入直接链接到模板中,如:函数
$output = $twig->render("Dear " . $_GET['name']);
此时,不是将静态值传递到模板中,而是使用 GET name 动态生成模板自己的一部分。因为模板语法是在服务端执行的,这可能容许攻击者使用 name 参数以下:
http://vulnerable-website.com/?name={{bad-stuff-here}}
像这样的漏洞有时是因为不熟悉安全概念的人设计了有缺陷的模板形成的。与上面的例子同样,你可能会看到不一样的组件,其中一些组件包含用户输入,链接并嵌入到模板中。在某些方面,这相似于 SQL 注入漏洞,都是编写了不当的语句。
然而,有时这种行为其实是有意为之。例如,有些网站故意容许某些特权用户(如内容编辑器)经过设计来编辑或提交自定义模板。若是攻击者可以利用特权账户,这显然会带来巨大的安全风险。
识别服务端模板注入漏洞并策划成功的攻击一般涉及如下抽象过程。
服务端模板注入漏洞经常不被注意到,这不是由于它们很复杂,而是由于它们只有在明确寻找它们的审计人员面前才真正明显。若是你可以检测到存在漏洞,则利用它将很是容易。在非沙盒环境中尤为如此。
与任何漏洞同样,利用漏洞的第一步就是先找到它。也许最简单的初始方法就是注入模板表达式中经常使用的一系列特殊字符,例如 ${{<%[%'"}}%\
,去尝试模糊化模板。若是引起异常,则代表服务器可能以某种方式解释了注入的模板语法,从而代表服务端模板注入可能存在漏洞。
服务端模板注入漏洞发生在两个不一样的上下文中,每一个上下文都须要本身的检测方法。无论模糊化尝试的结果如何,也要尝试如下特定于上下文的方法。若是模糊化是不肯定的,那么使用这些方法之一,漏洞可能会暴露出来。即便模糊化确实代表存在模板注入漏洞,你仍然须要肯定其上下文才能利用它。
纯文本上下文。
大多数模板语言容许你经过直接使用 HTML tags 或模板语法自由地输入内容,后端在发送 HTTP 响应以前,会把这些内容渲染为 HTML 。例如,在 Freemarker 模板中,render('Hello ' + username)
可能会渲染为 Hello Carlos
。
这有时常常被误认为是一个简单的 XSS 漏洞并用于 XSS 攻击。可是,经过将数学运算设置为参数的值,咱们能够测试其是否也是服务端模板注入攻击的潜在攻击点。
例如,考虑包含如下模板代码:
render('Hello ' + username)
在审查过程当中,咱们能够经过请求如下 URL 来测试服务端模板注入:
http://vulnerable-website.com/?username=${7*7}
若是结果输出包含 Hello 49
,这代表数学运算被服务端执行了。这是服务端模板注入漏洞的一个很好的证实。
请注意,成功计算数学运算所需的特定语法将因使用的模板引擎而异。咱们将在 Identify 步骤详细说明。
代码上下文。
在其余状况下,漏洞暴露是由于将用户输入放在了模板表达式中,就像上文中的电子邮件示例中看到的那样。这能够采用将用户可控制的变量名放置在参数中的形式,例如:
greeting = getQueryParameter('greeting') engine.render("Hello {{"+greeting+"}}", data)
在网站上生成的 URL 相似于:
http://vulnerable-website.com/?greeting=data.username
渲染的输出可能为 Hello Carlos
。
在评估过程当中很容易忽略这个上下文,由于它不会产生明显的 XSS,而且与简单的 hashmap 查找几乎没有区别。在这种状况下,测试服务端模板注入的一种方法是首先经过向值中注入任意 HTML 来肯定参数不包含直接的 XSS 漏洞:
http://vulnerable-website.com/?greeting=data.username<tag>
在没有 XSS 的状况下,这一般会致使输出中出现空白(只有 Hello,没有 username ),编码标签或错误信息。下一步是尝试使用通用模板语法来跳出该语句,并尝试在其后注入任意 HTML :
http://vulnerable-website.com/?greeting=data.username}}<tag>
若是这再次致使错误或空白输出,则说明你使用了错误的模板语法。或者,模板样式的语法均无效,此时则没法进行服务端模板注入。若是输出与任意 HTML 一块儿正确呈现,则这是服务端模板注入漏洞存在的关键证实:
Hello Carlos<tag>
一旦检测到潜在的模板注入,下一步就是肯定模板引擎。
尽管有大量的模板语言,但许多都使用很是类似的语法,这些语法是专门为避免与 HTML 字符冲突而选择的。所以,构造试探性载荷来测试正在使用哪一个模板引擎可能相对简单。
简单地提交无效的语法就足够了,由于生成的错误消息会告诉你用了哪一个模板引擎,有时甚至能具体到哪一个版本。例如,非法的表达式 <%=foobar%>
触发了基于 Ruby 的 ERB 引擎的以下响应:
(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'}}
在 Twig
中返回 49
,在 Jinja2
中返回 7777777
。所以,不要只由于成功响应了就草率下结论。
在检测到存在潜在漏洞并成功识别模板引擎以后,就能够开始尝试寻找利用它的方法。详细请翻阅下文。
防止服务端模板注入的最佳方法是不容许任何用户修改或提交新模板。然而,因为业务需求,这有时是不可避免的。
避免引入服务端模板注入漏洞的最简单方法之一是,除非绝对必要,始终使用“无逻辑”模板引擎,如 Mustache
。尽量的将逻辑与表示分离,这能够大大减小高危险性的基于模板的攻击的风险。
另外一措施是仅在彻底删除了潜在危险模块和功能的沙盒环境中执行用户的代码。不幸的是,对不可信的代码进行沙盒处理自己就很困难,并且容易被绕过。
最后,对于接受任意代码执行没法避免的状况,另外一种补充方法是,经过在锁定的例如 Docker 容器中部署模板环境,来应用你本身的沙盒。
在本节中,咱们将更仔细地了解一些典型的服务端模板注入漏洞,并演示如何利用以前概括的方法。经过付诸实践,你能够潜在地发现和利用各类不一样的服务端模板注入漏洞。
一旦发现服务端模板注入漏洞,并肯定正在使用的模板引擎,成功利用该漏洞一般涉及如下过程。
阅读
除非你已经对模板引擎了如指掌,不然应该先阅读其文档。虽然这可能有点无聊,可是不要低估文档多是有用的信息来源。
学习基本语法、关键函数和变量处理显然很重要。即便只是简单地学习如何在模板中嵌入本机代码块,有时也会很快致使漏洞利用。例如,一旦你知道正在使用基于 Python 的 Mako 模板引擎,实现远程代码执行能够简单到:
<% import os x=os.popen('id').read() %> ${x}
在非沙盒环境中,实现远程代码执行并将其用于读取、编辑或删除任意文件在许多常见模板引擎中都很是简单。
除了提供如何建立和使用模板的基础知识外,文档还可能提供某种“安全”部分。这个部分的名称会有所不一样,但它一般会归纳出人们应该避免使用模板进行的全部潜在危险的事情。这多是一个很是宝贵的资源,甚至能够做为一种备忘单,为你应该寻找哪些行为,以及如何利用它们提供指南。
即便没有专门的“安全”部分,若是某个特定的内置对象或函数会带来安全风险,文档中几乎老是会出现某种警告。这个警告可能不会提供太多细节,但至少应将其标记为能够深刻挖掘研究的内容。
例如,在 ERB 模板中,文档显示能够列出全部目录,而后按以下方式读取任意文件:
<%= Dir.entries('/') %> <%= File.open('/example/arbitrary-file').read %>
利用服务端模板注入漏洞的另外一个关键方面是善于查找其余在线资源。一旦你可以识别正在使用的模板引擎,你应该浏览 web 以查找其余人可能已经发现的任何漏洞。因为一些主要模板引擎的普遍使用,有时可能会发现有充分记录的漏洞利用,你能够对其进行调整以利用到本身的目标网站。
此时,你可能已经在使用文档时偶然发现了一个可行的漏洞利用。若是没有,下一步就是探索环境并尝试发现你能够访问的全部对象。
许多模板引擎公开某种类型的 self
或 environment
对象,其做用相似于包含模板引擎支持的全部对象、方法和属性的命名空间。若是存在这样的对象,则能够潜在地使用它来生成范围内的对象列表。例如,在基于 Java 的模板语言中,有时可使用如下注入列出环境中的全部变量:
${T(java.lang.System).getenv()}
这能够做为建立一个潜在有趣对象和方法的短名单的基础,以便进一步研究。
须要注意的是,网站将包含由模板提供的内置对象和由 web 开发人员提供的自定义、特定于站点的对象。你应该特别注意这些非标准对象,由于它们特别可能包含敏感信息或可利用的方法。因为这些对象可能在同一网站中的不一样模板之间有所不一样,请注意,你可能须要在每一个不一样模板的上下文中研究对象的行为,而后才能找到利用它的方法。
虽然服务端模板注入可能致使远程代码执行和服务器的彻底接管,但在实践中,这并不是老是能够实现。然而,仅仅排除了远程代码执行,并不必定意味着不存在其余类型的攻击。你仍然能够利用服务端模板注入漏洞进行其余高危害性攻击,例如目录遍历,以访问敏感数据。
到目前为止,咱们主要研究了经过重用已记录的漏洞攻击或使用模板引擎中已知的漏洞来构建攻击。可是,有时你须要构建一个自定义的漏洞利用。例如,你可能会发现模板引擎在沙盒中执行模板,这会使攻击变得困难,甚至不可能。
在识别攻击点以后,若是没有明显的方法来利用漏洞,你应该继续使用传统的审计技术,检查每一个函数的可利用行为。经过有条不紊地完成这一过程,你有时能够构建一个复杂的攻击,甚至可以利用于更安全的目标。
如上文所述,第一步是标识你有权访问的对象和方法。有些对象可能会当即跳出来。经过结合你本身的知识和文档中提供的信息,你应该可以将你想要更完全地挖掘的对象的短名单放在一块儿。
在研究对象的文档时,要特别注意这些对象容许访问哪些方法,以及它们返回哪些对象。经过深刻到文档中,你能够发现能够连接在一块儿的对象和方法的组合。将正确的对象和方法连接在一块儿有时容许你访问最初看起来高不可攀的危险功能和敏感数据。
例如,在基于 Java 的模板引擎 Velocity 中,你能够调用 $class
访问 ClassTool
对象。研究文档代表,你能够链式使用 $class.inspect()
方法和 $class.type
属性引用任意对象。在过去,这被用来在目标系统上执行 shell 命令,以下所示:
$class.inspect("java.lang.Runtime").type.getRuntime().exec("bad-stuff-here")
一些模板引擎默认运行在安全、锁定的环境中,以便尽量地下降相关风险。尽管这使得利用这些模板进行远程代码执行变得很困难,可是开发人员建立的暴露于模板的对象能够提供更进一步的攻击点。
然而,虽然一般为模板内置对象提供了大量的文档,可是网站特定的对象几乎根本就没有文档记录。所以,要想知道如何利用这些漏洞,就须要你手动调查网站的行为,以肯定攻击点,并据此构建你本身的自定义攻击。