
PoC
CVE-2022-37706

Hallo Leute, diesmal werde ich über eine aktuelle 0-Day-Lücke sprechen, die ich in einem der wichtigsten Fenstermanager von Linux namens Enlightenment (https://www.enlightenment.org/) gefunden habe.
Diese 0-Day-Lücke bringt jeden Benutzer sehr einfach und sofort zu Root-Rechten.
Der Exploit wurde auf Ubuntu 22.04 getestet, sollte aber auf jeder Distribution einwandfrei funktionieren.
Zunächst einmal ist Enlightenment ein Fenstermanager, Compositor und ein 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. Er war für mich interessant,
da er eine Menge 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, und ich überlegte, ob ich eine davon nutzen kann,
um zu Root-Rechten zu eskalieren. Die Binärdateien sahen alle sicher aus und waren gut codiert.
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 Vorbewertung 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 geprüft.
Und da dieser Fenstermanager Open Source ist, ist der Quellcode für all diese Binärdateien und Module verfügbar.
Als erstes habe ich also apt source enlightenment ausgeführt, um den gesamten Quellcode zu erhalten,
und mit ein wenig Graben können wir zum Code der Ziel-Binärdatei gelangen.
Um die Binärdatei zu debuggen, habe ich sie in Ghidra geladen, um sie zu analysieren und Adressen
für Breakpoints und so weiter zu haben.
Beim ersten Versuch wurden keine Symbole gefunden, aber die brauchen wir auch nicht, da es sich
als relativ kleine Binärdatei herausstellte.
Überraschenderweise fand ich es viel angenehmer, den dekompilierten Pseudocode von
Ghidra anzusehen als direkt den Quellcode (man vermeidet Makros und auch diese Prüfungen
gegen das Betriebssystem, mit dem ein bestimmter Codeblock kompiliert wird).
Also lasst uns mit der Analyse beginnen.
1- Mit der Binärdatei spielen.
Führen wir die Datei aus, um einige Informationen über unser Ziel zu sehen:
Screenshot
Das Ausführen der Binärdatei erzeugt keine Ausgabe:
Screenshot
Die Übergabe des Arguments --help ergab diese Ausgabe:
Screenshot
Tut mir leid, ich werde es nutzen, um Root zu bekommen.
Als Nächstes lassen wir einfach strace laufen und prüfen, ob verdächtige Syscalls wie
execve oder openat verwendet werden:
strace ./enlightenment_sys 2>&1 | grep open
Screenshot
Es öffnet nur bekannte Bibliotheken an Orten, an denen wir keine Änderungen vornehmen dürfen.
strace ./enlightenment_sys 2>&1 | grep exec
Screenshot
2- Lasst uns die Binärdatei reverse-engineeren und sie 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 entry ausmachen.
Das erste Argument der entry-Funktion ist main selbst.
Ich habe sie für spätere Referenzen in main umbenannt.
Wenn ich ein wenig nach unten scrolle, sehe ich bereits, dass die Funktion system() verwendet wird.
Als Pwner verbringe ich Tage mit Herausforderungen, um genau diese Funktion zu spawnen x)
Ich habe die Binärdatei reverse-engineert und nach einem Memory-Corruption-Bug oder Heap-Problemen gesucht,
aber tatsächlich war es eine seltsame Command Injection.
Die Binärdatei ergreift alle Sicherheitsvorkehrungen, bevor sie system ausführt, aber leider
können wir unsere Eingabe dort immer einschleusen.
Screenshot
Ok, jetzt lasst uns die Binärdatei von oben bis zu unserer system-Funktion durchgehen und versuchen,
unsere Eingabe dort einzuschleusen.
Zuerst prüft die Binärdatei nur, ob das erste Argument --help oder -h ist, und zeigt diese
Nachricht an, die wir zuvor gesehen haben.
Screenshot
Zweitens erhöht sie ihre Privilegien auf Root.
Screenshot
Als Nächstes entfernt sie fast alle Umgebungsvariablen (Sicherheitsvorkehrungen), um
keine andere unbeabsichtigte Binärdatei aufzurufen.
Screenshot
Wenn das erste eingegebene Argument also "mount" ist, wird dieser Zweig betreten und einige
übergebene Flags geprüft; diese Flags werden auf dem Stack abgelegt.
Als Nächstes prüft sie, ob der nächste Parameter nach mount UUID= ist. Hier wollen wir nicht hinein,
daher haben wir "/dev/../tmp/;/tmp/exploit" angegeben.
Screenshot
Auf diese Weise passieren wir die Prüfung in Zeile 410, die strncmp-Prüfung.
Denn wenn er nicht mit /dev/ beginnt, beendet sich die Binärdatei.
Als Nächstes erfolgt ein Aufruf von stat64 auf die von uns angegebene Datei. Beachte, dass wir einen Ordner
namens ";" erstellen können, was die Command Injection auslösen wird.
Bis jetzt hat der Exploit diese Datei /dev/../tmp/;/tmp/exploit bereits erstellt,
aber das ist nicht der Exploit, der aufgerufen wird.
Screenshot
Screenshot
Wir kommen system() jetzt 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/// die Länge 6 hat.
Nun prüft das letzte stat64 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.
Nun prüft sie die Verfügbarkeit einiger Dateien, aber das ist an diesem Punkt nicht wichtig,
denn wir sind bereit und kurz davor, beliebige Command Execution auszulösen.
Nun initialisiert eina_strbuf_new() nur den Befehl, 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 der Befehl wird zu:
/bin/mount -o noexec,nosuid,utf8,nodev,iocharset=utf8,utf8=0,utf8=1,uid=$(id -u), /dev/../tmp/;/tmp/exploit /tmp///net
Beachte, dass die doppelten Anführungszeichen entfernt werden und wir /tmp/exploit
als Root aufrufen können.
Screenshot
Die Binärdatei hat ihr Bestes getan, um unbeabsichtigtes Verhalten zu verhindern, aber wie üblich
kann alles gehackt werden. Ich habe nicht erwartet, dies mithilfe eines logischen Fehlers
wie diesem auszunutzen.
Ich möchte, dass die nächste CVE eine Speicherkorruption ist, die zu LPE bis root führt.