
Proof-of-concept-Exploit für CVE-2022-22706: nutzt einen Fehler beim Schreiben in den Page-Cache des Mali-GPU-Kernel-Treibers aus, um /etc/passwd im Speicher zu verändern und eine Root-Shell zu erhalten.
Der Arm-Mali-GPU-Treiber übergibt dem Userspace eine CPU-beschreibbare Abbildung von Seiten, die er schreibgeschützt gepinnt hat, sodass ein unprivilegierter Prozess einen beschreibbaren Alias des Page-Cache erhält, der eine Datei unterstützt, die er nur mit O_RDONLY öffnen kann.
exploit.c leert das Passwortfeld von root im Page-Cache der /etc/passwd und führt su root aus. Die Datei auf der Festplatte wird nie verändert.
PoC für den Treiberbaum und das QEMU-Ziel in diesem Repository (
mali_kbaser35p0-01eac0,CONFIG_MALI_NO_MALI=y, x86_64 GKI 5.15). Führen Sie es in der VM aus.
Die Beschreibbarkeit des CPU-Mappings stammt von KBASE_REG_CPU_WR, aber der Pin fragt Schreibzugriff basierend auf KBASE_REG_GPU_WR allein an:
/* mali_kbase_mem.c */
pinned_pages = pin_user_pages_remote(
mm, address, alloc->imported.user_buf.nr_pages,
reg->flags & KBASE_REG_GPU_WR ? FOLL_WRITE : 0, pages, NULL, NULL);
Importiere mit gesetztem CPU_WR und gelöschtem GPU_WR und du erhältst beide Hälften auf einmal: eine beschreibbare CPU-Abbildung des Imports und einen get_user_pages-Pin, der ohne FOLL_WRITE genommen wurde. Ohne FOLL_WRITE bricht get_user_pages bei einem schreibgeschützten Datei-Mapping nie COW, es gibt die Page-Cache-Seite selbst zurück. Der Treiber bildet dann genau diese Seiten wieder als beschreibbar auf den Userspace ab.
Behoben durch 5381ff7
(„GPUCORE-32592 Fix userbuf imports to respect RO memory“), das den Schreibzugriff von KBASE_REG_CPU_WR | KBASE_REG_GPU_WR ableitet, statt von GPU_WR allein.
sequenceDiagram
participant U as unprivileged process
participant K as mali_kbase
participant PC as page cache
U->>U: mmap /etc/passwd O_RDONLY, PROT_READ
U->>K: MEM_IMPORT(anon page, CPU_RD|CPU_WR|GPU_RD)
Note over K: address recorded, nothing pinned yet
U->>U: munmap(anon) + mremap file mapping onto that VA
U->>K: mmap(import cookie) → writable CPU mapping
U->>K: JOB_SUBMIT(EXTERNAL_RESOURCES)
K->>PC: pin_user_pages_remote() without FOLL_WRITE
U->>PC: memcpy() through the writable mapping
U->>U: execl("/bin/su", "su", "root")MEM_IMPORT zeichnet nur eine Adresse auf; der Pin erfolgt später bei JOB_SUBMIT. Diese Lücke ermöglicht es, die anonyme Seite in der Zwischenzeit gegen das Datei-Mapping auszutauschen.
Die Bearbeitung erhält die Länge, sodass sich nichts nach roots Zeile verschiebt:
root:x:0:0:root:/root:/bin/sh ← before
root::0:0:rootx:/root:/bin/sh ← after (empty password)
busybox su gibt CHECKPASS_PW_HAS_EMPTY_PASSWORD zurück, bevor es zur Eingabe auffordert, wenn das Passwortfeld leer ist, und liest /etc/shadow nur, wenn es genau x ist.
gcc -static -o exploit exploit.c
Kopieren Sie es in die VM und führen Sie es als unprivilegierter Benutzer aus:
$ ./exploit

Aufgenommen per SSH in das QEMU-Ziel als user (UID 1000). ./exploit patcht roots Zeile im Page-Cache der /etc/passwd und führt su root aus, das direkt ohne Nachfrage in eine Root-Shell wechselt.
Verlässt man diese Shell und führt erneut su aus, erhält man weiterhin root: Die Seite bleibt im Cache, sodass jedes spätere open()/read() von /etc/passwd die gepatchten Bytes sieht.
Nachdem echo 1 > /proc/sys/vm/drop_caches die Seite verdrängt und die Datei vom Speicher erneut gelesen wird, fragt su wieder nach einem Passwort. Die Bytes auf der Festplatte wurden nie berührt.