Vulnerabilità critica nei server DNS Windows (SIGRed)
cve-2020-1350 dns rce sigred windows domain controller windows server
I ricercatori di Checkpoint hanno individuato una vulnerabilità critica nei server DNS Windows; nota con il nome di SIGRed, l’identificativo ufficiale assegnatoli è CVE-2020-1350 ed ha un indice di pericolosità (in gergo CVSS base score) di 10.0. Il massimo assegnabile su tale scala.
Microsoft ha rilasciato una patch per la sua correzione nella giornata di ieri, 15 Luglio 2020 e gli amministratori di macchine Windows Server dalla versione 2003 fino alla versione 2019 sono incoraggiati ad installarla con la massima priorità.
Sono a rischio solo le macchine che espongono il servizio DNS.
La vulnerabilità, quando adeguatamente sfruttata, permette di eseguire codice privilegiato e quindi prendere possesso del server vittima in modo del tutto automatico.
Qualora non sia possibile installare immediatamente la patch, Checkpoint ha fornito un workaround che consiste nel limitare la dimensione massima dei pacchetti DNS quando veicolati tramite TCP. Le ragioni sulla necessità di questa limitazione sono chiarite nei dettagli tecnici forniti in seguito.
reg add “HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\DNS\Parameters” /v “TcpReceivePacketSize” /t REG_DWORD /d 0xFF00 /f
net stop DNS && net start DNS
Panoramica sulla vulnerabilità
I ricercatori di Checkpoint hanno deciso di focalizzarsi sulla ricerca di falle di sicurezza nell’eseguibile dns.exe, responsabile dell’implementazione server del protocollo DNS.
L’implementazione client del protocollo (non di interesse qui) si trova invece in dnsapi.dll e condivide alcune funzionalità con l’implementazione server.
Una buona strategia di ricerca di vulnerabilità in implementazioni di protocolli è quella di focalizzarsi sulle parti più complesse del protocollo stesso e, in particolare, sul parsing delle richieste ricevute.
Il protocollo DNS ha un formato di richieste (messaggi inviati dai client al server) piuttosto semplice e quindi probabilmente il loro parsing è correttamente implementato.
Le risposte DNS (i messaggi inviati dal server ai client) sono invece varie e con il tempo sono nati tipi di record DNS con strutture anche complesse.
Sebbene questo possa sembrare non rilevante ai fini della compromissione di un server, la natura gerarchica dell’infrastruttura DNS fa sì che un dato server debba delegare ad un altro (più autoritativo) la risposta alla richiesta di un suo client.
In questo “passaggio di carte” il server in oggetto si trova quindi nella necessità di processare la risposta del server più autoritativo ed è quindi dotato del relativo codice di parsing con annesse eventuali falle.
Ammesso che il suddetto codice contenga una vulnerabilità, non è scontato che sia usabile da un attaccante. Infatti affinchè una vulnerabilità nel parser di un messaggio sia innescata è necessario avere il pieno controllo sul contenuto del messaggio (che tipicamente risulterà essere di natura non valida).
Tuttavia i server autoritativi configurati dagli amministratori non sono sotto il controllo dell’attaccante (si pensi al server 8.8.8.8 che è di Google) e non vi è possibilità di generare messaggi arbitrari da questi server.
I ricercatori di checkpoint hanno aggirato questa limitazione utilizzando i record NS (NameServer) dello stesso protocollo DNS.
Questi record sono usati per denominare quali computer gestiscono un insieme di sottodomini.
In questo modo è possibile forzare il server da attaccare a contattare un server DNS sotto il pieno controllo dell’attaccante.
Stabilito che è possibile controllare i messaggi di risposta da far processare al server vittima, rimane da trovare una vulnerabilità.
I ricercatori di Checkpoint hanno individuato il seguente codice nella funzione SigWireRead (funzione per la gestione dei record SIG e da cui deriva il nome della vulnerabilità):
lea rcx, [REL nameLength]mov r9, rdi
call Name_PacketNameToCountNameEx ;Lunghezza della stringa decompressa
mov rbp, rax
test rax, rax
je _da_qualche_altra_parte
movzx ecx, BYTE [nameLength] ;NB: la lunghezza è a 8 bit
sub rdi, rax ;Lunghezza della firma
add cx, 20 ;Lunghezza nome + 20
add cx, di ;Lun. nome + 20 + lun. firma
call RR_AllocateEx ;Alloca buffer
Da cui si vede che viene allocato un buffer (chiamata a RR_AllocateEx) la cui lunghezza (il primo parametro in convenzione Win64 è in rcx) è di 16 bit!
Se quindi la lunghezza della firma contenuta nella risposta fosse abbastanza vicina a 65536 (la sedicesima potenza di 2) si avrebbe un integer overflow sulla dimensione allocata.
In gergo, il registro cx, che è di 16 bit, “wraps around to 0” per cui se venisse richiesta una dimensione di 70’000 bytes, ne verrebbero effettivamente allocati solo 4’464 (hint: in esadecimale è banale da vedere).
La routine di gestione delle risposte SIG penserebbe però di avere a disposizione tutti i 70’000 bytes e finirebbe per corrompere strutture di memoria vicine.
Notare come non vi siano controlli sull’overflow della dimensione.
Sebbene in teoria innescare la vulnerabilità sembri semplice, in pratica la faccenda è complicata dal fatto che il protocollo DNS limita la dimensione delle richieste a 512 bytes (o 4 KiB nel caso di EDNS0).
Leggendo le specifiche, i ricercatori di Checkpoint si sono accorti che lo standard permette di rispondere con richieste più lunghe a patto di passare a connessioni TCP.
Il server che deve inviare una risposta di grosse dimensione può impostare un flag per indicare al client (che in questo caso è il server sotto attacco) di riprovare la richiesta usando TCP.
Tuttavia anche questo non è sufficiente di per sè: infatti la dimensione massima dei pacchetti TCP è 65536 byte (proprio perchè il campo di dimensione è a 16 bit) e questa include tutta una serie di campi che non sono usati nel calcolo visto sopra.
Il risultato è che non è possibile ottenere una dimensione da allocare che superi i 65536 byte.
I ricercatori di Checkpoint non si sono però arresi ed hanno usato un ingegnoso trucco: la risposte DNS possono infatti condifcare le stringhe riusando più volte i soliti byte all’interno del messaggio.
In questo modo è possibile avere una dimensione logica della risposta maggiore della sua dimensione fisica (limitata da TCP).
Questo è il contributo dell’addendo nameLength nel codice sopra.
In questo modo i ricercatori di Checkpoint sono riusciti a far crashare il server DNS sotto attacco. Un crash è sempre il punto di partenza per rendere effettiva una falla di sicurezza.
Attacco da browser
I ricercatori hanno poi proseguito notando che i server DNS Windows accettano connessioni TCP sulla porta DNS (la 53) e grazie alle funzionalità di Connection reusing/Pipelining ed alla generalità del protocollo DNS, si sono accorti che è possibile innescare la vulnerabilità anche con un browser.
Le funzionalità di Connection reusing e Pipelining permettono infatti di inviare richieste multiple in un unico messaggio (la prima) e di non dover attendere la risposta (la seconda).
Inviando una richiesta POST al server DNS, questo interpreterà gli header HTTP con una prima richiesta DNS di dimensione 20’559 bytes (ovvero 0x504f, cioè i caratteri ‘P’ ed ‘O’ derivanti dal comando HTTP di POST) ed eventuali richieste extra se propriamente messe nel corpo.
Uso della vulnerabilità
Il crash del server è il primo passo per l’uso della vulnerabilità. Generalmente la corruzione dell’heap si può sfruttare in due modi:
- Sovrascrivendo qualche puntatore di funzione con un valore controllato dall’attaccante per far deragliare il flusso di esecuzione.
- Costruire delle primitive di lettura e scrittura ad indirizzi arbitrari.
Il punto primo non è usabile poichè dns.exe è generalmente compilato con la tecnologia Control Flow Guard di Microsoft.
Questa tecnologia si basa sull’ausilio del compilatore per generare una lista di target validi per ogni istruzione di salto indiretto (incluse call).
Questo ovviamente include il caso di un puntatore a funzione corrotto.
Il punto secondo è più lungo da attuare e non è stato descritto dai ricercatori di Checkpoint (per farsi un’idea è possibile leggere un caso da manuale qui) se non a grandi linee.
Lo scopo finale è quello di avere una primitiva di scrittura ad indirizzi arbitrari con valori controllati dall’attaccante. L’idea è quello di sovrascrivere l’indirizzo di ritorno (e quello soltanto) della funzione di parsing (notare che questo non è in generale sufficiente per via di tecniche come W^X ed Intel CFE, ma è un inizio. Altri dettagli sono stati omessi).
Per ottenere la primitiva di scrittura i ricercatori hanno studiato la struttura dei metadati dell’heap usato dal server DNS e hanno visto che corrompendo queste strutture è possibile indurlo a credere che un’area di memoria arbitraria sia libera per essere allocata.
Quando questa viene effettivamente allocata ed usata dalle normali funzioni di parsing, il contenuto di quell’area viene sovrascritto con dei valori provenienti dalla risposta DNS (e quindi controllabili dall’attaccante).
Per raggiungere lo scopo di sovrascrivere l’indirizzo di ritorno è però necessario sapere in quale area di memoria questo si trovi (in modo da corrompere opportunamente l’heap).
Tuttavia grazie a tecnologie come ASRL questo compito non è banale, i ricercatori di Checkpoint hanno creato una primitiva di infoleak la quale ritorna dati utili ad individuare dove sia l’indirizzo cercato.
Questa funziona corrompendo ancora una volta l’heap ma in questo caso vengono sovrascritti i dati di una precedente risposta DNS salvata in cache per questioni di performance.
Ripetendo poi la richiesta DNS che ha portato alla creazione della risposta in cache, il server ritorna i dati corrotti che contengono informazioni sulla struttura del suo address space.
L’implementazione pratica di queste primitive e il loro utilizzo per eseguire uno shellcode che porti all’esecuzione di codice arbitrario è comunque laboriosa.
Considerando però che dns.exe è eseguito con privilegi dell’account SYSTEM e la sensibilità dei server DNS (che spesso sono anche Domain Controller) è possibile che un gruppo APT spenda l’effort necessario per lo sfruttamento completo della vulnerabilità.
Una volta ottenuto un payload funzionante, non richiedendo intervendo umano, nè da parte delle vittime, nè da parte degli attaccanti, è possibile modificarlo per una diffusione automatica e sistematica.
Per questi motivi, in un gergo emerso recentemente, la vulnerabilità è detta “wormable“.