
Ein zuverlässiger Exploit + Ausarbeitung zur Rechteerweiterung auf Root. (Getestet auf Ubuntu 22.04)
CVE-2022-37706

Hallo Leute, diesmal werde ich über einen kürzlich gefundenen 0-Day in einem der
Haupt-Fenstermanager von Linux namens Enlightenment (https://www.enlightenment.org/) sprechen.
Dieser 0-Day bringt jeden Benutzer sehr einfach und sofort zu Root-Rechten.
Der Exploit wurde auf Ubuntu 22.04 getestet, sollte aber auf jeder Distribution funktionieren.
Zunächst ist Enlightenment ein Window Manager, Compositor und Minimal Desktop
für Linux (die primäre Plattform), BSD und jedes andere kompatible UNIX-System.
Ich habe diesen Fenstermanager installiert, um ein wenig damit zu experimentieren. Es war interessant
für mich, da es viele Werkzeuge enthält und ehrlich gesagt ziemlich ordentlich aussieht.
Nachdem ich das Paket mit apt install enlightenment installiert hatte, untersuchte ich die
installierten Dateien und Verzeichnisse auf meinem System, viele Module und viele Hilfs-
Binärdateien, aber das Interessanteste ist:
➜ enlightenment cd /usr/lib/x86_64-linux-gnu/enlightenment/
➜ enlightenment find . -perm -4000
./utils/enlightenment_ckpasswd
./utils/enlightenment_system
./utils/enlightenment_sys
Es installiert einige SUID-Binärdateien, dann dachte ich, ob ich eine davon verwenden kann,
um zu Root zu eskalieren, die Binärdateien sahen alle sicher aus und waren gut programmiert.
Die Binärdatei, über die wir sprechen werden, ist enlightenment_sys.
Wie bei jedem anderen Ziel wählen wir eine Strategie, die wir nach einer Vorab-Bewertung anwenden,
siehe meinen Blog hier, falls noch nicht geschehen (https://pwn-maher.blogspot.com/2020/10/vulnerability-assessment.html)
Ich habe den Code mit einem Top-Down-Ansatz auditiert.
Und da dieser Fenstermanager Open Source ist, wird der Quellcode für alle diese Binärdateien und Module verfügbar sein.
Als erstes habe ich also apt source enlightenment ausgeführt, um den gesamten Quellcode zu erhalten,
und mit etwas Graben können wir zum Ziel-Binärcode gelangen.
Aber um die Binärdatei zu debuggen, lade ich sie in Ghidra zur Analyse und um Adressen
zu haben, um Breakpoints zu setzen und so weiter.
Beim ersten Versuch wurden keine Symbole gefunden, aber ja, die waren auch nicht nötig, da es sich
um eine relativ kleine Binärdatei handelte.
Überraschenderweise fand ich es sehr angenehm, den dekompilierten Pseudocode von Ghidra zu betrachten,
als direkt in den Quellcode zu schauen (Makros vermeiden, auch diese Überprüfungen vermeiden,
die gegen das Betriebssystem durchgeführt werden, um einen bestimmten Codeblock zu kompilieren).
Also lasst uns mit der Analyse beginnen.
1- Mit der Binärdatei spielen.
Lassen Sie uns die Datei ausführen, um einige Informationen über unser Ziel zu sehen:

Das Ausführen der Binärdatei gibt keine Ausgabe:

Die Übergabe des Arguments --help ergab diese Ausgabe:

Entschuldigung, ich werde es verwenden, um Root zu bekommen.
Als Nächstes machen wir einfach ein strace und sehen, ob es verdächtige Syscalls wie
execve oder openat verwendet:
strace ./enlightenment_sys 2>&1 | grep open

Es öffnet nur bekannte Bibliotheken an Orten, an denen wir keine Berechtigung haben, herumzupfuschen.
strace ./enlightenment_sys 2>&1 | grep exec

2- Lassen Sie uns die Binärdatei reverse-engineeren und dann ausnutzen.
Ich habe ein neues Ghidra-Projekt erstellt und diese spezifische Binärdatei geladen.
Da keine Symbole gefunden wurden, können wir die main-Funktion über den Einstiegspunkt erkennen.
Das erste Argument der Einstiegsfunktion ist main selbst.
Ich habe es für spätere Referenzen in main umbenannt.
Wenn ich ein wenig nach unten scrolle, kann ich bereits die Verwendung der system()-Funktion erkennen.
Als Pwner verbringe ich Tage mit Herausforderungen, um diese spezifische Funktion zu spawnen x)
Ich habe die Binärdatei reverse-engineert und nach einem Memory-Corruption-Bug oder Heap-Problemen gesucht,
aber eigentlich war es eine seltsame Command Injection.
Die Binärdatei ergreift alle Sicherheitsvorkehrungen, bevor sie system() ausführt, aber leider können
wir unsere Eingabe immer dort injizieren.

Ok, jetzt gehen wir die Binärdatei von oben bis zu unserer system-Funktion durch und versuchen,
unsere Eingabe dort zu injizieren.
Zuerst prüft die Binärdatei, ob das erste Argument --help oder -h ist, und zeigt diese
Nachricht an, die wir zuvor gesehen haben.

Zweitens erhöht es seine Berechtigungen auf Root.

Als Nächstes löscht es fast alle Umgebungsvariablen (Sicherheitsvorkehrungen), um
keine andere nicht beabsichtigte Binärdatei aufzurufen.

Wenn das erste eingegebene Argument also "mount" ist, wird es in diesen Zweig einsteigen, einige
übergebene Flags prüfen, diese Flags werden auf dem Stack gesetzt.
Als Nächstes prüft es, ob der nächste Parameter nach mount UUID= ist. Wir wollen hier nicht
hinein, also haben wir "/dev/../tmp/;/tmp/exploit" übergeben.

Auf diese Weise bestehen wir die Prüfung in Zeile 410, die strncmp-Prüfung.
Denn wenn es nicht mit /dev/ beginnt, wird die Binärdatei beendet.
Als Nächstes erfolgt ein Aufruf von stat64 auf die von uns bereitgestellte Datei. Beachten Sie, dass wir
einen Ordner namens ";" erstellen können, was die Command Injection verursacht.
Bis jetzt hat der Exploit bereits diese Datei /dev/../tmp/;/tmp/exploit erstellt,
aber das ist nicht der Exploit, der aufgerufen wird.


Wir kommen jetzt system() näher.
Nun wird p (Zeiger) auf das letzte Argument aktualisiert, das unserer SUID-Binärdatei übergeben wurde,
/tmp///net.
Warum /tmp///net angeben, wenn wir /tmp/net übergeben können?
Wir werden diese Prüfung umgehen:
if (((next_next == (char *)0x0) || (next_next[1] == '\0')) || ((long)next_next - (long)p != 6))
Wir brauchten, dass /tmp/net existiert und /tmp/// eine Länge von 6 hat.
Nun prüft der letzte stat64-Aufruf die Existenz von "/dev/net"
__snprintf_chk(cmd,0x1000,1,0x1000,"/dev%s",next_next);
Und es wird es finden, also bestehen wir diese letzte Prüfung.
Jetzt wird es die Verfügbarkeit einiger Dateien prüfen, aber das ist an dieser Stelle nicht wichtig,
da wir bereit sind und kurz davor stehen, eine beliebige Befehlausführung auszulösen.
Nun wird eina_strbuf_new() lediglich den Befehl initialisieren, der an system übergeben wird.
Das Problem hier ist, dass wir ihn wie folgt eingegeben haben:
/bin/mount -o noexec,nosuid,utf8,nodev,iocharset=utf8,utf8=0,utf8=1,uid=$(id -u), "/dev/../tmp/;/tmp/exploit" /tmp///net
Aber die Binärdatei ruft eina_strbuf_append_printf() mehrmals auf und wird zu
/bin/mount -o noexec,nosuid,utf8,nodev,iocharset=utf8,utf8=0,utf8=1,uid=$(id -u), /dev/../tmp/;/tmp/exploit /tmp///net
Beachten Sie, dass die doppelten Anführungszeichen entfernt wurden, und wir werden in der Lage sein, /tmp/exploit
als Root aufzurufen.

Die Binärdatei hat ihr Bestes getan, um jedes nicht beabsichtigte Verhalten zu verhindern, aber wie üblich
kann alles gepwned werden. Ich hatte nicht erwartet, dies mit einem logischen Fehler wie diesem auszunutzen.
Ich möchte, dass das nächste CVE ein Memory Corruption ist, der zu LPE Root führt.
Twitter-Veröffentlichung: https://twitter.com/maherazz2/status/1569665311707734023