
Analisi tecnica e sviluppo di exploit per CVE-2021-3156, un heap-based buffer overflow in Sudo, inclusi tre exploit funzionanti per l'escalation locale dei privilegi sulle principali distribuzioni Linux.
Qualys Security Advisory
Baron Samedit: buffer overflow basato su heap in Sudo (CVE-2021-3156)
Sommario Analisi Sfruttamento Riconoscimenti Cronologia
Abbiamo scoperto un buffer overflow basato su heap in Sudo (https://www.sudo.ws/). Questa vulnerabilità:
è sfruttabile da qualsiasi utente locale (utenti normali e utenti di sistema, sudoers e non sudoers), senza autenticazione (cioè l'attaccante non ha bisogno di conoscere la password dell'utente);
è stata introdotta nel luglio 2011 (commit 8255ed69) e interessa tutte le versioni legacy dalla 1.8.2 alla 1.8.31p2 e tutte le versioni stabili dalla 1.9.0 alla 1.9.5p1, nella loro configurazione predefinita.
Abbiamo sviluppato tre exploit diversi per questa vulnerabilità e ottenuto privilegi di root completi su Ubuntu 20.04 (Sudo 1.8.31), Debian 10 (Sudo 1.8.27) e Fedora 33 (Sudo 1.9.2). Probabilmente anche altri sistemi operativi e distribuzioni sono sfruttabili.
Se Sudo viene eseguito per lanciare un comando in modalità "shell" (shell -c command):
tramite l'opzione -s, che imposta il flag MODE_SHELL di Sudo;
oppure tramite l'opzione -i, che imposta i flag MODE_SHELL e MODE_LOGIN_SHELL di Sudo;
allora, all'inizio della funzione main() di Sudo, parse_args() riscrive argv (righe 609-617), concatenando tutti gli argomenti della riga di comando (righe 587-595) e facendo l'escaping di tutti i metacaratteri con backslash (righe 590-591):
Successivamente, in sudoers_policy_main(), set_cmnd() concatena gli argomenti della riga di comando in un buffer basato su heap "user_args" (righe 864-871) e rimuove l'escaping dei metacaratteri (righe 866-867), "per scopi di corrispondenza e registrazione sudoers":
Purtroppo, se un argomento della riga di comando termina con un singolo carattere backslash, allora:
alla riga 866, "from[0]" è il carattere backslash e "from[1]" è il terminatore nullo dell'argomento (cioè non un carattere spazio);
alla riga 867, "from" viene incrementato e punta al terminatore nullo;
alla riga 868, il terminatore nullo viene copiato nel buffer "user_args" e "from" viene incrementato di nuovo e punta al primo carattere dopo il terminatore nullo (cioè fuori dai limiti dell'argomento);
il ciclo "while" alle righe 865-869 legge e copia caratteri fuori dai limiti nel buffer "user_args".
In altre parole, set_cmnd() è vulnerabile a un buffer overflow basato su heap, perché i caratteri fuori dai limiti copiati nel buffer "user_args" non erano inclusi nella sua dimensione (calcolata alle righe 852-853).
In teoria, tuttavia, nessun argomento della riga di comando può terminare con un singolo carattere backslash: se MODE_SHELL o MODE_LOGIN_SHELL è impostato (riga 858, condizione necessaria per raggiungere il codice vulnerabile), allora MODE_SHELL è impostato (riga 571) e parse_args() ha già effettuato l'escaping di tutti i metacaratteri, inclusi i backslash (cioè ha fatto l'escaping di ogni singolo backslash con un secondo backslash).
In pratica, però, il codice vulnerabile in set_cmnd() e il codice di escaping in parse_args() sono circondati da condizioni leggermente diverse:
versus:
La nostra domanda, quindi, è: possiamo impostare MODE_SHELL e MODE_EDIT o MODE_CHECK (per raggiungere il codice vulnerabile) ma non il MODE_RUN predefinito (per evitare il codice di escaping)?
La risposta, a quanto pare, è no: se impostiamo MODE_EDIT (opzione -e, riga 361) o MODE_CHECK (opzione -l, righe 423 e 519), allora parse_args() rimuove MODE_SHELL dai "valid_flags" (righe 363 e 424) ed esce con un errore se specifichiamo un flag non valido come MODE_SHELL (righe 532-533):
Ma abbiamo trovato una scappatoia: se eseguiamo Sudo come "sudoedit" invece di "sudo", allora parse_args() imposta automaticamente MODE_EDIT (riga 270) ma non reimposta "valid_flags", e i "valid_flags" includono MODE_SHELL per impostazione predefinita (righe 127 e 249):