服务端模板注入
翻译
原文:https://portswigger.net/web-security/server-side-template-injection
- name: 翻译
desc: 原文:https://portswigger.net/web-security/server-side-template-injection
bgColor: '#F0DFB1'
textColor: 'green'
2
3
4
# 0服务端模板注入
这项技术最早由 PortSwigger Research 在会议演示文稿 “服务端模板注入:现代 Web 应用程序的 RCE (opens new window)” 中记录。
在本节中,我们将讨论什么是服务端模板注入,并概述利用服务端模板注入漏洞的基本方法。我们还将提出一些建议方法,以确保你自己使用模板时,不会使你暴露在服务端模板注入的攻击之下。
实验室
如果你已经熟悉服务端模板注入漏洞背后的基本概念,并且只想在一些实际的、易受攻击的目标上练习和利用它们,那么你可以从下面的链接访问本主题中的所有实验室。
PortSwigger 在 2015 年关于该主题的研究论文中,首次记录了这种技术。如果你对我们 如何在实时网站上利用这些漏洞 感兴趣,可以在我们的研究页面上查看完整的文章。
Research
# 1什么是服务端模板注入?
服务端模板注入是指 攻击者能够使用本地模板语法,将恶意负载注入到模板中,然后在服务器端执行。
模板引擎通过将固定模板 与 易变数据相结合来生成网页。当用户输入直接拼接到模板中,而不是作为数据传入时,可能会发生服务端模板注入攻击。这允许攻击者注入任意模板指令,以操纵模板引擎,通常使他们能够完全控制服务器。顾名思义,服务端模板注入有效负载,是在服务器端传递和解析的,这可能使它们比典型的客户端模板注入更危险。
# 2服务端模板注入有什么影响?
服务端模板注入漏洞 可能会使网站遭受各种攻击,具体取决于所涉及的模板引擎 以及 应用程序使用它的确切方式。在某些罕见情况下,这些漏洞不会造成真正的安全风险。然而,大多数时候,服务端模板注入的影响可能是灾难性的。
在最严重的情况下,攻击者可能会实现远程代码执行,完全控制后端服务器,并通过它来 对内部基础设施进行其他攻击。
即使在 无法完全实现 远程代码执行的情况下,攻击者仍然可以使用服务端模板注入 作为许多其他攻击的基础,从而获得对服务器上敏感数据 和 任意文件的读取/访问权限。
# 3服务端模板注入漏洞是如何产生的?
当用户输入被拼接到模板中,而不是作为数据传入时,就会出现服务端模板注入漏洞。
静态模板只提供 用于呈现动态内容 的占位符,通常不容易受到服务端模板注入的攻击。典型的例子是一封电子邮件,它通过用户的名称来问候每个用户,例如下面的 Twig 模板示例:
$output = $twig->render("Dear {first_name},", array("first_name" => $user.first_name) );
这不容易受到服务端模板注入的影响,因为用户的名称只是作为数据传递到模板中。
但是,由于模板只是字符串,因此 Web 开发人员有时会在渲染之前,直接将用户输入拼接到模板中。让我们举一个与上面类似的例子,但这一次,用户可以在发送电子邮件之前,自定义电子邮件的某些部分。例如,他们可以选择使用的名称:
$output = $twig->render("Dear " . $_GET['name']);
在此示例中,不再是将静态值传递到模板中,而是使用GET
参数name
来动态生成模板自身的一部分。由于模板语法是在服务端解析的,因此攻击者可能会在name
参数中放置服务端模板注入有效负载,如下所示:
http:/?name={{这里有坏东西}}
像这样的漏洞,有时是因为不熟悉安全隐患的人 设计了错误的模板而造成的。与上面的示例一样,你可能会看到不同的组件,其中一些组件包含用户输入,这些组件被拼接并嵌入到模板中。在某些方面,这有点类似于编写不当的预处理语句中发生的 SQL 注入 (opens new window)漏洞。
但有时,这种行为实际上是故意实现的。例如,某些网站故意允许某些特权用户(如内容创作者)编辑或提交自定义模板。如果攻击者能够危及具有此类权限的帐户,这显然会带来巨大的安全风险。
# 4构建服务端模板注入攻击
识别服务端模板注入漏洞 并 成功构建攻击,通常涉及以下高级过程。

# 4.1检测
服务端模板注入漏洞经常被忽视,不是因为它们很复杂,而是因为它们只对那些 明确寻找它们的审计人员 才真正显而易见。如果你能够检测到一个漏洞的存在,那么利用它就会非常容易,尤其是在非沙箱环境中。
与任何漏洞一样,利用漏洞的第一步是能够找到它。也许最简单的起步方法是,尝试注入模板表达式中 常用的特殊字符序列 来对模板进行模糊测试。例如:
${{<%[%'"}}%\
如果引发异常,则表明 服务器可能正在以某种方式 解析注入的模板语法。这是可能存在 服务端模板注入漏洞 的一个迹象。
服务端模板注入漏洞 发生在两种不同的上下文中,每种上下文都拥有自己的一套检测方法。无论你的模糊测试结果如何,尝试下列特定于上下文的方法也很重要。即使模糊测试没有得到结果,漏洞仍然可能以其中一种方法暴露出来。即使模糊测试表明,确实存在模板注入漏洞,你仍然需要识别其上下文才能利用它。
# 4.1.1文本上下文
大多数模板语言,允许你直接使用 HTML 标记或使用模板的本地语法自由输入内容,这些语法将在发送 HTTP 响应之前,于后端呈现为 HTML。例如,在 Freemarker 中,一行标记render('Hello ' + username)
将呈现为类似Hello Carlos
的内容。
这有时可能会被用于 XSS (opens new window),这经常被误认为是一个简单的 XSS 漏洞。但是,通过将数学运算设置为参数的值,我们可以测试 这是否也是服务端模板注入攻击 的潜在入口点。
例如,假设一个模板包含以下易受攻击的代码:
render('Hello ' + username)
在审计期间,我们可能会通过请求 URL 来测试服务端模板注入,例如:
http:/?username=${7*7}
如果生成的输出中包含Hello 49
,则表明服务器端正在解析数学运算。这是服务端模板注入漏洞的一个良好概念证明。
请注意,成功解析数学运算所需的特定语法,将根据所使用的模板引擎而异。我们将在 “识别 (opens new window)” 步骤中更详细地讨论这一点。
# 4.1.2代码上下文
在其他情况下,该漏洞是由 放置在模板表达式中的用户输入 暴露的,正如我们之前在电子邮件示例中看到的那样。这可以表现为将 用户可控的变量名 放置在参数中的形式,例如:
greeting = getQueryParameter('greeting')
engine.render("Hello {{"+greeting+"}}", data)
2
在网站上,生成的 URL 将类似于:
http:/?greeting=data.username
例如,这将在输出中呈现为Hello Carlos
。
在审计过程中很容易遗漏这个上下文,因为它不会产生明显的 XSS,并且几乎无法与简单的 hashmap 查找区分开来。在此上下文中,测试服务端模板注入的一种方法是,首先向值中注入任意 HTML 来确定参数不包含直接的 XSS 漏洞:
http:/?greeting=data.username<tag>
在没有 XSS 的情况下,这通常会导致输出中出现空白条目(只有Hello
,没有用户名)、编码标记或错误消息。下一步是 尝试使用通用模板语法打破语句,并尝试在其后注入任意 HTML:
http:/?greeting=data.username}}<tag>
如果这再次导致错误或空白输出,则表示你使用了错误的模板语法。或者,如果模板语法是无效的,则说明无法进行服务端模板注入。但如果,输出与任意 HTML 一起正确呈现,则这是存在服务端模板注入漏洞的关键指示:
Hello Carlos<tag>
# 4.2识别
一旦检测到模板注入的可能性,下一步就是识别模板引擎。
尽管有大量的模板语言,但其中许多都具有非常相似的语法,这些语法是专门选择的,以免与 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>'
2
3
4
否则,你将需要针对特定的语言,手动测试不同的有效负载,并研究模板引擎如何解析它们。使用基于语法有效 或 无效的排除过程,你可以比想象中更快地缩小选项范围。执行此操作的常见方法是,使用来自不同模板引擎的语法,注入任意数学运算。然后,你可以观察它们是否被成功解析。为了帮助完成此过程,你可以使用类似于以下内容的决策树:

你应该知道这个,同一有效负载偶尔可以在 多种模板语言 中返回成功的响应。例如:
{{7*'7'}}
该有效负载在 Twig 中返回49
,但在 Jinja2 中返回7777777
。因此重要的是,不要根据单一的成功响应就妄下结论。
# 4.3利用
当检测到潜在漏洞,并成功识别模板引擎之后,你可以开始尝试寻找利用该漏洞的方法。
# 5如何防范服务端模板注入漏洞
防止服务端模板注入的最佳方法是,不允许任何用户修改或提交新模板。但是,由于业务需求,这有时是不可避免的。
避免引入 服务端模板注入漏洞 的最简单方法之一是,始终使用 “无逻辑” 的模板引擎,例如 Mustache。除非绝对必要,尽可能地将逻辑与表示层分离,这可以极大地减少你遭受 基于模板 的攻击风险。
(沙箱)另一种措施是,只在沙箱环境中执行用户的代码,在沙箱环境中,潜在的危险模块和函数已被完全删除。但不幸的是,对不受信任的代码进行沙箱化 本质上是困难的,并且容易被绕过。
(容器 + 沙箱)最后一种补充方法是,在封闭的 Docker 容器中部署模板环境,并在其中应用自己的沙箱,然后接受不可避免的任意代码执行。