
Umgebung mit anfälligem Kernel zur Ausnutzung des TEE-Treibers (CVE-2021-44733)
Kürzlich wurde eine Use-after-Free-Schwachstelle im Linux-Kernel-TEE-Subsystem bis einschließlich Version 5.15.11 entdeckt und als CVE-2021-44733 [1] eingestuft.
Auf den ersten Blick schien sie aus mehreren Gründen nicht ausnutzbar zu sein, jedoch war es nach weiterer Analyse des anfälligen Codepfads und durch die Implementierung eines rudimentären Proof-of-Concept-Exploits möglich, einen Funktionszeiger im Kernel zu überschreiben. In diesem Beitrag wird kein Privilege-Escalation-Payload vorgestellt, die gesamte Umgebung zum Ausführen von OPTEE und des Exploits steht jedoch für weitere Tests zur Verfügung (siehe 'Setting up the environment').
Eine TEE (Trusted Execution Environment) ist ein vertrauenswürdiges Betriebssystem, das in einer sicheren Umgebung läuft, zum Beispiel TrustZone auf ARM-CPUs. Ein TEE-Treiber übernimmt die Details, die für die Kommunikation mit der TEE erforderlich sind. Zu den wichtigeren Aufgaben des Treibers gehört es, eine generische API für die TEE basierend auf der Globalplatform TEE Client API Specification [3] bereitzustellen, aber auch den gemeinsamen Speicher zwischen Linux und der TEE zu verwalten. Dieses Subsystem kann durch Konfigurieren von CONFIG_OPTEE in den Kernel-Konfigurationen für ARM-Architekturen aktiviert werden.
Die sichere Welt enthält das vertrauenswürdige Betriebssystem OP-TEE OS [4]. Auf diesem Betriebssystem können sogenannte Trusted Applications (TAs) ausgeführt werden, die einige Operationen in der isolierten Umgebung durchführen können (siehe Abbildung 1).
Abbildung 1: Überblick über TEE – aus Linares Präsentation [5]
Die normale Welt (Linux-Benutzerbereich/Kernel) kann mit diesen Anwendungen über Client-Anwendungen (CAs) und die vom TEE-Subsystem bereitgestellte API interagieren. Eine CA kann eine Sitzung zu einer bestimmten TA öffnen und Funktionen aufrufen, die die TA implementiert. Die Übergabe von Argumenten zwischen der TA und der CA erfolgt über gemeinsamen Speicher. Die Interaktion zwischen einer CA und einer TA unter Verwendung aller relevanten Syscalls wird als nächstes beschrieben.
Eine CA öffnet /dev/tee[0-9], um mit dem Treiber zu kommunizieren. Beachten Sie, dass dies bei der herkömmlichen Verwendung dieser APIs implizit über libteec geschieht.
Der gemeinsame Speicher kann von der CA mit dem IOCTL TEE_IOC_SHM_ALLOC registriert werden. Dies weist gemeinsamen Speicher zu und gibt einen Dateideskriptor zurück, den der Benutzerbereich als Teil von mmap verwenden kann.
Der nächste Schritt ist die Einrichtung einer Sitzung mit dem IOCTL TEE_IOC_OPEN_SESSION und der Angabe der UUID für eine bestimmte TA. Diese UUID wird während der Kompilierung der TA fest codiert.
Um eine bestimmte Funktion in der TA aufzurufen, ruft die CA diese auf, indem sie den Bezeichner einer Funktion zusammen mit etwaigen Eingabeargumenten angibt. Dies geschieht mit TEE_IOC_INVOKE.
Wenn die CA alle Anforderungen abgeschlossen hat, kann die Sitzung mit TEE_IOC_CLOSE_SESSION geschlossen werden.
Abbildung 2: Sitzung zwischen CA und TA – aus Linares Präsentation [5]
Ein Großteil der Kommunikation zwischen Clients und der TEE ist für den Treiber undurchsichtig. Die Hauptaufgabe des Treibers besteht darin, den Kontext zu verwalten, Anfragen von Clients zu empfangen, sie an die TEE weiterzuleiten und die Ergebnisse zurückzusenden [2].