Categories: ⚔️, Hacking12.6 min read

Windows Kernel Exploitation

Kernel

Il compito di ogni Sistema Operativo è quello di astrarre le risorse hardware e renderle fruibili al loro utilizzatore. Volendo, potremmo vedere il Sistema Operativo come una sorta di interprete tra la macchina ed il suo operatore.

Il Kernel è il nucleo centrale del sistema operativo ed ha il compito di fornire ai processi, in esecuzione sull’elaboratore, un accesso sicuro e controllato all’hardware. È quindi il primo programma che viene caricato subito dopo la fase di boot della macchina. Una volta caricato, controlla e coordina ogni altro programma e processo, ponendosi come un “ponte” di collegamento fra le istruzioni che l’utente impartisce alla macchina e l’hardware.

Esistono varie tipologie di Kernel, in base al grado di astrazione dell’hardware:

  • Kernel monolitici: questa tipologia di kernel contiene in un unico file le varie funzioni del sistema operativo e i driver delle varie periferiche. Per ogni funzione da svolgere e per ciascuna periferica da gestire, avremo uno specifico modulo. Un esempio di Kernel monolitici sono i tradizionali kernel UNIX, Linux.
  • Microkernel: in maniera opposta alla struttura monolitica, i microkernel implementano solamente funzioni di carattere basilare, come la comunicazione tra i processi, multitasking e la gestione della memoria. Demandano le altre funzioni a programmi terzi che prendono il nome di server.
  • Kernel ibridi: utilizzati nella maggior parte dei sistemi operativi commerciali (Microsoft Windows e Mac OS) combinano alcune caratteristiche dei Kernel monolitici con quelle dei Microkernel. Difatti, hanno un insieme più ampio di moduli core.
  • Esokernel: “mascherano” il livello di astrazione fra software e hardware, in modo che un programma non conosca le risorse hardware a sua disposizione o in quale parte di memoria è stato allocato.

Windows Kernel

Dato che il nostro target è un kernel dei sistemi operativi Windows, addentriamoci più a fondo in questa specifica tipologia.

Esistono due grandi famiglie di Kernel Windows:

  • Windows 9x kernel, usati in Windows 95-98 ed ME
  • Windows NT kernel, usati in tutti I sistemi Windows NT, inclusi Windows NT, 2000, XP, Vista, 7,8,8.1, 10 e 11

Quelli su cui ci concentreremo sono i kernel NT.

Poco fa abbiamo detto che i kernel commerciali, come Windows, hanno una struttura ibrida, che combina aspetti del kernel monolitico con quelli dei microkernel. L’idea alla base è combinare i punti di forza delle due tipologie: le performance garantite dalla struttura monolitica assieme alla stabilità intrinseca dei microkernel.

L’architettura della famiglia NT è altamente modulare e si struttura in due layer principali: user mode e kernel mode.

  • user mode: i programmi hanno accesso solo a determinate risorse del sistema operativo
  • kernel mode: non ha restrizioni, godendo di un accesso alla memoria centrale ed ai device esterni

Terminata la fase teorica, prepariamo ora il nostro ambiente di laboratorio. Cominciamo dagli applicativi e script meno comuni, di cui avremo bisogno:

Adesso che sappiamo che cosa ci occorre, procediamo per passi.

FASE I – PREPARIAMO IL DEBUGGER

Portiamoci sulla nostra macchina virtuale di analisi.

Come prima cosa, dobbiamo installare il nostro debugger, contenuto nel pacchetto Software Development Kit di Microsoft (SDK). Scarichiamo l’installer delle SDK (winsdksetup.exe) al seguente percorso: https://go.microsoft.com/fwlink/p/?linkid=2196241

  1. Dopo averlo scaricato eseguiamo il setup;
  2. Selezioniamo la Directory di installazione (lasciamo pure quella di default) e facciamo click su “Next”;
  3. Al disclaimer della Privacy, scegliamo di non inviare i nostri dati a Microsoft e poi ancora “Next”, per accettare anche la licenza di utilizzo;
  4. Adesso dobbiamo selezionare quale componente installare. Installiamo esclusivamente il Debuggings Tool for Windows e spuntiamo tutti gli altri. Selezioniamo Install.

Terminata l’installazione, ci basterà digitare sulla SearchBar di Windows: windbg per lanciare il nostro debugger, ora presente fra le applicazioni disponibili.

Potremmo adesso voler cambiare la nostra User Interface (UI) con una a noi più congeniale. Utilizziamo ad esempio windbg-theme: https://github.com/lololosys/windbg-theme

Scarichiamo da github il repository in formato compresso, estraiamo ed eseguiamo il file dark.reg , eseguendo semplicemente un doppio click. Ora, riaprendo il debugger, vedremo l’interfaccia modificata in favore di una più fruibile.

Dovremo adesso installare mona e per farlo abbiamo bisogno di integrare pykd.pyd che, come abbiamo detto, ci permetterà di integrare lo script in python.

  1. Per prima cosa, abbiamo bisogno di installare Python, nel mio caso specifico ho utilizzato la versione 2.7.18 versione x86 (Windows x86 MSI installer), scaricabile al seguente link: https://www.python.org/ftp/python/2.7.18/python-2.7.18.msi
  2. Riavviamo la macchina virtuale
  3. Scarichiamo dal repository https://github.com/corelan/windbglib/raw/master/pykd/pykd.zip
    • Estraiamo sul desktop il contenuto di pykd.zip
    • Clicchiamo rispettivamente con il tasto destro prima su pyd e poi vcredist_x86.exe . Selezioniamo la voce proprietà, nella scheda “General”, e scegliamo “Unblock”, applichiamo le modifiche e diamo “Ok”.
  4. Eseguiamo (con privilegi di amministratore) vcredist_x86.exe , procedendo nell’installazione
  5. Copiamo il file pykd.pyd nel seguente percorso: C:Program Files (x86)Windows Kits10Debuggersx86winext
  6. Apriamo il cmd (sempre con privilegi di amministrazione) e digitiamo i seguenti comandi:
  • cd "C:Program Files (x86)Common FilesMicrosoft SharedVC"
  • regsvr32 msdia90.dll

In risposta, riceveremo un messagebox che ci informerà che la .dll è stata registrata con successo.

  1. Riavviamo la macchina virtuale
  2. Scarichiamo windbglib.py dal seguente percorso: https://raw.githubusercontent.com/corelan/windbglib/master/windbglib.py
  3. Salviamo il file appena scaricato al seguente percorso: C:Program Files (x86)Windows Kits10Debuggersx86 (“sblocchiamo” il file, se necessario, come fatto in precedenza al punto 3b)
  4. Scarichiamo mona.py dal seguente percorso (“sblocchiamo” il file, se necessario, come fatto in precedenza al punto 3b)
  5. https://github.com/corelan/mona/raw/master/mona.py
  6. Salviamo il file appena scaricato al seguente percorso: C:Program Files (x86)Windows Kits10Debuggersx86

Al termine della procedura, verifichiamo di aver installato tutto correttamente:

Per farlo, seguiamo i seguenti passi:

  1. Lanciamo il debugger: WinDGB;
  2. Agganciamo al debugger l’applicazione vulnerabile vulnserver. In alto a sinistra clicchiamo su File, nel menù a tendina selezioniamo Open Executable e selezioniamo il file exe ;
  3. Riduciamo ad icona la finestra del programma (che il debugger ha aperto);
  4. Portiamoci sulla barra di comando, del debugger, in basso e carichiamo il modulo pykd: .load pykd.pyd ;
  5. Lanciamo mona, utilizzando il comando !py mona

Se avete seguito la procura correttamente, il debugger dovrebbe caricare il modulo mona.

FASE II – DRIVER VULNERABILE

Adesso, dobbiamo simulare l’ambiente da attaccare sulla macchina vittima.

Per prima cosa, installiamo Python, che utilizzeremo più avanti per lanciare il nostro attacco. Scarichiamo dal sito ufficiale la release 3 (nel mio caso ho utilizzato la 3.8.10). Una volta installato, utilizziamo il comando pip per installare alcuni moduli che useremo nel nostro script di exploit: pip install infi.wioctl . Al termine, riavviamo la macchina.

Dopo il riavvio, dovremo caricare il nostro driver vulnerabile: HEVD.

  1. Come prima cosa, dobbiamo scaricare il “Driver Loader”, OSR, dal seguente link: https://www.osronline.com/OsrDown.cfm/osrloaderv30.zip e successivamente HEVD dal suo repository: https://github.com/hacksysteam/HackSysExtremeVulnerableDriver/releases/download/v3.00/HEVD.3.00.zip
  2. Estraiamoli entrambi sul nostro Desktop.

Utilizzando il OSR Loader dobbiamo adesso caricare il Driver vulnerabile nella nostra macchina vittima.

  1. Spostiamoci nella cartella OSR Driver Loader, al seguente percorso:

[cartella_decompressa]kitWNETAMD64FRE e lanciamo l’eseguibile OSRLOADER.exe con privilegi di amministrazione;

  1. Avviato il loader, clicchiamo sulla voce Driver Path e facciamolo puntare al driver HEVD scaricato, al seguente percorso
    [cartella_decompressa]HEVD3.00vulnerablex64HEVD.sys
  2. Concludiamo Selezionando in basso Register Service.

Controlliamo adesso che il nostro driver sia stato caricato con successo nella macchina vittima. Apriamo il cmd e digitiamo il seguente comando:

driverquery | findstr HEVD

A questo punto, per avviare il relativo servizio “HEVD”, dobbiamo prima disabilitare la “verifica della firma”. Per farlo, procediamo come segue:

  1. Tenendo premuto il tasto “Shift” riavviamo la nostra macchina cavia;
  2. Questa volta, prima del boot del sistema operativo, ci apparirà un menù che ci chiederà di scegliere una di tre opzioni. Scegliamo “Troubleshoot”;
  3. Nella successiva schermata “Advanced options”;
  4. Ancora “Startup Settings”;
  5. Ed infine scegliamo “Restart”;
  6. All’avvio del menù di startup, ci verranno presentati una serie si punti, quello che a noi interessa è il punto 7: “Disable driver signature enforcement”, attivabile digitando F7.

A questo punto, la nostra vittima avvierà il sistema operativo senza l’opzione di “verifica della firma”, che non ci avrebbe permesso di avviare il servizio vulnerabile.

Assicuriamoci che il driver venga caricato sempre dal nostro sistema operativo. Lanciamo il “cmd” con privilegi di amministrazione e digitiamo:
sc config HEVD start=system

FASE III – CONNESSIONE CON IL DEBUGGER

Adesso dobbiamo connettere la nostra macchina vittima con il nostro debugger. Apriamo quindi il cmd sulla macchina vittima e digitiamo il seguente comando:

bcdedit /dbgsettings NET HOSTIP:192.168.178.189 PORT:55555

Il parametro HOSTIP è l’indirizzo IP della macchina con cui facciamo il debugging; il numero di porta (parametro “PORT”) deve essere compreso fra 49151 e 65536 . Una volta dato “invio” ci verrà fornita la key identificativa.

Spostiamoci ora sulla macchina dove abbiano installato il debugger e lanciamolo.

  • In alto a sinistra scegliamo File;
  • Dal menù a tendina scorriamo sino alla voce “Kernel Debugging”;
  • Nella nuova finestra, alla voce PORT, inseriamo: 55555;
  • Alla voce key, incolliamo la nostra chiave e diamo ok.

Il debugger entrerà in stato di attesa per ricevere la connessione.

Torniamo adesso sulla macchina vittima e facciamo in modo che al prossimo restart si riavvii in DEBUG mode. Digitiamo i seguenti comandi, necessari per abilitare il debug mode e riavviare la macchina:

  • bcdedit /debug ON
  • shutdown -r -t 0

Se abbiamo effettuato tutto correttamente, al riavvio, la macchina vittima verrà connessa al nostro debugger.

Stabilita la connessione con il nostro debugger, verifichiamo che il nostro modulo HEVD sia caricato. Portiamoci sulla macchina virtuale dove abbiamo configurato il debugger: nel menù in alto scegliamo la voce “Debug”, dal menù a tendina clicchiamo su “Break”. A questo punto, abbiamo interrotto il flusso d’esecuzione sul sistema operativo vittima.

Adesso spostiamoci in basso nel debugger e digitiamo lm m H* : il comando mostra tutti i moduli caricati che cominciano con la lettera H.

Direi proprio che ci siamo: il nostro modulo è stato caricato con successo!

FASE IV – HEVD VULNERABILITÀ

Solitamente, lo scopo principale degli attacchi al Kernel di Windows è quello di ottenere privilegi più elevati, per eseguire qualsiasi attività sulla macchina vittima. L’operazione viene fatta trovando una vulnerabilità all’interno del codice in esecuzione nel “kernel-space” e stabilendo una comunicazione tra l’exploit in “user-mode” e il target in “kernel-mode” (che è lo spazio dove i driver risiedono).

Poiché Windows viene eseguito in modalità protetta, le istruzioni dell’utente non possono interagire con lo spazio di memoria del Kernel. Ma esiste un’interfaccia, messa a disposizione dal sistema operativo, che consente di interagire con i drivers: il “Device Input and Output Control” (IOCTL).

Quando un driver viene installato, per definire le routine che verranno esposte, viene utilizzata la chiamata: IoCreateDevice . Tali routine sono fondamentalmente delle funzioni che interagiscono con altri livelli del sistema operativo, per manipolare un dispositivo Hardware. È possibile identificare le varie routine attraverso un codice di controllo I/O (codice IOCTL).

In HEVD (nel nostro driver vulnerabile) queste funzioni di routine interagiscono direttamente a livello kernel, originando diverse vulnerabilità. Il driver elaborerà le chiamate a quelle routine utilizzando le strutture IRP ( I/O Request Packets) e imposterà un gestore che invierà la routine specifica con un dato codice IOCTL: IrpDeviceIoCtlHandler

FASE V – STACK OVERFLOW

IMPORTANTE: In questa parte daremo per scontato l’apprendimento della tecnica di attacco overflow, che abbiamo già trattato e che potete andare a leggere, in dettaglio al seguente link: https://thehackingquest.net/buffer-overflow-attack/ [HACKER JOURNAL NUMERO: 247]

Il driver vulnerabile HEVD può essere sfruttato per vari attacchi, noi lo utilizzeremo per perpetrare un attacco DOS.

Analizzando il driver, possiamo osservare alcune caratteristiche della tabella di jump generata attraverso la funzione IrpDeviceIoCtlHandler .

  1. Viene attivata quando il codice dello IOCTL assume il valore decimale 2236419 o 0x222003 in esadecimale, che è il punto esatto dove effettuare la chiamata BufferOverflowStackIoctlHandler
  2. Analizzando la chiamata, osserviamo che esiste un controllo che verifica se il pacchetto IRP contiene dati forniti dall’utente.
    • Se esiste, viene attivata la chiamata alla funzione TriggerBufferOverflowStack
  3. La funzione TriggerBufferOverflow presenta una chiamata a:
    • memset(&KernelBuffer, 0, 800h) che indica la lunghezza del buffer 2048 byte
  4. Al termine di TriggerBufferOverflowStack viene eseguita una chiamata a memcpy (&KernelBuffer, &UserBuffer, SizeOfUserBuffer), la quale evidenzia che possiamo “controllare” sia i dati di “UserBuffer”, che quelli “SizeOfUserBuffer”.

Quindi, se volessimo sfruttare l’attacco di bufferoverflow attraverso la variabile “KernelBuffer”, dovremmo iniettare un payload con più di 2048 byte, usando il codice IOCTL 0x222003. Non resta che provarci!

Per il nostro test utilizzeremo il seguente codice in python, scritto per l’exploit, che sfrutta la vulnerabilità sopra menzionata, riempiendo il buffer di lettere “A”:

#!/usr/bin/env python3

from infi.wioctl import DeviceIoControl

DEVICE_NAME = r'\.HackSysExtremeVulnerableDriver'

IOCTL_HEVD_STACK_OVERFLOW = 0x222003
SIZE = 3000

PAYLOAD = (
b'A' * SIZE
)

HANDLE = DeviceIoControl(DEVICE_NAME)
HANDLE.ioctl(IOCTL_HEVD_STACK_OVERFLOW, PAYLOAD, SIZE, 0, 0)

Nota: ad esempio, la vittima potrebbe ricevere il file malevolo compilato .exe, via posta.

Riavviamo la macchina vittima. Spostiamoci sulla macchina di debugging ed appena ricevuta la connessione, mettiamo in stato di running il debugger (F5).Non ci resta che fare doppio click sul file che abbiamo appena creato (o avviarlo dal cmd).

Il risultato sarà il nostro buffer pieno di lettere A (x41). Abbiamo appena compromesso il flusso d’esecuzione del nostro driver e creato le condizioni per un attacco di Denial of Service.

Go to Top