Gli aggiornamenti di sicurezza rilasciati da Microsoft l’11 aprile 2023 hanno affrontato oltre 90 vulnerabilità individuali. Di particolare rilievo è stato il CVE-2023-21554, soprannominato QueueJumper, una vulnerabilità nell’esecuzione remota del codice che interessa il servizio Microsoft Message Queueing (MSMQ). MSMQ è un componente opzionale di Windows che consente alle applicazioni di scambiare messaggi tramite code di messaggi raggiungibili sia localmente che da remoto. Questa analisi è stata condotta in collaborazione con i team Randori e X-Force Adversary Services, da Valentina Palmiotti, Fabius Watson e Aaron Portnoy.
Le seguenti qualità del CVE-2023-21554 hanno attirato l'attenzione iniziale:
Resta al passo con le tendenze più importanti e interessanti del settore relative ad AI, automazione, dati e oltre con la newsletter Think. Leggi l' Informativa sulla privacy IBM.
Per determinare come rilevare la presenza di MSMQ, è stato configurato un sistema di test con il componente in un ambiente di laboratorio. Durante la connessione all'endpoint MSMQ sulla porta 1801, il server non ha risposto con alcun dato. Si potrebbe supporre che il server non risponderà senza aver ricevuto dati ragionevolmente formulati. Per capire come condurre una conversazione con l'endpoint, era necessaria un'ulteriore ispezione del codice sottostante.
Dall'avviso, non è immediatamente chiaro quale binario sia stato corretto. Il primo passo per identificare il codice modificato è stato scaricare il file di aggiornamento. Questo è stato ottenuto cercando KB5025224 nel catalogo Microsoft Update.
Risultati della ricerca nel catalogo Microsoft Update per KB5025224
Nello specifico, il file di aggiornamento cumulativo
windows10.0-kb5025224-x64_3522b1b562ed44bc949541dd1c7d08e1e967d925.msu
per Windows 11 per sistemi basati su x86 è stato recuperato.
L'estrattore 7-Zip è stato utilizzato per decomprimere il contenuto del file MSU e i file CAB in esso contenuti. La ricerca della stringa "msmq" in tutti i file nelle cartelle risultanti ha prodotto il file Container Index,
Windows11.0-KB5025239-x64/express.psf.cix.xml,
che includeva la seguente voce:
<File id="21296" name="amd64_microsoft-windows-msmq-queuemanager-core_31bf3856ad364e35_10.0.22621.1555_none_5f975f8fdb349517\f\mqqm.dll" length="34811" time="133245266510000000" attr="128">
Questa voce fa riferimento a
Per identificare le modifiche apportate tra le versioni con e senza patch del fileMQQM.dll1.00
Risultati BinDiff tra file MQQM.dll patchati e non patchati
L'analisi di queste funzioni tramite Hex-Rays Decompiler ha rivelato simboli per la versione patchata contenente nomi di funzioni verbosi che sembrano riferire agli identificatori di vulnerabilità del Centro di ricerca Microsoft (MSRC):
Simboli delle caratteristiche all'interno di MQQM.dll
Dopo un esame più attento, i simboli contenenti i numeri di caso MSRC corrispondono a flag di caratteristiche per correzioni di vulnerabilità. Le caratteristiche sono una componente di Windows che attiva varie funzionalità e esperimenti. Qui, la "caratteristica" scoperta è in realtà una patch per una vulnerabilità. Se la caratteristica è abilitata, viene eseguito il percorso di codice sicuro e e con la patch. Altrimenti, viene eseguito il percorso di codice originale senza patch.
Snippet di codice decompilato che mostra il flag di caratteristiche per la correzione della vulnerabilità
Una verifica delle caratteristiche era presente per ciascuna delle vulnerabilità che comprendevano CVE-2023-21554 sia nei componenti utente che in quelli del kernel. L'analisi dei binari rimanenti contenuti nel pacchetto di aggiornamento di sicurezza ha rivelato che sono state introdotte anche le caratteristiche per la maggior parte o tutte le vulnerabilità corrette nel Patch Tuesday di aprile 2023.
Il riferimento incrociato di questi flag ci ha aiutato a individuare rapidamente le posizioni del codice con la patch. In particolare, i cambiamenti all'interno di che si riferivano al flagMSRC76146_MSMQ_OOBRWFixes sembravano quelli di maggior interesse.
Il controllo per questa caratteristica avviene otto volte all'interno della funzione. Di seguito è illustrato uno di questi controlli:
Snippet di codice decompilato che mostra la correzione della vulnerabilità
Nel frammento di codice decompilato mostrato sopra, il percorso del codice con la patch ottiene il puntatore alla sezione successiva del messaggio MSMQ tramite una chiamata a
Estratto di decompilazione della funzione GetNextSectionPtrSafe
Dopo aver esaminato tutte le occorrenze di questa caratteristica e di altri, è stato determinato che CVE-2023-21554 è composto da diversi bug che non verificano insufficientemente la dimensione dei vari tipi di sezioni di messaggi MSMQ. Queste vulnerabilità consentono di aumentare il puntatore alla fine del messaggio MSMQ di un offset arbitrario di 32 bit al di fuori dei limiti del buffer originariamente allocato.
Dopo l'elaborazione dei pacchetti, il passaggio finale di
Controllo di accesso alla memoria fuori limite per la fine del messaggio
Durante la valutazione della sfruttabilità delle vulnerabilità, sono state identificate ulteriori vulnerabilità del kernel nel driver del kernel corrispondente per il servizio MSMQ, . Le vulnerabilità erano nuovamente indicate da simboli caratteristiche, che fanno riferimento anche al caso MSRC 76146 come osservato in .
Simboli delle caratteristiche all’interno di mqac.sys
L’analisi del flag delle caratteristiche di
Per eseguire le operazioni associate, il componente del kernel analizza varie sezioni del buffer dei pacchetti MSMQ. Di seguito sono illustrati due di questi casi:
Snippet di codice decompilato che mostra la caratteristica per la correzione di vulnerabilità nel driver mqac.sys
Come si vede nel frammento di codice decompilato qui sopra, se la caratteristica è abilitata, le funzioni "sicure"
La maggior parte del codice relativo al parsing del protocollo si trova all’interno della libreria che viene caricata dal processomqsvc.exe, deve essere inviata una richiesta MSMQ valida dal client. Le specifiche e il protocollo dei messaggi MSMQ sono vasti e complessi. Inizialmente è stato trovato un codice pubblico per la scansione dei server MSMQ in un generico GitHub che ha fornito la base per un’esplorazione più approfondita dell’elaborazione dei pacchetti. Inoltre, la documentazione Microsoft è stata utile per comprendere il formato dei messaggi MSMQ.
Il codice all’interno della DLL accetta i dati tramite la porta 1801 tramite una chiamata alla funzione WSARecv, come si può vedere nella sessione di debug qui sotto:
0:020> bp ws2_32!wsarecv ; g
Breakpoint 0 hit
WS2_32!WSARecv:
00007ffd`651115c0 48895c2408 mov qword ptr [rsp+8],rbx ss:000000aa`f07fef30=0000000000000006
0:019> k
# Child-SP RetAddr Call Site
00 000000aa`f07fef28 00007ffd`57ba9cb0 WS2_32!WSARecv
01 000000aa`f07fef30 00007ffd`57b9683d
MQQM!NoReceivePartialBuffer+0x38
02 000000aa`f07fefb0 00007ffd`57b2078e MQQM!CWinsockConnection::ReceivePartialBuffer+0x7d
03 000000aa`f07ff010 00007ffd`57b25b28
MQQM!CSockTransport::BeginReceive+0xe6
04 000000aa`f07ff060 00007ffd`57b2d40b
MQQM!CSockTransport::NewSession+0x194
05 000000aa`f07ff3f0 00007ffd`57b2d367 MQQM!CSessionMgr::AcceptSockSession+0x6f
06 000000aa`f07ff430 00007ffd`645e26bd MQQM!AcceptIPThread+0x6a7
07 000000aa`f07ff7d0 00007ffd`6650a9f8
KERNEL32!BaseThreadInitThunk+0x1d
08 000000aa`f07ff800 00000000`00000000 ntdll!RtlUserThreadStart+0x28
0:019> r @$t0=@rdx
0:019> pt
WS2_32!WSARecv+0x19f:
00007ffd`6511175f c3 ret
0:019> dc poi(@$t0+8) LC
000001b5`880ff490 62f06110 524f494c 00000070 5a5a5a5a .a.bLIORp…ZZZZ
000001b5`880ff4a0 0073006e 00610074 0063006e 00200065 n.s.t.a.n.c.e. .
000001b5`880ff4b0 00440049 00000000 00000000 00000000 I.D………….
0:019> ba r1 poi(@$t0+8) ; g
Breakpoint 1 hit
MQQM!CBaseHeader::SectionIsValid+0x154:
00007ffd`57b53450 7462 je
MQQM!CBaseHeader::SectionIsValid+0x1b8 (00007ffd`57b534b4) [br=1]
La funzione è responsabile dell’analisi dell’inizio di un nuovo messaggio, che inizia con una struttura BaseHeader della forma:
Struttura dell'oggetto BaseHeader
I campi dei messaggi in arrivo sono convalidati da
Codice di disassembly per SectionIsValid all’interno di MQQM.dll
Dopo la validazione iniziale, la maggior parte dell’analisi del protocollo avviene all’interno della funzione costruttore
Decompilazione che mostra vulnerabilità nella gestione di EodHeader
La decompilazione sopra mostra il percorso con patch che chiama la funzione chiamata
La struttura di un
Disassembly che calcola la dimensione totale della sezione EodHeader
Nel disassembly qui sopra, il puntatore all'inizio di
Sessione WinDbg che mostra una violazione di accesso durante la scrittura alla fine di un messaggio MSMQ non valido
Per valutare la forza della scrittura fuori limite ottenuta dalle vulnerabilità, abbiamo innanzitutto indagato l'allocazione e la gestione della memoria sottostante.
Prima che un pacchetto di messaggiMSMQ. Questa funzione richiama il driver del kernel tramiteNtDeviceIoControl.
Decompilazione della funzione di allocazione del buffer dei pacchetti
Il componente kernel restituisce un buffer che viene partizionato da una vista mappata di un file su disco, memorizzato nella directory
Il layout della memoria ProcessHacker del processo MQSVC che mostra la memoria buffer dei pacchetti è una mappatura di file
L'indirizzo del buffer dei pacchetti, insieme a un handle di pacchetto casuale, viene restituito nello spazio utente dopo la richiesta di allocazione tramite IOCTL. Il driver utilizza l'handle per calcolare l'indirizzo del buffer del pacchetto quando gestisce le richieste dallo spazio utente.
La memoria di supporto per i buffer vulnerabili si trova all'interno di un file mappato, e quindi non in un heap del processo. Inoltre, la memoria interessata non memorizza alcun oggetto. L'unica cosa contenuta nella mappatura del file è il contenuto dei messaggi MSMQ insieme ai metadati memorizzati nel
Inoltre, senza una conoscenza preventiva della disposizione della memoria, non è possibile conoscere la posizione relativa del file che si mappa all'heap del processo per sovrascrivere i puntatori dell'oggetto. Pertanto, per ottenere l'esecuzione di codice in remoto, è necessario un metodo per modellare la memoria in una posizione prevedibile.
Dopo aver sperimentato l'invio di messaggi MSMQ legittimi al target, è stato scoperto che è possibile forzare una nuova allocazione di heap adiacente a una mappatura dei file
Layout di memoria del processo MQSVC che mostra un nuovo heap allocato adiacente a una mappatura di file MQ
Tuttavia, ciò richiede che il malintenzionato abbia accesso per inviare messaggi ad almeno una coda del bersaglio, altrimenti i messaggi verranno scartati e sovrascritti e quindi non occuperanno memoria dei pacchetti in modo persistente.
Negli esperimenti di ricerca condotti, è stato scoperto che è possibile per un utente non privilegiato creare una coda aperta a chiunque per inviare messaggi da remoto, di default. La coda persiste nel sistema indefinitamente. Sebbene la maggior parte degli usi moderni di MSMQ esistano come componenti e adattatori opzionali, fornitori come Oracle, Veritas e Xerox offrono software aziendali che si basano su MSMQ come dipendenza principale. Pertanto, lo scenario dell’attacco non è irrealistico.
La vulnerabilità di accesso alla memoria out-of-bound introduce una primitiva di utilizzare che consente a un
client malintenzionato di scrivere unOnDiskExtensionHeader
allocata per il pacchetto.
Quando si valuta una vulnerabilità che permette di scrivere valori su un offset, alcune proprietà
devono essere considerate:
Nel caso delle vulnerabilità QueueJumper, l’offset del buffer allocato alla scrittura OOB è controllabile perché viene calcolato utilizzando dati controllati dal malintenzionato. Tuttavia, il controllo sul contenuto della scrittura OOB è limitato.
In questo caso,Address. La serie di operazioni che si verificano è la seguente:
0x000000000000000C
è scritto inAddress0x00000000Address+0x2
0x0000000000000000Address+0x12
0x0000000000000000
è scritto in Address+0x1A
0x00000094
è scritto Address+0xE
0x0000Address+0x220x0000
è scritto in Address+0x62
memcpy(Address+0xA6, Source, Source->AddressLength+0x08)
IlSourcememcpy definisce un unico indirizzo di trasporto di un tipo specifico (ad esempio, NetBIOS). La definizione è la seguente:
typedef struct _TA_ADDRESS {
USHORT AddressLength;;
USHORT AddressType; Info;
UCHAR Address[1];;
} TA_ADDRESS, *PTA_ADDRESS;
Specifica il numero di byte in un indirizzo del tipo di indirizzo specificato.
Specifica il tipo di indirizzo di trasporto.
Specifica un array di dimensioni variabili contenente l’indirizzo di trasporto.
In una sessione di debug qui sotto,TA_ADDRESS
rax=0000014e1d110180 rbx=0000014e1d110180 rcx=0000014e1d110184
rdx=0000014e1ce86b10 rsi=0000014e1d110001 rdi=0000000545d7fa50
rip=00007ff843208074 rsp=0000000545d7f940 RBP=0000000545d7f980
r8=000000000000000c r9=0000000000000002 r10=0000000000000000
r11=0000000545d7F938 r12=0000000000000002 r13=0000000000001000
r14=0000014e1ce86b10 r15=0000000000000000
iopl=0 nv up ei pl nz na po nc
cs=0033 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000206
MQQM!CQmPacket::CQmPacket+0x89c:
00007ff8`43208074 e8a5780a00 call MQQM!memcpy (00007ff8`432af91e)
0:017> dc rcx
0000014e`1d110184 00010004 00000000 8620a8c0 00000000 ………. …..
Sopra, l’oggettoTA_ADDRESS, un tipo di1. La conversione di ogni byte in un ottetto IPv4 produce l'indirizzo, che è l’indirizzo di origine IPv4 del cliente.
Questo scenario consente la scrittura di dati controllati dal malintenzionato tramite un indirizzo a un offset controllato rispetto all’allocazione dei pacchetti in memoria. Sebbene sia molto restrittivo e forse poco pratico, questo potrebbe teoricamente essere utilizzato per controllare con precisione i valori scritti nella memoria, effettuando richieste multiple da indirizzi di origine diversi con offset di pacchetti decrescenti.
A causa delle numerose limitazioni incontrate per ottenere l'esecuzione alla cieca di codice in modalità remota, sono stati studiati scenari di attacco alternativi, come fughe di informazioni.
Un attacco preso in considerazione è stato quello di corrompere il puntatore a una sezione specifica del messaggio per puntare alla memoria fuori limite e poi recuperare il messaggio per leggere la memoria adiacente.
Tuttavia, all'inizio del parsing di ogni sezione, viene effettuato un controllo sulla posizione della sezione corrente, quindi non possiamo ingannare la logica di parsing e farle analizzare una sezione di dati che è fuori dai limiti.
Un altro possibile scenario di attacco consiste nel sovrascrivere l'OnDiskExtensionHeader di un altro messaggio per ingannare il destinatario del messaggio sulle origini del messaggio ricevuto. Allo stesso modo, è possibile sovrascrivere il contenuto dei messaggi destinati alle code a cui il malintenzionato non ha accesso. La sovrascrittura di contenuti specifici di un messaggio richiederebbe che il malintenzionato disponga di informazioni sul messaggio, come la sua dimensione.
Esiste una sezione opzionale chiamata SoapHeader che è suscettibile all'accesso ai dati fuori limite, con il seguente formato:
Formato del pacchetto SoapHeader
È stato osservato che la memoria utilizzata per memorizzare i dati dei pacchetti MSMQ non viene cancellata correttamente quando il buffer viene liberato. Di conseguenza, inviare un pacchetto MSMQ di dimensioni inferiori rispetto a un precedente pacchetto MSMQ comporterà una disposizione di memoria contenente i dati del pacchetto corrente seguita dai dati provenienti da pacchetti MSMQ precedentemente processati.
Si è scoperto che il
Il controllo limitato sui contenuti di scrittura, con scritture aggiuntive non controllate che danneggiano la memoria adiacente, insieme alla conoscenza limitata del layout dei messaggi, si sono rivelati un grosso ostacolo allo sfruttamento.
Al momento dell'analisi, solo le vulnerabilità dello spazio utente coinvolte nell'elaborazione iniziale di un pacchetto di messaggi MSMQ sono state valutate per la sfruttabilità. L'analisi delle vulnerabilità delle varianti del kernel potrebbe scoprire ulteriori vie di sfruttamento. Ad esempio, è possibile che l'inclusione di un'intestazione non valida possa ottenere una primitiva di scrittura nella mappatura della memoria del kernel relativa al file. In alcuni casi, controlli nell'elaborazione dello spazio utente impediscono al kernel di eseguire alcune operazioni di post-elaborazione.
Inoltre, nel caso delle varianti kernel, il malintenzionato ha meno controllo diretto sul contenuto dei messaggi e opera anche sulla stessa mappatura file dello spazio utente, non in un kernel pool (heap). Anche le mitigazioni, come KASLR, presentano ostacoli difficili da superare per lo sfruttamento remoto.
Grazie all’implementazione del codice con la patch, un client remoto può determinare in sicurezza se un sistema ha applicato l’aggiornamento. Ciò può essere fatto inviando un messaggio MSMQ che attiva la vulnerabilità, ma non provoca effettivamente un accesso fuori limite, evitando quindi di bloccare il processo del servizio. Se il flag HTTP è impostato nell’UserHeader del messaggio, è atteso un SRMPEnvelopeHeader. Nella patch, viene effettuato un controllo per un overflow intero sul campo moltiplicato per 2. Questa lunghezza può essere impostata in modo che il risultato della moltiplicazione si riversi in un numero piccolo pari alla dimensione effettiva dei dati.
Frammento di codice decompilato che mostra il controllo di overflow intero su SRMPEnvelopeHeader DataLength
Se al target viene aggiunta la patch, la vulnerabilità provoca l'esecuzione di un'eccezione e il server interrompe l'elaborazione del messaggio, quindi nessuna risposta viene inviata al client. Se non viene aggiunta la patch, il server elaborerà il messaggio normalmente e invierà una risposta.
A causa della posizione di backup della memoria dei buffer vulnerabili, l'esecuzione di codice remoto sembra più fattibile per un malintenzionato con la capacità di inviare messaggi a qualsiasi coda sull'obiettivo. In questo scenario, è possibile fare un groom in modo che venga allocato un heap adiacente. Il controllo estremamente limitato sui contenuti scritti presenta delle sfide per lo sfruttamento remoto. A causa di mitigazioni come ASLR, è necessario ottenere anche una fuga di informazioni. È possibile che una singola scrittura di una costante (0xC, 0x0
In definitiva, concludiamo che, sebbene lo sfruttamento remoto di QueueJumper possa non essere impossibile, i requisiti sopra menzionati ne rendono difficile la realizzazione. Questo post sul blog tratta solo superficialmente una specifica, un servizio e un protocollo molto complessi. È necessario approfondire la ricerca sulle vulnerabilità sia nello spazio utente che nelle operazioni dei componenti del kernel.