
(1) IQVW32.sys precedente alla versione 1.3.1.0 e (2) IQVW64.sys precedente alla versione 1.3.1.0 nel driver di diagnostica Ethernet Intel per Windows consente agli utenti locali di causare un denial of service o eventualmente eseguire codice arbitrario con privilegi di kernel tramite una chiamata IOCTL appositamente predisposta (a) 0x80862013, (b) 0x8086200B, (c) 0x8086200F o (d) 0x80862007.
(1) IQVW32.sys prima della versione 1.3.1.0 e (2) IQVW64.sys prima della versione 1.3.1.0 nel driver di diagnostica Ethernet Intel per Windows consente agli utenti locali di provocare un denial of service o potenzialmente eseguire codice arbitrario con privilegi di kernel tramite una chiamata IOCTL appositamente modificata (a) 0x80862013, (b) 0x8086200B, (c) 0x8086200F, o (d) 0x80862007.
Questo repository contiene un'analisi della vulnerabilità in questione, insieme a proof-of-concept funzionanti su Windows 7 SP1 a 64 bit e Windows 10 20H2. Il file del driver si trova nella directory Driver Files. Se dovessi trovare errori di battitura nell'analisi/documento, o se desideri vedere alcuni dettagli con una descrizione più approfondita, apri una issue sul repository! Li correggerò appena possibile.
La motivazione alla base della scrittura di un exploit per questo driver di dispositivo in particolare è dovuta unicamente al fatto che attualmente viene abusato in natura per caricare un rootkit non firmato dell'attaccante. Utilizzando il metodo BYOVD (Bring Your Own Vulnerable Driver), il malware può verificare se è in esecuzione con privilegi elevati, rilasciare una copia del driver vulnerabile, caricare il driver e successivamente sfruttarlo per ottenere l'esecuzione di codice nel kernel e caricare il rootkit. Non sono riuscito a fare reverse engineering del campione di malware, quindi mi sono preso l'incarico di creare l'exploit.
Campioni individuati in natura: https://bazaar.abuse.ch/sample/84ed7fec67de5621806dbb43af5167a5fc60ab7f2403448519dc0eca2b8f9022/ https://bazaar.abuse.ch/sample/0925b8985b19d7925d68186d666b0050a4cb3f2a577d64765d770a57a2eab9ae/ https://bazaar.abuse.ch/sample/e8b7f42d544fe8b954c4021315cff2fdd44d67d11704009cdf3037d34e0c0a93/
Il driver di dispositivo, ovvero iqvw64e.sys, è un driver progettato per eseguire la diagnostica della scheda di rete. Esso consente al componente in modalità utente di interagire con il driver di dispositivo per eseguire una miriade di routine del kernel esponendo alcuni codici di controllo I/O (noti anche come IOCTL), con un codice di controllo I/O "sub" fornito nel buffer di input dell'utente durante l'interazione. Il codice di controllo I/O che verrà utilizzato per raggiungere il percorso di codice vulnerabile è 0x80862007. Oltre al codice di controllo primario, i summenzionati codici di controllo I/O "sub" che verranno trattati in questa analisi saranno il codice 0x33 per raggiungere la chiamata di funzione memmove, e il codice 0x30 per raggiungere i percorsi di codice della chiamata di funzione memset. Questa analisi non coprirà alcun dettaglio relativo alla routine DriverEntry, poiché sulla pagina della documentazione Microsoft c'è abbastanza documentazione per fornirti una spiegazione approfondita.
Per cominciare, vogliamo sapere come possiamo interagire con questo particolare driver di dispositivo. Il mezzo più comune per comunicare con un driver di dispositivo è l'uso di una funzione chiamata DeviceIoControl. L'idea generale alla base di questa funzione è che possiamo passare un handle del driver valido creato da CreateFileA, fornire un codice di controllo I/O che corrisponde alla routine del kernel che vogliamo, passare una struttura (o buffer) che si aspetta, e restituirà i dati nel nostro buffer di output. Sebbene routine come queste possano talvolta essere necessarie (ad es. accedere ai registri specifici del modello per scopi di overclocking), rappresentano anche un serio rischio per la sicurezza. Ma... come?
Nel caso di CVE-2015-2291, la vulnerabilità può essere innescata da un utente senza privilegi. Poiché non sono presenti controlli di sanificazione, e i privilegi di amministratore non sono richiesti per sfruttare la vulnerabilità, questo rappresenta un rischio per la sicurezza. Ciò che si cela sotto questi due difetti è la capacità di controllare completamente le chiamate di funzione memset e memmove esposte dall'interfaccia dei codici di controllo I/O. Ricordi la suddetta funzione DeviceIoControl di prima, come siamo in grado di passare una struttura che verrà utilizzata in una routine del kernel? È così che tutto si unisce.
Facciamo un passo indietro. Per prima cosa vogliamo ottenere l'handle del driver relativo al driver di dispositivo vulnerabile. Ancora prima di questo, però, dobbiamo individuare il named device object corrispondente. Questi sono esposti allo spazio utente tramite un symbolic link (comunemente hard-coded), che può essere trovato usando WinObj, parte della [suite SysInternals]. Sebbene potremmo usare un'utility di dump delle stringhe per estrarre il symbolic link, o in alternativa fare reverse engineering del driver di dispositivo, ho semplicemente caricato il driver e lo ho individuato usando WinObj. Il symbolic link trovato in relazione al driver di dispositivo è \\.\GLOBALROOT\Device\Nal. Per ottenere l'handle del driver, dobbiamo chiamare la funzione CreateFileA e fare in modo che restituisca un handle del driver valido da usare successivamente nel processo. Il codice per questo processo è il seguente:```C
if (h_nal == (HANDLE)-1)
{
printf("\n[-] Unable to obtain a driver handle to the Nal device driver. Error: %d (0x%x)", GetLastError(), GetLastError());
unused = getchar();
return 1;
}
printf("\n[+] Obtained a driver handle to the Nal device driver. Handle Value: 0x%p", h_nal);
Utilizzeremo l'handle del driver più avanti nel processo di exploitation. Per ora, inizieremo la preparazione del nostro exploit. Il passo successivo sarebbe caricare la libreria `ntdll.dll` usando la funzione [LoadLibraryA](https://docs.microsoft.com/en-us/windows/win32/api/libloaderapi/nf-libloaderapi-loadlibrarya) per restituire un [module handle](https://docs.microsoft.com/en-us/windows/win32/winprog/windows-data-types), così da poter individuare dinamicamente le funzioni di cui abbiamo bisogno. Sebbene la libreria `ntdll.dll` possa essere già caricata nel nostro processo, dobbiamo comunque ottenere un handle alla libreria che possiamo usare. Le funzioni che ci servono per l'exploitation sono [NtQuerySystemInformation](https://docs.microsoft.com/en-us/windows/win32/api/winternl/nf-winternl-ntquerysysteminformation) per far trapelare l'indirizzo di base del kernel NT (con integrità di processo media) per le fasi successive del processo di exploitation, e la funzione [NtQueryIntervalProfile](http://undocumented.ntinternals.net/index.html?page=UserMode%2FUndocumented%20Functions%2FNT%20Objects%2FProfile%2FNtQueryIntervalProfile.html) per attivare la vulnerabilità. Per quanto riguarda il codice per caricare la libreria `ntdll.dll`, è il seguente:```C
h_ntdll = LoadLibraryA("C:\\Windows\\System32\\ntdll.dll");
if (!h_ntdll)
{
printf("\n[-] Failed to load the \"ntdll.dll\" API library. Error: %d (0x%x)", GetLastError(), GetLastError());
unused = getchar();
return 0;
}
printf("\n[+] Loaded the \"ntdll.dll\" API library. Handle Value: 0x%p", h_ntdll);