
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].
CVE-2021-44733 wurde durch Fuzzing mit syzkaller entdeckt. Die dafür verwendete Beschreibungsdatei ist unten angegeben. Beachten Sie, dass ioctl$TEE_SHM_REGISTER_FD nur Teil des Linaro-(Maintainer)-Kernel-Baums ist und nicht im Upstream enthalten. Die in 'Setting up the environment' bereitgestellte Umgebung könnte für Fuzzing verwendet werden, wenn sie gemäß der syzkaller-Dokumentation [6] richtig konfiguriert ist.```
#include <uapi/linux/tee.h>
resource fd_tee0[fd] resource session_resource[int32]
openat$tee0(fd const[AT_FDCWD], dev ptr[in, string["/dev/tee0"]], flags flags[open_flags], mode flags[open_mode]) fd_tee0 ioctl$TEE_OPEN_SESSION(fd fd_tee0, cmd const[0x8010a402], arg ptr[inout, tee_ioctl_buf_data_session]) ioctl$TEE_INVOKE(fd fd_tee0, cmd const[0x8010a403], arg ptr[inout, tee_ioctl_buf_data_invoke]) ioctl$TEE_CANCEL(fd fd_tee0, cmd const[0x8008a404], arg ptr[in, tee_ioctl_buf_data_cancel]) ioctl$TEE_CLOSE_SESSION(fd fd_tee0, cmd const[0x8004a405], arg ptr[in, tee_ioctl_buf_data_close]) ioctl$TEE_VERSION(fd fd_tee0, cmd const[0x800ca400], arg ptr[out, tee_ioctl_buf_data_version]) ioctl$TEE_SHM_ALLOC(fd fd_tee0, cmd const[0xc010a401], arg ptr[inout, tee_ioctl_buf_data_shm_alloc]) ioctl$TEE_SHM_REGISTER(fd fd_tee0, cmd const[0xc018a409], arg ptr[inout, tee_ioctl_buf_data_shm_register]) ioctl$TEE_SHM_REGISTER_FD(fd fd_tee0, cmd const[0xc018a408], arg ptr[inout, tee_ioctl_buf_data_shm_register_fd]) ioctl$TEE_SUPPL_RECV(fd fd_tee0, cmd const[0x8010a406], arg ptr[inout, tee_ioctl_buf_suppl_recv]) ioctl$TEE_SUPPL_SEND(fd fd_tee0, cmd const[0x8010a407], arg ptr[inout, tee_ioctl_buf_suppl_send])
#=======================================================
define TEE_IOCTL_UUID_LEN 16
tee_ioctl_param_struct { attr flags[TEE_IOCTL_PARAM_ATTR_TYPE, int64] a int64 b int64 c int64 }
TEE_IOCTL_PARAM_ATTR_TYPE = 0, 1, 2, 3, 5, 6, 7 TEE_LOGIN = 0, 1, 2, 4, 5, 6
#=======================================================
tee_ioctl_buf_data_session { buf_ptr ptr64[inout, tee_ioctl_open_session_struct] buf_len len[buf_ptr, int64] }
tee_ioctl_open_session_struct { uuid array[int8, TEE_IOCTL_UUID_LEN] (in) clnt_uuid array[int8, TEE_IOCTL_UUID_LEN] (in) clnt_login flags[TEE_LOGIN, int32] (in) cancel_id int32 (in) session session_resource (out) ret int32 (out) ret_origin int32 (out) num_params len[params, int32] (in) params array[tee_ioctl_param_struct] (in) }
#=======================================================
tee_ioctl_buf_data_invoke { buf_ptr ptr64[inout, tee_ioctl_invoke_struct] buf_len len[buf_ptr, int64] }
tee_ioctl_invoke_struct { func int32 (in) session session_resource (in) cancel_id int32 (in) ret int32 (out) ret_origin int32 (out) num_params len[params, int32] (in) params array[tee_ioctl_param_struct] (in) }
#=======================================================
tee_ioctl_buf_data_cancel { cancel_id int32 (in) session session_resource (in) }
#=======================================================
tee_ioctl_buf_data_close { session session_resource (in) }
#=======================================================
tee_ioctl_buf_data_version { impl_id int32 (out) impl_caps int32 (out) gen_caps int32 (out) }
#=======================================================
tee_ioctl_buf_data_shm_alloc { size int64 (inout) flags const[0, int32] (inout) id int32 (out) }
#=======================================================
tee_ioctl_buf_data_shm_register { addr int64 (in) length int64 (inout) flags const[0, int32] (inout) id int32 (out) }
#=======================================================
tee_ioctl_buf_data_shm_register_fd { fd int64 (in) size int64 (out) flags const[0, int32] (in) id int32 (out) } [align[8]]
#=======================================================
tee_ioctl_buf_suppl_recv { func int32 (in) num_params len[params, int32] (inout) params array[tee_ioctl_param_struct] (inout) }
#=======================================================
tee_ioctl_buf_suppl_send { ret int32 (out) num_params len[params, int32] (in) params array[tee_ioctl_param_struct] (in) }
Während des Fuzzings hing der Absturz, der die Aufmerksamkeit erregte, mit einem Use-After-Free eines task_struct-Objekts zusammen, während ein Mutex gehalten wurde:```
==================================================================
BUG: KASAN: use-after-free in __mutex_lock.constprop.0+0x118c/0x11c4
Read of size 4 at addr 863b0714 by task optee_example_r/244
CPU: 0 PID: 244 Comm: optee_example_r Tainted: G D 5.14.0 #151
Hardware name: Generic DT based system
[<8012b204>] (unwind_backtrace) from [<8011f460>] (show_stack+0x20/0x24)
[<8011f460>] (show_stack) from [<81cf0108>] (dump_stack_lvl+0x5c/0x68)
[<81cf0108>] (dump_stack_lvl) from [<80650f04>] (print_address_description.constprop.0+0x38/0x304)
[<80650f04>] (print_address_description.constprop.0) from [<80651548>] (kasan_report+0x1c0/0x1dc)
[<80651548>] (kasan_report) from [<81d0a9b4>] (__mutex_lock.constprop.0+0x118c/0x11c4)
[<81d0a9b4>] (__mutex_lock.constprop.0) from [<81d0ada4>] (mutex_lock+0x128/0x13c)
[<81d0ada4>] (mutex_lock) from [<817424b0>] (tee_shm_release+0x4b0/0x6cc)
[<817424b0>] (tee_shm_release) from [<81303674>] (dma_buf_release+0x1b8/0x2f0)
[<81303674>] (dma_buf_release) from [<806d5ac0>] (__dentry_kill+0x4c4/0x678)
[<806d5ac0>] (__dentry_kill) from [<806d8a68>] (dput+0x630/0xba4)
[<806d8a68>] (dput) from [<8067d890>] (__fput+0x3b4/0x900)
[<8067d890>] (__fput) from [<801dd1d8>] (task_work_run+0x15c/0x230)
[<801dd1d8>] (task_work_run) from [<80172b70>] (do_exit+0x103c/0x3770)
[<80172b70>] (do_exit) from [<80179aec>] (do_group_exit+0x134/0x3ac)
[<80179aec>] (do_group_exit) from [<801a7658>] (get_signal+0x7d8/0x2f28)
[<801a7658>] (get_signal) from [<8011dea4>] (do_work_pending+0x984/0x154c)
[<8011dea4>] (do_work_pending) from [<801000d0>] (slow_work_pending+0xc/0x20)
Exception stack(0x85743fb0 to 0x85743ff8)
3fa0: 00023108 00000080 00000000 00000000
3fc0: 66bca2d0 66bca2d0 66bca2d0 000000f0 66bca2d0 66bca340 00000000 6ec00b0c
3fe0: 66bc9cc8 66bc9cb8 00011655 66c80c20 000e0130 00023108
Allocated by task 242:
set_alloc_info+0x48/0x50
__kasan_slab_alloc+0x48/0x58
kmem_cache_alloc+0x14c/0x314
copy_process+0x2014/0x7b18
kernel_clone+0x244/0xfc8
sys_clone+0xc8/0xec
ret_fast_syscall+0x0/0x58
0x6ec00a10
Freed by task 67:
kasan_set_track+0x28/0x30
kasan_set_free_info+0x20/0x34
__kasan_slab_free+0xdc/0x108
kmem_cache_free+0x80/0x394
__put_task_struct+0x2b4/0x35c
delayed_put_task_struct+0x104/0x384
rcu_core+0x91c/0x2a68
__do_softirq+0x2fc/0xfb8
Last potentially related work creation:
kasan_record_aux_stack+0xb8/0xc0
call_rcu+0x9c/0xfd0
put_task_struct_rcu_user+0x9c/0xbc
finish_task_switch+0x534/0xa10
__schedule+0x934/0x1adc
schedule_idle+0x9c/0x120
do_idle+0x2ec/0x434
cpu_startup_entry+0x18/0x1c
start_kernel+0x3ec/0x430
The buggy address belongs to the object at 863b0700
which belongs to the cache task_struct of size 1664
The buggy address is located 20 bytes inside of
1664-byte region [863b0700, 863b0d80)
The buggy address belongs to the page:
page:f09c9565 refcount:1 mapcount:0 mapping:00000000 index:0x0 pfn:0x463b0
head:f09c9565 order:3 compound_mapcount:0 compound_pincount:0
flags: 0x10200(slab|head|zone=0)
raw: 00010200 00000000 00000122 82802e00 00000000 80120012 ffffffff 00000001
page dumped because: kasan: bad access detected
Memory state around the buggy address:
863b0600: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
863b0680: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
>863b0700: fa fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
^
863b0780: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
863b0800: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
==================================================================
Dies wurde ausgelöst durch das Schließen aller Dateideskriptoren von TEE_IOC_SHM_ALLOC, während ein anderer Thread eine Sitzung zu – in unserem Fall – einer nicht existierenden TA öffnet. Syzkaller konnte es reproduzieren, und durch Experimentieren mit dem Reproduktionscode und einer leichten Verzögerung des Aufrufs von TEE_IOC_OPEN_SESSION trat ein weiterer UAF für ein Objekt auf, das zum kmalloc-64-Cache gehörte:```
================================================================== BUG: KASAN: use-after-free in tee_shm_put+0x8c/0x98 Read of size 4 at addr 86467020 by task optee_example_h/216
CPU: 0 PID: 216 Comm: optee_example_h Not tainted 5.14.0 #21 Hardware name: Generic DT based system [<80122584>] (unwind_backtrace) from [<80117fd4>] (show_stack+0x10/0x14) [<80117fd4>] (show_stack) from [<819d57a0>] (dump_stack_lvl+0x40/0x4c) [<819d57a0>] (dump_stack_lvl) from [<819ced74>] (print_address_description.constprop.0+0x5c/0x2d8) [<819ced74>] (print_address_description.constprop.0) from [<805a12c4>] (kasan_report+0x1b4/0x1d0) [<805a12c4>] (kasan_report) from [<814cc6b0>] (tee_shm_put+0x8c/0x98) [<814cc6b0>] (tee_shm_put) from [<814c9b2c>] (tee_ioctl+0x1578/0x2e44) [<814c9b2c>] (tee_ioctl) from [<806038ec>] (sys_ioctl+0x918/0x1e70) [<806038ec>] (sys_ioctl) from [<80100060>] (ret_fast_syscall+0x0/0x58) Exception stack(0x86417fa8 to 0x86417ff0) 7fa0: 00000080 00000000 00000003 8010a402 200001c0 00000003 7fc0: 00000080 00000000 00423018 00000036 66c562d0 66c55e10 66c562d0 6ebebafc 7fe0: 66c55cb0 66c55ca0 004114bd 66cebd72
Allocated by task 216: tee_shm_alloc+0x15c/0x7e8 tee_ioctl+0x8d0/0x2e44 sys_ioctl+0x918/0x1e70 ret_fast_syscall+0x0/0x58 0x66c55ca0
Freed by task 215: kasan_set_free_info+0x20/0x34 __kasan_slab_free+0xdc/0x108 kfree+0x98/0x294 tee_shm_release+0x1dc/0x610 dma_buf_release+0x180/0x2a0 __dentry_kill+0x488/0x6ac __fput+0x2f0/0x7b4 task_work_run+0x178/0x230 do_work_pending+0xaf8/0x10a8 slow_work_pending+0xc/0x20 0x66d5bd16
The buggy address belongs to the object at 86467000 which belongs to the cache kmalloc-64 of size 64 The buggy address is located 32 bytes inside of 64-byte region [86467000, 86467040) The buggy address belongs to the page: page:(ptrval) refcount:1 mapcount:0 mapping:00000000 index:0x0 pfn:0x46467 flags: 0x200(slab|zone=0) raw: 00000200 00000000 00000122 82401200 00000000 00200020 ffffffff 00000001 page dumped because: kasan: bad access detected
Memory state around the buggy address: 86466f00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 86466f80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
86467000: fa fb fb fb fb fb fb fb fc fc fc fc fc fc fc fc ^ 86467080: fa fb fb fb fb fb fb fb fc fc fc fc fc fc fc fc 86467100: fa fb fb fb fb fb fb fb fc fc fc fc fc fc fc fc ==================================================================
Diese Schwachstelle wurde durch Fuzzing des TEE-Treibers entdeckt, ohne dass eine Sitzung mit einem auf dem System laufenden TA eingerichtet wurde. Dies könnte weiter mit sogenannten Pseudo-Syscalls in Syzkaller erweitert werden, um eine Sitzung zu einem TA einzurichten und zu initiieren.
## Ursachenanalyse
Die Schlussfolgerung ist ein Designproblem bei der Lebenszyklusverfolgung eines `tee_shm:dmabuf`-Objekts. Der Treiber ist so ausgelegt, dass der Userspace nach einem Aufruf von `tee_ioctl_shm_alloc()` den einzigen Referenzzähler behält.
Es wird angenommen, dass, wenn das Objekt noch im IDR-Objekt des Treibers gefunden wird, die Referenz auf den dmabuf noch gültig ist und ihr Referenzzähler erhöht werden kann. Es stellt sich heraus, dass dies nur teilweise zutrifft. Der dmabuf-Speicher gehört immer noch dem dmabuf-Treiber, aber er befindet sich möglicherweise im Prozess der Zerstörung, und dies kann nicht gestoppt werden, indem der Referenzzähler wieder ungleich null wird.
Das Szenario, das das Problem auslöst, ist eine Multi-Thread-Anwendung, bei der ein Thread den dmabuf-Dateideskriptor schließt, während ein anderer Thread den IOCTL-Befehl `TEE_IOC_OPEN_SESSION` oder `TEE_IOC_INVOKE` aufruft, der auf diesen gemeinsamen Speicher verweist.
Die Verfolgung der Zerstörung des dmabuf, wenn der Userspace den fd schließt, führt im Kernel zu folgendem Code:
1. `fput()`
2. `fput_many()` >> Dateireferenzzähler erreicht null. Rennfenster öffnet sich.
3. `[task_work wird geplant]`
4. `__fput`
5. `dput`
6. `dma_buf_release`
7. `tee_shm_release`
8. `mutex_lock(teedev->mutex)`
9. `idr_remove(teedev->idr, shm->id)` >> Ab jetzt kann auf das shm-Objekt nicht mehr vom Userspace aus zugegriffen werden. Rennfenster schließt sich.
10. `mutex_unlock()`
Dies bedeutet, dass die IDR-Tabelle und ihr Mutex-Lock nicht garantieren können, dass der dmabuf und das entsprechende `tee_shm` noch aktiv sind. Ein Prozess, der `fput()` überholt, indem er `tee_shm_get_from_id()` aufruft, kann eine Referenz auf einen shm erhalten, der kurz vor dem Absterben steht.```
/**
* tee_shm_get_from_id() - Find shared memory object and increase reference
* count
* @ctx: Context owning the shared memory
* @id: Id of shared memory object
* @returns a pointer to 'struct tee_shm' on success or an ERR_PTR on failure
*/
struct tee_shm *tee_shm_get_from_id(struct tee_context *ctx, int id)
{
struct tee_device *teedev;
struct tee_shm *shm;
if (!ctx)
return ERR_PTR(-EINVAL);
teedev = ctx->teedev;
mutex_lock(&teedev->mutex);
shm = idr_find(&teedev->idr, id);
if (!shm || shm->ctx != ctx)
shm = ERR_PTR(-EINVAL);
else if (shm->flags & TEE_SHM_DMA_BUF)
get_dma_buf(shm->dmabuf);
mutex_unlock(&teedev->mutex);
return shm;
}
Um dies auszunutzen, muss eine Neuzuweisung erfolgen, nachdem das Objekt freigegeben wurde und bevor der UAF ausgelöst wird. Nach dem Aufruf von tee_shm_get_from_id() wird die Funktion tee_shm_put() (bei der der zweite UAF-Crash von syzkaller auftritt) aufgerufen, die das tee_shm:dmabuf-Objekt dereferenziert, das als Eingabeargument für dma_buf_put() verwendet wird.```
/**
Das `tee_shm`-Objekt könnte vor dem UAF neu zugewiesen werden, da es zum kmalloc-64-Cache gehört. Es müsste neu zugewiesen werden mit:
1. fake `tee_shm`-, `tee_shm:dmabuf`-, `dma_buf:file`-Objekten
2. setze `file->f_count = 1`
3. erstelle ein `file:file_operations`-Objekt, das den `fasync`-Funktionszeiger auf eine beliebige Adresse gesetzt hat
Diese Funktion wird dann in `__fput()` nach dem Aufruf von `dma_buf_put()` aufgerufen, wenn `file->f_count` Null erreicht.
PAN (Privileged Access Never) verhindert dies, da fake-Objekte im Userspace-Speicher referenziert werden müssen, um einen beliebigen Funktionszeiger in der `file:f_ops`-Struktur zu setzen. Daher muss `CONFIG_CPU_SW_DOMAIN_PAN` deaktiviert sein, damit dies funktioniert, was in der bereitgestellten Umgebung der Fall ist. Es gibt einige offene Fragen, ob PAN bei dieser Schwachstelle umgangen werden kann, z. B. mittels ret2dir.
Um eine erfolgreiche Neuzuordnung des freigegebenen shm-Objekts durchzuführen, muss der IOCTL-Aufruf `TEE_IOC_OPEN_SESSION` oder `TEE_IOC_INVOKE` durch einen Thread unterbrochen werden, der den Dateideskriptor schließt, sowie durch einen Heap-Spraying-Thread, der den kmalloc-64-Cache füllt. Damit dies funktioniert, muss der Kernel mit `CONFIG_PREEMPT` konfiguriert sein. In diesem PoC wurde der Heap-Spray aus Nicolas Fabrettis Blogbeitrag [7] verwendet, der auf blockierendem `sendmsg()` basiert.
Zusammenfassend besteht das Problem bei der Ausnutzung darin, dass sowohl der Free als auch der UAF innerhalb desselben Systemaufrufs auftreten müssen. Zusätzlich ist das Freigeben schwer auszulösen, da es einen Wettlauf innerhalb des Syscalls erfordert. Nach dem Freigeben bleibt ein kleines Zeitfenster, in dem ein Heap-Spray durchgeführt werden muss, um das freigegebene Objekt neu zuzuweisen. Die folgende Abbildung zeigt die am Exploit-Code beteiligten Threads und ihre Rolle.
<p align="center">
<img src="https://raw.githubusercontent.com/pjlantz/pjlantz.github.io/master/docs/assets/Threads.png?raw=true" alt="Beteiligte Threads" width="50%" height="50%"/>
<br /><em>Abbildung 3: Am Exploit-Code beteiligte Threads</em>
</p>
Drei Arten von Threads laufen kontinuierlich. Um den systemaufrufenden Thread zu unterbrechen, läuft er mit der niedrigstmöglichen Priorität, `SCHED_IDLE`, während die anderen auf Priorität `SCHED_OTHER` gesetzt sind. Da wir blockierendes `sendmsg()` verwenden, muss jeder Spray-Versuch in einem eigenen Thread und auf demselben CPU-Kern ausgeführt werden, der den UAF auslöst, da jeder Kern seine eigenen kmalloc-Caches verwaltet. Es gibt auch eine Reihe von Freigabe-Threads, die den Dateideskriptor von der Shared-Memory-Allokation in Schritt 1b) schließen. Der vollständige Quellcode für diesen UAF-Trigger und die Funktionszeigerüberschreibung ist unter [10] zu finden.
## Einrichten der neuen Umgebung
Um die Umgebung mit einem anfälligen Kernel und OPTEE zu reproduzieren, kann sie aus dem folgenden Repository geklont und mit folgendem Befehl erstellt werden:```
$ mkdir optee-qemu && cd optee-qemu
$ repo init -u https://github.com/pjlantz/optee-qemu.git
$ repo sync
$ cd build
$ make toolchains -j2
$ make run
Nach erfolgreichem Build werden drei Konsolen geöffnet, eine für QEMU – drücken Sie 'c' in der QEMU-Konsole, um zu booten. Eine zweite Konsole zeigt Ausgaben aus der sicheren Welt und die letzte bootet in Linux. Melden Sie sich als root an (kein Passwort).
Führen Sie den Exploit-Code aus, bis der Funktionszeiger fasync der file_operations-Struktur auf 0x22000000 gesetzt ist.```
until optee_exploit | grep "0x22000000" /var/log/messages; do sleep 0.01; done
Dies wird aufgrund von Privileged execute-never (PXN) gestoppt, das die Ausführung bei `PC=0x22000000` blockiert. Ab hier können die Ausnutzungsstrategien je nach Kernel-Version variieren, aber es könnte möglich sein, einen Kernel-ROP auszuführen und Stack Pivoting zu betreiben oder den vDSO-Bereich beschreibbar zu machen und die Nutzlast dort zu platzieren. Es könnte auch für zukünftige Arbeiten interessant sein zu untersuchen, ob PAN mit ret2dir und etwas Physmap-Spraying umgangen werden kann. PAN kann im Kernel aktiviert werden, indem `CONFIG_CPU_SW_DOMAIN_PAN=y` in `linux/.config` gesetzt wird. Auf echter Hardware ist es standardmäßig auf ARMv8.1 und AArch64 aktiviert, für ARMv7 und AArch32 ist es möglich, software-emuliertes PAN mit dieser Einstellung zu aktivieren [8].
**Hinweis**: Dieser Exploit ist nicht sehr gut optimiert und kann gelegentlich den Treiber aufhängen, wenn es gelingt, das gemeinsam genutzte Speicherobjekt zu früh freizugeben. In diesem Fall steht der PC bei `tee_shm_get_from_id()`. Wenn dies passiert, geben Sie `system_reset` in der QEMU-Konsole ein, um die Umgebung neu zu starten.
## Danksagungen
Dank an Lars Persson von Axis Communications für die Hilfe bei der Ursachenanalyse und an Jens Wiklander von Linaro und Betreuer des TEE-Subsystems für eine reibungslose Kommunikation und schnelle Behebung dieses Problems [9].
## Referenzen
[1] CVE-2021-44733 - https://nvd.nist.gov/vuln/detail/CVE-2021-44733
[2] TEE-Subsystem - https://www.kernel.org/doc/html/latest/staging/tee.html
[3] Globalplatform TEE-API - https://globalplatform.org/specs-library/?filter-committee=tee
[4] OP-TEE OS - https://github.com/OP-TEE/optee_os
[5] BKK16-110: Eine sanfte Einführung in Trusted Execution und OP-TEE - https://connect.linaro.org/resources/bkk16/bkk16-110/
[6] Syzkaller - https://github.com/google/syzkaller
[7] Lexfos Sicherheitsblog, von Nicolas Fabretti: CVE-2017-11176: Eine schrittweise Linux-Kernel-Ausnutzung - https://blog.lexfo.fr/cve-2017-11176-linux-kernel-exploitation-part3.html
[8] Linux Kernel Security Subsystem: Exploit-Methoden/Datenverwendung im Userspace - http://kernsec.org/wiki/index.php/Exploit_Methods/Userspace_data_usage
[9] [PATCH v2] tee: handle lookup of shm with reference count 0 - https://lore.kernel.org/lkml/[email protected]/T/
[10] Proof-of-Concept-Exploit - https://github.com/pjlantz/optee_examples/tree/master/exploit/host