
Proof-of-Concept-LPE-Exploit für Android Binder UAF, der iovec-Spraying und addr_limit-Overwrite nutzt, um beliebigen Kernel-Lese-/Schreibzugriff zu erreichen.
Ein neu geschriebener Proof-of-Concept-/Local-Privilege-Escalation-Exploit (LPE), der auf CVE-2019-2215 abzielt, eine Use-After-Free-Schwachstelle im Android-Binder-Treiber.
Ich habe den verwundbaren Kernel-Build im Ordner vulnerable_kernel_builds bereitgestellt. Erstelle einen Android-10-Emulator (AOSP) und starte den Kernel damit.
emulator -show-kernel -no-window -no-snapshot -wipe-data -avd research -kernel bzImage
Die technischen Details des Bugs kannst du in diesem großartigen Blog erfahren: https://projectzero.google/2019/11/bad-binder-android-in-wild-exploit.html
Die Struktur task_struct hat ein wichtiges Mitglied addr_limit vom Typ mm_segment_t. addr_limit speichert die höchste gültige Benutzerraum-Adresse. addr_limit ist Teil von struct thread_info oder struct thread_struct, abhängig von der Zielarchitektur. Da wir es hier mit einem x86_64-Bit-System zu tun haben, ist addr_limit in struct thread_struct definiert.

Wenn wir dieses addr_limit mit 0xFFFFFFFFFFFFFFFF überschreiben können, sind wir in der Lage, auf beliebige Teile des Kernel-Speichers lesend und schreibend zuzugreifen. Für eine bessere Kompatibilität des Exploits auf x86_64 und arm64 ist es besser, addr_limit auf 0xFFFFFFFFFFFFFFFE zu setzen.
struct iovec wird für Vectored I/O verwendet, auch bekannt als Scatter/Gather I/O. Eines der Hauptprobleme von struct iovec ist, dass diese nur kurzlebig sind. Sie werden von Systemaufrufen allokiert, wenn diese mit den Puffern arbeiten, und unmittelbar freigegeben, wenn sie in den Benutzermodus zurückkehren.
Wir möchten, dass die iovec-Struktur im Kernel verbleibt, wenn wir die Unlink-Operation auslösen und den iov_base-Zeiger mit der Adresse von binder_thread->wait.head überschreiben, um einen eingeschränkten Lese- und Schreibzugriff zu erhalten. Eine Möglichkeit besteht darin, Systemaufrufe wie readv, writev auf einem pipe-Dateideskriptor zu verwenden, da diese blockieren können, wenn die pipe voll oder leer ist. pipe ist ein unidirektionaler Datenkanal, der für die Interprozesskommunikation genutzt werden kann. Die blockierende Eigenschaft von pipe verschafft uns ein erhebliches Zeitfenster, um die iovec-Struktur im Kernelspace zu korrumpieren.
Auf ähnliche Weise können wir den Systemaufruf recvmsg zum Blockieren verwenden, indem wir MSG_WAITALL als Flag-Parameter übergeben.
Da die Größe der binder_thread-Struktur 408 Bytes beträgt, landet sie im kmalloc-512-Cache.

Wir müssen 25 iovec-Strukturen stapeln, um den verwaisten Chunk neu zu allokieren. 408 / 16 = 25.5

Wie wir im obigen Bild sehen können, werden iovecStack[10].iov_len und iovecStack[11].iov_base überschrieben.

Daher möchten wir iovecStack[10] verarbeiten, den Systemaufruf writev blockieren und dann die unlink-Operation auslösen. Dadurch wird sichergestellt, dass wir den Systemaufruf writev fortsetzen, wenn iovecStack[11].iov_base überschrieben wird. Schließlich leaken wir den Inhalt des binder_thread-Chunks zurück in den Benutzerraum und lesen daraus den task_struct-Zeiger.

Um einen eingeschränkten write zu erreichen, verwenden wir den Systemaufruf recvmsg zum Blockieren, indem wir MSG_WAITALL als Flag-Parameter übergeben. Der Systemaufruf recvmsg kann genau wie der Systemaufruf writev blockieren.

Da die Größe von mm_segment_t 0x8 Bytes beträgt, möchten wir es mit 0xFFFFFFFFFFFFFFFE überschreiben, da dies die höchste gültige Kernelspace-Adresse ist und den Prozess nicht zum Absturz bringt, wenn auf einem arm64-System ein Seitenfehler (Page Fault) auftritt.

