
Auslösen und Analysieren der Android-Kernel-Sicherheitslücke CVE-2019-2215
Im November 2017 wurde ein Use-After-Free Fehler im Linux-Kernel durch das syzkaller -System entdeckt. Im Februar 2018 wurde dies in einigen Linux-Kernels und Android-Versionen gepatcht.
Dieser Fix wurde nie in die Android Monthly Security Bulletins aufgenommen, sodass er in vielen neu veröffentlichten Geräten wie Pixel und Pixel2 nicht gepatcht wurde.
Im September 2019 wurde Android von Project Zero über die Sicherheitsauswirkungen dieses Fehlers informiert. Daraufhin vergab Android die Bezeichnung CVE-2019-2215 für diese Schwachstelle, um sie formaler und bekannter zu machen.
CVE-2019-2215 ist ein Use-After-Free in binder.c, der die Privilegieneskalation (Erlangen von Root-Zugriff) aus einer Android-Anwendung heraus ermöglicht. Es ist keine Benutzerinteraktion erforderlich, um diese Schwachstelle auszunutzen. Es ist lediglich die Installation einer bösartigen lokalen Anwendung erforderlich.
Hier werden wir diese Android-Kernel-Sicherheitslücke detaillierter vorstellen und diese Schwachstelle nutzen, um Root-Zugriff (Privilegieneskalation) auf dem gesamten Android-Gerät zu erlangen.
Wir werden diesen Proof of Concept (PoC) verwenden:
https://github.com/cloudfuzz/android-kernel-exploitation
Wir zeigen Ihnen zunächst, wie Sie diese Schwachstelle auf einem Android-Emulator auslösen und einen Kernel-Absturz verursachen können. Um zu sehen, wie gefährlich es sein kann, verwenden wir dann den PoC, um Root-Zugriff auf dem simulierten Android-Gerät zu erlangen. Anschließend analysieren wir den Kernel-Code, um die Ursache zu finden (statische und dynamische Analyse).
Nach der Analyse werden wir sehen, wie wir den Root-Zugriff erlangt haben. Schließlich sehen wir, wie diese Schwachstelle durch Patches abgemildert wird.
Um durch Auslösen dieser Schwachstelle einen Kernel-Absturz zu verursachen, gehen wir wie folgt vor:
Sehen Sie sich das folgende Video an:
[video]
In diesem (und im nächsten) Abschnitt werden wir verstehen, warum der Absturz passiert, indem wir statische und dynamische Analyse verwenden.
Hier werden wir den Kernel-Code (statische Analyse) analysieren, um das Problem zu verstehen. In crash_report.txt befindet sich der Bericht von KASan, der besagt, dass es sich um einen Use-After-Free-Fehler handelt. Das bedeutet, dass ein Objekt im Heap allokiert wurde (und wir eine Referenz darauf haben), dann haben wir das Objekt aus dem Heap freigegeben und anschließend fälschlicherweise über eine Referenz darauf zugegriffen. In diesem Bericht wird der Stacktrace dieser drei Phasen ausgegeben.
Wenn Sie sich an oben erinnern, haben wir 'trigger.cpp' im PoC verwendet.
Hier ist der Hauptcode von trigger.cpp:``` int main() { //1 int fd, epfd; //2 struct epoll_event event = {.events = EPOLLIN}; //3 fd = open("/dev/binder", O_RDONLY); //4 epfd = epoll_create(1000); //5 epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &event); //6 ioctl(fd, BINDER_THREAD_EXIT, NULL); }
Sehen wir uns an, was 'trigger.cpp' macht.
In Android (wie auch in anderen Unix-basierten Betriebssystemen) gibt es Prozesse. Jedes Programm, das wir ausführen, erzeugt einen (oder mehrere) Prozesse, und diese Prozesse werden vom Betriebssystem verwaltet. Das Betriebssystem kann zwischen ihnen wechseln (Multitasking) oder einen Prozess beenden usw. Aus Sicherheitsgründen sind Prozesse standardmäßig voneinander isoliert.
In manchen Fällen muss ein Prozess Daten mit einem anderen Prozess austauschen. Dies wird als Interprozesskommunikation (IPC) bezeichnet. Es gibt mehrere Möglichkeiten für Prozesse, unter Linux zu kommunizieren. Android hat einen spezifischen IPC-Mechanismus namens **'Binder'** eingeführt. Binder ist ein Kernel-Treiber zur Erleichterung der Interprozesskommunikation.
In Android kann IPC entweder durch direkten Aufruf einiger Kernel-Methoden (die meisten befinden sich in drivers/binder.c) oder durch die Verwendung von High-Level-Implementierungen (z.B. in Java) erfolgen.

Um **binder** zu verwenden, müssen wir das Kernel-Binder-Modul öffnen. Dies geschieht in Zeile 3 von trigger.cpp. Dann haben wir einen Dateideskriptor-Zeiger. Mit diesem fd können der Kernel-Initiator und die Empfänger der IPC identifiziert werden.
Alle Interaktionen mit dem Treiber erfolgen über eine kleine Menge von **'ioctl'**-Befehlen (BINDER_THREAD_EXIT, BINDER_WRITE_READ, ...).
Mehr über Binder: [link1](https://www.nds.ruhr-uni-bochum.de/media/attachments/files/2012/03/binder.pdf), [link2](http://rts.lab.asu.edu/web_438/project_final/CSE_598_Android_Architecture_Binder.pdf)
Unter Linux gibt es ein Konzept namens '**Ereignis-Polling**'. Die '**epoll**'-API wird verwendet, wenn wir mehrere Dateideskriptoren überwachen möchten (Dateideskriptoren sind das, was wir beim Öffnen eines Treibers oder bei der Arbeit mit E/A haben usw.).
**epoll** ist eine Kernel-Struktur mit zwei wichtigen Feldern.
* interest list = Liste der Dateideskriptoren, die wir überwachen möchten.
* ready list = Liste der Dateideskriptoren, die für E/A bereit sind.
Um Ereignis-Polling zu verwenden, erstellen wir zuerst ein epoll (Zeile 4), dann fügen wir ein Ereignis (&event), das mit einem Dateideskriptor (**fd**) verknüpft ist, zu unserem erstellten epoll (**epfd**) hinzu oder löschen es (EPOLL_CTL_ADD), indem wir die **epoll_ctl**-Methode des Kernels aufrufen.
=> `epoll_event event` ist ein Ereignis, das ausgelöst wird, wenn die zugehörige Datei (fd) für Lesevorgänge verfügbar ist.
Jetzt verstehen wir, was **trigger.cpp** macht (kein Grund, zu tief einzusteigen!). Es öffnet das **binder**-Modul, erstellt ein **epoll**, um darauf zu lauschen, wenn es bereit ist. Dann beenden wir in Zeile 6 den Binder, den wir in Zeile 3 gestartet haben.
#### Zuweisung:
Durch Aufruf von open() rufen wir tatsächlich **open_binder()** auf ( open()-Implementierung in binder.c ), in open_binder() wird eine neue **'binder_proc'**-Struktur erstellt und:
` fd->pricate_data = binder_proc`
Durch Aufruf von **epoll_create()** wird eine neue epoll-Struktur erstellt und einer Warteschlangenstruktur hinzugefügt.
