
Este repositorio contiene un exploit de prueba de concepto para CVE-2019-0217, junto con un Dockerfile para configurar un servidor web vulnerable a este CVE.
En Apache HTTP Server 2.4, versiones 2.4.38 y anteriores, una condición de carrera en mod_auth_digest cuando se ejecuta en un servidor con hilos podría permitir que un usuario con credenciales válidas se autentique usando otro nombre de usuario, omitiendo las restricciones de control de acceso configuradas.
Cuando un cliente intenta acceder a un recurso protegido por autenticación HTTP Digest, el servidor responde con un código de estado 401, junto con una cabecera WWW-Authenticate, con un formato similar al siguiente:
Digest realm="rlm", nonce="TToc8M0jBgA=7afa4f292c97632a6c17eec458d3db31021b111f", algorithm=MD5, qop="auth"
El cliente calcula el HA1, que es el resumen md5 de A1 (es decir, usuario:realm:contraseña), y luego le añade el nonce y otros metadatos como el cnonce para obtener A2, cuyo resumen md5 a su vez da HA2, que el cliente envía como respuesta, junto con el nombre de usuario, el cnonce y otros metadatos, en la cabecera Authentication:
'Digest username="attacker", realm="rlm", nonce="TToc8M0jBgA=7afa4f292c97632a6c17eec458d3db31021b111f", uri="/scripts/userprofile.cgi", response="bcb433e4071fa228fe0c9452a7495efd", algorithm="MD5", qop="auth", nc=00000001, cnonce="2309510923095109"
^^^^^^^^^^
Como se describe a continuación, la parte resaltada de la cabecera anterior puede manipularse para crear la condición de carrera necesaria para que el exploit funcione.
La vulnerabilidad fue corregida por este commit.
Observando el código del módulo mod_auth_digest (concretamente la función get_hash en mod_auth_digest.c), es evidente que el servidor primero analiza la cabecera Authentication para determinar qué hay dentro de las comillas después de username=, lo cual trata como el usuario asociado a la petición (r->user). A continuación, comprueba si este "usuario" tiene acceso al recurso solicitado o no. Si lo tiene, la función get_hash recupera el HA1 correspondiente del archivo de autenticación.
Antes del commit que corrige la vulnerabilidad, el HA1 así obtenido se almacenaba en la variable conf = (digest_config_rec *) ap_get_module_config(r->per_dir_config,&auth_digest_module), que aparentemente no es segura para hilos.
Lo cual nos da el exploit:
Basado en la sección de análisis de la vulnerabilidad anterior, debería ser posible crear una condición de carrera enviando simultáneamente peticiones con una cabecera Authentication válida junto con peticiones con una cabecera Authentication falsificada (que es la misma que la válida pero con el valor de username= establecido al usuario objetivo (aquel como el que el atacante quiere autenticarse)).
Terminología utilizada en este exploit: - Atacante: el usuario que ejecuta este exploit. Tiene un nombre de usuario y una contraseña válidos para su propia cuenta. - Víctima: el usuario al que el atacante quiere suplantar. El nombre de usuario de la víctima es conocido, pero la contraseña (obviamente) no.
Pude hacer funcionar este método lanzando un montón de hilos de Python, algunos de los cuales enviaban peticiones del primer tipo y otros del segundo, pero no era fiable y requería un gran número de peticiones para que se materializara. Esto me llevó a usar el Turbo Intruder de Burp (gratuito), que envía las peticiones en paralelo de forma fiable. Desafortunadamente, el soporte de CLI de Turbo Intruder está roto, por lo que algunas partes del exploit deben realizarse usando la GUI.
Para usar el servidor vulnerable de ejemplo que he incluido en este repositorio, ejecuta:
$ cd sample_vulnerable_server
$ docker build -t poc_httpd .
$ docker run -p 8038:80 poc_httpd
Ejecuta generate_turbo_intruder_script.py, después de configurar las variables de la parte superior:
# Configuration
URL = 'http://localhost:8038/scripts/userprofile.cgi' # location to the resource protected by digest auth
ATTACKER_USERNAME = 'attacker'
ATTACKER_PASSWORD = 'known'
VICTIM_USERNAME = 'victim'
NUMBER_OF_REQUESTS = 2000 # number of concurrent requests sent to catch a glimpse of the race condition
Este script genera entonces dos archivos, generated/turbo_intruder_script.py y generated/request.txt, en el directorio actual.
Ahora abre BurpSuite Community Edition e instala la extensión "Turbo Intruder" yendo a Extensiones -> BApp Store, si aún no lo has hecho.
Abre el Repeater de Burp. Configura el host y el puerto del objetivo:

Pega el contenido de generated/request.txt en la sección Request:

Haz clic derecho en cualquier parte de la sección Request y selecciona 'Extensions->Turbo Intruder->Send to Turbo Intruder'.

Pega el contenido de generated/turbo_intruder_script.py en la sección de script:

Finalmente, haz clic en el botón Attack en la parte inferior de la ventana de Turbo Intruder. Debería mostrar cualquier resultado exitoso en la pantalla resultante:
