
Ambiente con kernel vulnerabile per lo sfruttamento del driver TEE (CVE-2021-44733)
Recentemente è stata scoperta una vulnerabilità use-after-free nel sottosistema TEE del kernel Linux, fino alla versione 5.15.11 inclusa, alla quale è stato assegnato l'ID CVE-2021-44733 [1].
A prima vista non sembrava sfruttabile per diverse ragioni; tuttavia, dopo un'ulteriore analisi del percorso di codice vulnerabile e implementando un proof-of-concept grezzo, è stato possibile sovrascrivere un puntatore a funzione nel kernel. In questo post non viene presentato alcun payload per l'escalation dei privilegi, ma l'intero ambiente per eseguire OPTEE e l'exploit è disponibile per ulteriori test, si veda 'Configurazione dell'ambiente'.
Un TEE (Trusted Execution Environment) è un sistema operativo fidato che gira in un ambiente sicuro, ad esempio TrustZone sulle CPU ARM. Un driver TEE gestisce i dettagli necessari per comunicare con il TEE. Alcuni dei compiti più importanti del driver sono fornire un'API generica verso il TEE basata sulla specifica Globalplatform TEE Client API [3], ma anche gestire la memoria condivisa tra Linux e il TEE. Questo sottosistema può essere abilitato configurando CONFIG_OPTEE nelle configurazioni del kernel per le architetture ARM.
Il mondo sicuro contiene il sistema operativo fidato denominato OP-TEE OS [4]. Sopra questo OS è possibile avere le cosiddette Trusted Applications (TA) in esecuzione, che possono svolgere alcune operazioni nell'ambiente isolato, si veda la Figura 1.
Figura 1: Panoramica del TEE - dalla presentazione di Linaro [5]
Il mondo normale (userspace/kernel Linux) può interagire con queste applicazioni usando client application (CA) e l'API esposta dal sottosistema TEE. Una CA può aprire una sessione verso una specifica TA e invocare funzioni implementate dalla TA. Il passaggio di qualsiasi argomento tra TA e CA avviene tramite memoria condivisa. L'interazione tra una CA e una TA utilizzando tutte le syscall rilevanti è descritta di seguito.
Una CA apre /dev/tee[0-9] per comunicare con il driver. Si noti che, per l'uso convenzionale di queste API, ciò avviene implicitamente tramite libteec.
La memoria condivisa può essere registrata dalla CA usando l'IOCTL TEE_IOC_SHM_ALLOC. Questo alloca memoria condivisa e restituisce un descrittore di file che lo spazio utente può usare come parte di mmap.
Il passo successivo è stabilire una sessione usando l'IOCTL TEE_IOC_OPEN_SESSION e specificando lo uuid per una specifica TA. Questo uuid è hardcoded durante la compilazione della TA.
Per invocare una qualsiasi funzione specifica nella TA, la CA la invoca specificando l'identificatore di una funzione insieme agli eventuali argomenti di input; questo viene fatto usando TEE_IOC_INVOKE.
Quando la CA ha terminato tutte le richieste, la sessione può essere chiusa usando TEE_IOC_CLOSE_SESSION.
Figura 2: Sessione tra CA e TA - dalla presentazione di Linaro [5]
Gran parte della comunicazione tra i client e il TEE è opaca per il driver. Il compito principale del driver è gestire il contesto, ricevere le richieste dai client, inoltrarle al TEE e rispedire i risultati [2].