I ricercatori della sicurezza di Qualys hanno rivelato i dettagli di due vulnerabilità che interessano il kernel Linux e il gestore di sistema systemd. La vulnerabilità nel kernel (CVE-2021-33909) consente a un utente locale di ottenere l'esecuzione di codice con privilegi di root manipolando directory profonde.
Il pericolo della vulnerabilità è aggravato dal fatto che i ricercatori sono riusciti a preparare exploit funzionanti per Ubuntu 20.04/20.10/21.04, Debian 11 e Fedora 34 con la configurazione di default. Si segnala che altre distribuzioni non sono state testate, ma teoricamente possono anch'esse essere vulnerabili e attaccate. Il codice completo degli exploit verrà pubblicato dopo la risoluzione diffusa del problema, mentre attualmente è disponibile solo un prototipo limitato che causa il crash del sistema. Il problema è emerso nel luglio 2014 e interessa le versioni del kernel a partire dalla 3.16. La correzione della vulnerabilità è stata coordinata con la comunità e inclusa nel kernel il 19 luglio. Le principali distribuzioni hanno già preparato aggiornamenti dei pacchetti con il kernel (Debian, Ubuntu, Fedora, RHEL, SUSE, Arch).
La vulnerabilità è causata dalla mancanza di controllo del risultato della conversione da size_t a int prima di eseguire operazioni nel codice seq_file, che crea file da una sequenza di record. L'assenza di controllo può portare a una scrittura oltre i limiti del buffer durante la creazione, il montaggio e l'eliminazione di strutture di directory con un livello di nidificazione molto elevato (dimensione del percorso superiore a 1 GB). Di conseguenza, un attaccante potrebbe riuscire a scrivere una stringa di 10 byte "//deleted" con un offset "-2 GB - 10 byte", indicando un'area immediatamente precedente al buffer allocato.
L'exploit preparato richiede 5 GB di memoria e 1 milione di inode liberi. Il funzionamento dell'exploit consiste nella creazione, tramite la chiamata mkdir(), di un'altezza di circa un milione di directory annidate per raggiungere una dimensione del percorso file superiore a 1 GB. Questa directory viene montata attraverso un bind-mount in uno spazio dei nomi degli identificatori utente (user namespace) separato, dopodiché viene eseguita la funzione rmdir() per la sua eliminazione. Contemporaneamente, viene creato un thread che carica un piccolo programma eBPF, il quale si blocca nella fase successiva alla verifica del pseudocodice eBPF, ma prima della sua JIT-compilazione.
In uno spazio dei nomi degli identificatori utente non privilegiato, viene aperto il file /proc/self/mountinfo e inizia la lettura del lungo percorso della directory montata tramite bind-mount, il che porta alla scrittura della stringa «//deleted» nell'area prima dell'inizio del buffer. La posizione per la scrittura della stringa viene scelta in modo tale da sovrascrivere un'istruzione già controllata, ma non ancora compilata, nel programma eBPF.
A livello del programma eBPF, la scrittura non controllata al di fuori del buffer viene trasformata in una capacità gestita di lettura e scrittura in altre strutture del kernel attraverso la manipolazione delle strutture btf e map_push_elem. Di conseguenza, l'exploit determina la posizione del buffer modprobe_path[] nella memoria del kernel e lo sovrascrive con il percorso «/sbin/modprobe», il che consente di avviare qualsiasi file eseguibile con diritti di root nel caso venga effettuata una chiamata request_module(), che viene eseguita, ad esempio, durante la creazione di un socket netlink.
I ricercatori forniscono diversi metodi di bypass della protezione, che sono efficaci solo per un exploit specifico, ma non risolvono il problema alla radice. Si raccomanda di impostare il parametro «/proc/sys/kernel/unprivileged_userns_clone» a 0 per vietare il montaggio di directory in un namespace di identificazione utenti separato, e «/proc/sys/kernel/unprivileged_bpf_disabled» a 1 per disabilitare il caricamento dei programmi eBPF nel kernel.
È interessante notare che, analizzando una variante alternativa di attacco che utilizza meccanismi FUSE invece di bind-mount per il montaggio di una grande directory, i ricercatori hanno scoperto un'altra vulnerabilità (CVE-2021-33910) che coinvolge il gestore di sistema systemd. Si è constatato che, tentando di montare tramite FUSE una directory con un percorso che supera gli 8 MB, si verifica l'esaurimento della memoria dello stack nel processo di inizializzazione (PID1), causando un crash che porta il sistema in uno stato di "panic".
Il problema è legato al fatto che systemd monitora e analizza il contenuto di /proc/self/mountinfo, e gestisce ciascun punto di montaggio nella funzione unit_name_path_escape(), in cui viene eseguita l'operazione strdupa(), posizionando i dati nello stack invece che nella memoria allocata dinamicamente. Poiché la dimensione massima dello stack è limitata tramite RLIMIT_STACK, l'elaborazione di un percorso troppo grande per il punto di montaggio provoca il crash del processo PID1 e l'arresto del sistema. Per l'attacco si può utilizzare un semplice modulo FUSE insieme all'uso di una directory con un alto livello di annidamento, il cui percorso supera gli 8 MB.
Il problema si manifesta a partire da systemd 220 (aprile 2015), ed è già stato risolto nel repository principale di systemd e correttamente sistemato nelle distribuzioni (Debian, Ubuntu, Fedora, RHEL, SUSE, Arch). È interessante notare che nell'uscita di systemd 248 l'exploit non funziona a causa di un errore nel codice di systemd, che provoca un crash durante l'elaborazione di /proc/self/mountinfo. Inoltre, nel 2018 si è verificata una situazione simile: nel tentativo di scrivere un exploit per la vulnerabilità CVE-2018-14634 nel kernel Linux, i ricercatori di Qualys hanno scoperto tre vulnerabilità critiche in systemd.
Fonte: opennet.ru
