
Un exploit affidabile + write-up per elevare i privilegi a root. (Testato su Ubuntu 22.04)
CVE-2022-37706

Ciao ragazzi, questa volta parlerò di uno 0-day recente che ho trovato in uno dei
principali window manager di Linux chiamato Enlightenment (https://www.enlightenment.org/).
Questo 0-day porta qualsiasi utente ai privilegi di root molto facilmente e all'istante.
L'exploit è testato su Ubuntu 22.04, ma dovrebbe funzionare senza problemi su qualsiasi distro.
Prima di tutto, Enlightenment è un Window Manager, Compositor e Desktop minimale
per Linux (la piattaforma primaria), BSD e qualsiasi altro sistema UNIX compatibile.
Ho installato questo window manager per sperimentarci un po'. Era interessante
per me perché contiene molti strumenti e, ad essere onesto, sembra piuttosto curato.
Dopo aver installato il pacchetto usando apt install enlightenment ho esaminato i
file e le directory installati sul mio sistema: molti moduli e molti binari
di supporto, ma la cosa più interessante è:
➜ enlightenment cd /usr/lib/x86_64-linux-gnu/enlightenment/
➜ enlightenment find . -perm -4000
./utils/enlightenment_ckpasswd
./utils/enlightenment_system
./utils/enlightenment_sys
Installa alcuni binari SUID, quindi ho pensato se potessi usarne uno
per escalare a root; i binari sembravano tutti sicuri e ben codificati.
Il binario di cui parleremo è enlightenment_sys.
Come per qualsiasi altro target, scegliamo una strategia da applicare dopo un pre-assessment
(vedi il mio blog qui se non l'hai ancora fatto: https://pwn-maher.blogspot.com/2020/10/vulnerability-assessment.html).
Ho eseguito un audit del codice con un approccio Top-Down.
E poiché questo window manager è open source, il codice sorgente è disponibile
per tutti questi binari e moduli.
Quindi la prima cosa che ho fatto è stata apt source enlightenment per ottenere tutto il codice sorgente,
e con un po' di scavo possiamo arrivare al codice del binario target.
Ma per fare il debug del binario l'ho caricato in Ghidra per l'analisi e per avere gli indirizzi
su cui impostare breakpoint e quant'altro.
Al primo tentativo non sono stati trovati simboli, ma non ce n'era bisogno, dato che
si è rivelato un binario relativamente piccolo.
Sorprendentemente, ho trovato molto più piacevole guardare il pseudo-codice decompilato di
Ghidra che guardare direttamente il sorgente (si evitano le macro e anche quei controlli
sul sistema operativo usato per compilare uno specifico blocco di codice).
Quindi iniziamo l'analisi.
1- Giocare con il binario.
Eseguiamo file per vedere alcune informazioni sul nostro target:

L'esecuzione del binario non produce alcun output:

Passare l'argomento --help ha prodotto questo output:

Scusate, lo userò per ottenere root.
Ora usiamo strace per vedere se userà syscall sospette come
execve o openat:
strace ./enlightenment_sys 2>&1 | grep open

Apre solo librerie note in percorsi in cui non abbiamo il permesso di intervenire.
strace ./enlightenment_sys 2>&1 | grep exec

2- Facciamo reverse engineering del binario e poi sfruttiamolo.
Ho creato un nuovo progetto Ghidra e ho caricato questo specifico binario.
Poiché i simboli non sono stati trovati, possiamo individuare la funzione main usando entry.
Il primo argomento della funzione entry è main stesso.
L'ho rinominata in main per riferimenti futuri.
Scorrendo un po' verso il basso posso già notare che viene usata la funzione system().
Come pwner passo giorni sulle challenge per riuscire a invocare questa specifica funzione x)
Ho reversato il binario cercando un bug di memory corruption o qualche problema nell'heap,
ma in realtà si trattava di una strana Command Injection.
Il binario prende tutte le precauzioni di sicurezza prima di eseguire system, ma purtroppo
possiamo sempre iniettare il nostro input lì dentro.

Ok, ora percorriamo il binario dall'inizio fino alla nostra funzione system, cercando di
iniettarvi il nostro input.
Prima di tutto il binario controlla se il primo argomento è --help o -h e mostra
il messaggio che abbiamo visto prima.

In secondo luogo eleva i suoi privilegi a root.

Poi rimuove quasi tutte le variabili d'ambiente (precauzioni di sicurezza) per non
invocare un altro binario non previsto.

Quindi se il primo argomento inserito è "mount" entrerà in questo ramo, controllerà alcuni
flag forniti e questi flag verranno impostati sullo stack.
Poi controlla se il parametro successivo a mount è UUID=; non vogliamo entrare
qui, quindi abbiamo passato "/dev/../tmp/;/tmp/exploit".

In questo modo superiamo il controllo alla riga 410, il controllo strncmp.
Perché se non inizia con /dev/ il binario esce.
Poi c'è una chiamata a stat64 sul file che abbiamo fornito; notate che possiamo
creare una cartella chiamata ";" e questo causerà la command injection.
Fino ad ora, l'exploit ha già creato questo file /dev/../tmp/;/tmp/exploit,
ma questo non è l'exploit che verrà chiamato.


Ci stiamo avvicinando a system() ora.
Ora p (il puntatore) viene aggiornato all'ultimo argomento passato al nostro binario SUID,
/tmp///net.
Perché passare /tmp///net quando possiamo passare /tmp/net?
In questo modo aggiriamo questo controllo:
if (((next_next == (char *)0x0) || (next_next[1] == '\0')) || ((long)next_next - (long)p != 6))
Serviva che /tmp/net esistesse e che /tmp/// avesse lunghezza 6.
Ora l'ultimo stat64 controllerà l'esistenza di "/dev/net"
__snprintf_chk(cmd,0x1000,1,0x1000,"/dev%s",next_next);
E lo troverà, quindi superiamo anche quest'ultimo controllo.
Poi controllerà la disponibilità di alcuni file, ma questo non è importante
a questo punto, perché siamo pronti e vicinissimi a innescare l'esecuzione arbitraria
di comandi.
Ora eina_strbuf_new() inizializza semplicemente il comando che verrà passato a
system; il problema qui è che lo abbiamo inserito come:
/bin/mount -o noexec,nosuid,utf8,nodev,iocharset=utf8,utf8=0,utf8=1,uid=$(id -u), "/dev/../tmp/;/tmp/exploit" /tmp///net
Ma il binario chiama eina_strbuf_append_printf() diverse volte e il comando diventa
/bin/mount -o noexec,nosuid,utf8,nodev,iocharset=utf8,utf8=0,utf8=1,uid=$(id -u), /dev/../tmp/;/tmp/exploit /tmp///net
Notate che i doppi apici vengono rimossi e saremo in grado di chiamare /tmp/exploit
come root.

Il binario ha fatto del suo meglio per mitigare qualsiasi comportamento non previsto, ma come al solito
qualsiasi cosa può essere pwnata. Non mi aspettavo di sfruttare tutto questo usando un bug logico
di questo tipo.
Voglio che la prossima CVE sia una memory corruption che porti a una LPE da root.
Divulgazione su Twitter: https://twitter.com/maherazz2/status/1569665311707734023