Skip to content
KitploitKITPLOIT
ToolsBlog
Einreichen
ToolsBlog
Einreichen

Hacking-, PenTest- und Cybersicherheits-Tools für Ihr Sicherheitsarsenal!

Kitploit ist ein Verzeichnis von Hacking-, Cybersicherheits- und Pentesting-Tools. Entdecken Sie die neuesten Projekt-Updates, um Schwachstellen zu finden, Systeme zu analysieren, Tests zu automatisieren und Ihre Sicherheit zu stärken.

··Feeds·Kontakt·Datenschutz·© 2026 Kitploit

Tool-Verzeichnis

Kategorien

Alle Kategorien anzeigen
Loading categories
optee-qemu — Umgebung mit anfälligem Kernel zur Ausnutzung des TEE-Treibers (CVE-2021-44733) | Kitploit
Tools/GitHubGitHub/pjlantz/optee-qemu
Embedded-System-SicherheitSchwachstellenanalyseExploitationFuzzingLernen & BildungBinary-ExploitationLabs & Praxis
GitHubpjlantz/optee-qemu

optee-qemu

Umgebung mit anfälligem Kernel zur Ausnutzung des TEE-Treibers (CVE-2021-44733)

Repository anzeigen
7611vor 4 JahrenVon Kitploit geprüft

Beliebteste

Alle anzeigen →

Entdecken Sie die meistgenutzten Tools unserer Community.

Alle Tools erkunden

Durchsuchen Sie unsere Tool-Sammlung

Alle Tools anzeigen →
Teilen

CVE-2021-44733: Fuzzing und Ausnutzung einer Use-after-Free im Linux-Kernel-TEE-Subsystem

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').

Hintergrund

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).

TEE overview
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.

  1. 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.

  2. 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.

  3. 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.

  4. 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.

  5. Wenn die CA alle Anforderungen abgeschlossen hat, kann die Sitzung mit TEE_IOC_CLOSE_SESSION geschlossen werden.

Session between CA and TA
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].

Fuzzing des TEE-Treibers

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])

COMMON

#=======================================================

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

OPEN SESSION

#=======================================================

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) }

INVOKE

#=======================================================

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) }

CANCEL SESSION

#=======================================================

tee_ioctl_buf_data_cancel { cancel_id int32 (in) session session_resource (in) }

CLOSE SESSION

#=======================================================

tee_ioctl_buf_data_close { session session_resource (in) }

VERSION

#=======================================================

tee_ioctl_buf_data_version { impl_id int32 (out) impl_caps int32 (out) gen_caps int32 (out) }

SHM ALLOC

#=======================================================

tee_ioctl_buf_data_shm_alloc { size int64 (inout) flags const[0, int32] (inout) id int32 (out) }

SHM REGISTER

#=======================================================

tee_ioctl_buf_data_shm_register { addr int64 (in) length int64 (inout) flags const[0, int32] (inout) id int32 (out) }

SHM REGISTER FD

#=======================================================

tee_ioctl_buf_data_shm_register_fd { fd int64 (in) size int64 (out) flags const[0, int32] (in) id int32 (out) } [align[8]]

SUPPLICANT RECV

#=======================================================

tee_ioctl_buf_suppl_recv { func int32 (in) num_params len[params, int32] (inout) params array[tee_ioctl_param_struct] (inout) }

SUPPLICANT SEND

#=======================================================

tee_ioctl_buf_suppl_send { ret int32 (out) num_params len[params, int32] (in) params array[tee_ioctl_param_struct] (in) }

root@kitploit:~
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 ==================================================================

root@kitploit:~
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;
}

Ausnutzen des UAF

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.``` /**

  • tee_shm_put() - Decrease reference count on a shared memory handle
  • @shm: Shared memory handle */ void tee_shm_put(struct tee_shm *shm) { if (shm->flags & TEE_SHM_DMA_BUF) dma_buf_put(shm->dmabuf); } EXPORT_SYMBOL_GPL(tee_shm_put);
root@kitploit:~
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

root@kitploit:~
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
Tool herunterladen