
Exploit proof-of-concept per CVE-2019-0217, una condizione di gara in mod_auth_digest del server HTTP Apache che consente il bypass dell'autenticazione. Include ambiente vulnerabile basato su Docker e script di automazione Burp Turbo Intruder.
Questo repository contiene un exploit proof of concept per CVE-2019-0217, insieme a un Dockerfile per configurare un server web vulnerabile alla CVE.
In Apache HTTP Server 2.4 release 2.4.38 e precedenti, una condizione di gara in mod_auth_digest quando viene eseguito in un server multi-thread potrebbe consentire a un utente con credenziali valide di autenticarsi utilizzando un altro nome utente, bypassando le restrizioni di controllo degli accessi configurate.
Quando un client tenta di accedere a una risorsa protetta da HTTP Digest Auth, il server risponde con un codice di stato 401, insieme a un header WWW-Authenticate, più o meno nel formato seguente:
Digest realm="rlm", nonce="TToc8M0jBgA=7afa4f292c97632a6c17eec458d3db31021b111f", algorithm=MD5, qop="auth"
Il client calcola l'HA1, che è il digest md5 di A1 (cioè username:realm:password), e poi vi aggiunge il nonce e altri metadati come cnonce per ottenere A2, il cui digest md5 a sua volta produce HA2, che il client invia come risposta, insieme a username, cnonce e altri metadati, nell'header Authentication:
'Digest username="attacker", realm="rlm", nonce="TToc8M0jBgA=7afa4f292c97632a6c17eec458d3db31021b111f", uri="/scripts/userprofile.cgi", response="bcb433e4071fa228fe0c9452a7495efd", algorithm="MD5", qop="auth", nc=00000001, cnonce="2309510923095109"
^^^^^^^^^^
Come descritto di seguito, la parte evidenziata dell'header sopra può essere manipolata per creare la condizione di gara necessaria affinché l'exploit funzioni.
La vulnerabilità è stata corretta da questo commit.
Osservando il codice del modulo mod_auth_digest (in particolare la funzione get_hash in mod_auth_digest.c), è evidente che il server analizza per primo l'header Authentication per determinare cosa si trova tra le virgolette dopo username=, e lo tratta come l'utente associato alla richiesta (r->user). Poi verifica se questo "utente" ha accesso alla risorsa richiesta o meno. Se lo ha, la funzione get_hash recupera il corrispondente HA1 dal file di autenticazione.
Prima del commit che corregge la vulnerabilità, l'HA1 così recuperato veniva memorizzato nella variabile conf = (digest_config_rec *) ap_get_module_config(r->per_dir_config,&auth_digest_module), che apparentemente non è thread-safe.
Il che ci dà l'exploit:
Sulla base dell'analisi della vulnerabilità di cui sopra, dovrebbe essere possibile creare una condizione di gara inviando simultaneamente richieste con un header di autenticazione valido insieme a richieste con un header di autenticazione contraffatto (che è identico all'header valido ma con il valore di username= impostato all'utente target (che l'attaccante vuole impersonare)).
Terminologia utilizzata in questo exploit: - Attaccante: l'utente che esegue questo exploit. Ha un nome utente e una password validi per il proprio account. - Vittima: l'utente di cui l'attaccante vuole assumere l'identità. Il nome utente della vittima è noto, ma la password (ovviamente) no.
Sono riuscito a far funzionare questo metodo inviando una raffica di thread Python, alcuni dei quali inviavano richieste del primo tipo e altri del secondo, ma era inaffidabile e richiedeva un numero elevato di richieste per andare a segno.
Questo mi ha portato a usare il turbo intruder di Burp (gratuito), che invia le richieste in parallelo in modo affidabile. Purtroppo, il supporto CLI di turbo intruder non funziona, quindi alcune parti dell'exploit devono essere eseguite tramite GUI.
Per usare il server vulnerabile di esempio che ho incluso in questo repository, esegui:
$ cd sample_vulnerable_server
$ docker build -t poc_httpd .
$ docker run -p 8038:80 poc_httpd
Esegui generate_turbo_intruder_script.py, dopo aver configurato le variabili in cima:
# 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
Questo script genera poi due file, generated/turbo_intruder_script.py e generated/request.txt, nella directory corrente.
Ora apri BurpSuite Community Edition e installa le estensioni "Turbo Intruder" andando su Extensions -> BApp Store, se non l'hai già fatto.
Apri il repeater di Burp. Imposta l'host e la porta del target:

Incolla il contenuto di generated/request.txt nella sezione Request:

Fai clic con il tasto destro in un punto qualsiasi della sezione Request e seleziona 'Extensions->Turbo Intruder->Send to Turbo Intruder'.

Incolla il contenuto di generated/turbo_intruder_script.py nella sezione script:

Infine, fai clic sul pulsante Attack in fondo alla finestra di Turbo Intruder. Dovrebbe mostrare eventuali risultati riusciti nella schermata risultante:
