Questa volta hai esagerato disturbando il riposo di un host
Analisi riassuntiva
- Il ricercatore di Akamai Ben Barnea ha individuato due importanti vulnerabilità in un servizio RPC di Microsoft Windows, a cui sono stati assegnati i codici CVE-2022-37998 e CVE-2022-37973 con un punteggio base di 7,7.
- Le vulnerabilità sfruttano diversi bug presenti nell'interfaccia RPC di Local Session Manager.
- Le vulnerabilità consentono di sferrare attacchi Senial-of-Service che impediscono il funzionamento dei servizi container e sessione (come Microsoft Defender Application Guard, Sandbox, Docker e Windows Terminal Server).
- La vulnerabilità esiste nei computer Windows 10, Windows 11 e Windows Server 2022 senza patch.
- Le vulnerabilità sono state segnalate in modo responsabile a Microsoft e descritte nella Patch Tuesday di ottobre 2022.
- Forniamo una POC (Proof of Concept) delle vulnerabilità nel nostro archivio delle ricerche.
Introduzione
L'anno scorso, l'Akamai Security Intelligence Group ha condotto una ricerca approfondita sull'MS-RPC. Per un protocollo che svolge così tante funzioni, l'MS-RPC è di gran lunga poco studiato e può avere effetti nel mondo reale. Uno di questi effetti è che le vulnerabilità in un' interfaccia RPC diventano esposte. In questo blog ci concentriamo su questo argomento: le vulnerabilità all'interno dell'interfaccia RPC di Local Session Manager (LSM).
LSM è un servizio che fa parte di Session Manager Subsystem ed è responsabile della gestione delle sessioni locali del terminal server su un computer Windows. Il servizio comunica con altri componenti di Windows correlati, come Winlogon e Csrss,
è implementato in lsm.dll e contiene logica client e server. Il servizio LSM espone diverse interfacce RPC, con un'interessante interfaccia relativa alla gestione delle sessioni dei container che vengono eseguiti all'interno di un computer virtuale Hyper-V. Le vulnerabilità si trovano all'interno di questa interfaccia.
Che cos'è l'interfaccia?
Alla nuova interfaccia RPC viene assegnato l'UUID c938b419-5092-4385-8360-7cdc9625976a. Questa interfaccia espone esattamente due funzioni: ContainerCom_AskForSession e ContainerCom_SessionLoggedOff. L'interfaccia è inoltre registrata con un callback di sicurezza che restituisce sempre RPC_S_OK, consentendo così l'accesso a tutti. Il server LSM registra un endpoint socket Hyper-V (hvsocket) accessibile solo ai container Hyper-V.
Figura 1: client che imposta la connessione RPC tramite hvsocket rispetto al server che imposta un endpoint hvsocket
Quando viene creata una sessione all'interno del container (ad esempio, a causa di una connessione RDP) il client LSM chiama prima RpcGetRequestForWinlogon all'interno dell'LSM del container. Questa funzione arbitra la creazione della sessione e, quando è in esecuzione all'interno di un container, chiede prima l'autorizzazione all'host. Ciò avviene invocando la chiamata RPC ContainerCom_AskForSession all'host utilizzando un hvsocket al genitore. L'interfaccia RPC limita il numero di sessioni ai container, tenendo traccia delle sessioni appena create.
In che modo il servizio LSM tiene traccia delle sessioni?
La risposta è semplice. È presente un oggetto globale chiamato ContainerSessionServer che contiene due variabili per tenere traccia delle sessioni:
- Il contatore delle sessioni totali create, che è limitato a uno, ossia è consentita solo una sessione in un dato momento.
- Una mappa tra il GUID di un container e il conteggio delle sessioni del relativo container, che è limitato a due per ogni container.
Ogni volta che un container richiede una sessione, ContainerSessionServer::AskForSession controlla innanzitutto se il contatore delle sessioni totali è inferiore a uno. In tal caso, incrementa il conteggio totale delle sessioni e incrementa anche il contatore delle sessioni del container nella mappa.
Quando viene chiamato ContainerSessionServer::OnSessionLoggedOff (all'uscita dal container o direttamente come chiamata RPC), la funzione decrementa sia il conteggio totale delle sessioni che il conteggio delle sessioni del container di uno.
Controllo delle funzioni RPC per individuare le vulnerabilità
Sebbene questa interfaccia sembri semplice e facile da implementare, abbiamo individuato quattro bug che siamo riusciti a concatenare in due vulnerabilità.
Catena n. 1 - DoS attraverso una sezione critica: CVE-2022-37998
Bug n. 1 - Impossibile uscire da una sezione critica
ContainerSessionServer::AskForSession utilizza una sezione critica per sincronizzare l'accesso all'oggetto globale ContainerSessionServer.
Figura 2: codice decompilato della vulnerabilità. La funzione esce senza rilasciare la sezione critica
Come osservato sopra, l'accesso alla sezione critica avviene alla riga 112. Successivamente, alle righe 114-116, c'è un controllo per verificare se il contatore di sessioni del container è al suo limite (due). In tal caso, l'LSM non terrà traccia di questa sessione ed esce immediatamente dalla funzione (riga 125). Sfortunatamente, il codice non esce dalla sezione critica a cui è stato eseguito l'accesso. Pertanto, ulteriori chiamate a questa interfaccia verranno bloccate in attesa del rilascio di questa sezione critica.
Ma, se ricordate, c'è un limite di uno per il contatore di sessioni, quindi come possiamo arrivare a un punto in cui il contatore di sessioni di un container sia due? Logicamente non è possibile. Ma ecco che arriva il secondo bug!
Bug n. 2 - Tracciamento errato del contatore
Quando una sessione al contenitore viene disconnessa, viene effettuata una chiamata RPC a ContainerSessionServer::OnSessionLoggedOff nell'host. Questa funzione prima riduce il contatore di sessioni totali chiamando DecreaseTotalSessionCount. Questa operazione viene eseguita indipendentemente dal fatto che il container venga monitorato. Se rileva che il container non viene tracciato, esce senza incrementare il contatore delle sessioni totali.
Questo può portare a un contatore di sessione totale con il valore di un numero negativo (perché è un numero intero con segno). Possiamo semplicemente inviare molte richieste OnSessionLoggedOff prima di inviare richieste AskForSession , quindi continua a decrementare il contatore totale della sessione a un numero negativo arbitrario.
Concatenamento del primo e del secondo bug
Possiamo utilizzare il bug n. 2 per diminuire alcune volte il contatore di sessioni totali fino a quando non è un numero negativo. Quindi, possiamo sfruttare il bug n. 1 inviando due richieste a AskForSession. La seconda volta che questa funzione viene chiamata, verificherà che il contatore di sessioni totale sia inferiore a 1 e, a causa del secondo bug, lo è. Quindi, vedrà che il contatore di sessione del container è 2 e tornerà senza uscire dalla sezione critica.
Fig. 3: una sintesi del processo di sfruttamento
L'attacco DoS dipende dal fatto che le nuove chiamate RPC in entrata vengano inviate allo stesso thread che ha creato il deadlock della sezione critica. Se il runtime RPC avesse inviato la nuova chiamata allo stesso thread, l'attacco DoS non si sarebbe verificato poiché EnterCriticalSection consente la proprietà nidificata, cioè, lo stesso thread può chiamare EnterCriticalSection due volte. Se la chiamata RPC fosse stata inviata a qualsiasi thread diverso da quello che contiene la sezione critica, allora aspetterebbe all'infinito.
Catena n. 2 - Attacco DoS tramite perdita di memoria: CVE-2022-37973
Bug n.3 - Perdita di memoria
ContainerSessionServer::AskForSession traccia anche gli eventi dei container come uscita/pausa/ripresa del container. Questa operazione viene eseguita chiamando HcsOpenComputeSystem con il GUID del container e quindi registrando un callback con HcsRegisterComputeSystemCallback.
Il callback registrato riceve un oggetto di contesto. Il contesto viene allocato all'interno di ContainerSessionServer::AskForSession. Sfortunatamente, in molti casi in cui si verifica un errore, la funzione esce senza liberare la memoria allocata per il contesto. Ciò comporta una perdita di memoria che un utente malintenzionato può attivare più volte. Dopo un numero sufficiente di chiamate, si verifica l'esaurimento della memoria per il processo LSM e il processo si arresta in modo anomalo.
Nei nostri test, l'invio di richieste RPC in un ciclo infinito ha prodotto circa 3 MB di allocazione al secondo. Nel nostro caso, dopo l'allocazione di 24 GB di memoria, il servizio LSM si è bloccato. Il tempo impiegato per esaurire 24 GB è di circa due ore. Il servizio non viene rigenerato automaticamente.
Bug n. 4 - Accesso remoto
Gli endpoint in MS-RPC sono multiplex. Se un server registra più interfacce e più endpoint, ciascuna interfaccia è accessibile tramite ciascun endpoint. Endpoint e interfacce non sono legati tra loro.
Questa interfaccia dovrebbe essere accessibile solo tramite hvsocket ai container. Nel nostro caso, il servizio LSM registra un endpoint named pipe denominato "\pipe\LSM_API_service", che è accessibile da remoto. A causa del multiplexing dell'endpoint, un utente malintenzionato remoto può connettersi all'endpoint named pipe e inviare una richiesta all'interfaccia del container. La correzione è semplice: il callback di sicurezza dovrebbe verificare quale endpoint ha utilizzato il client e, se non è hvsocket, negare l'accesso.
Concatenamento del terzo e del quarto bug
La funzionalità di tracciamento del container si basa sulla proprietà dell'identificatore del client. Ciò significa che per un hvsocket l'identificatore client sarà il GUID del container. Per una named pipe, questo sarà il nome del computer del client.
Per attivare il primo exploit, il client deve disporre di un identificatore client che sia un GUID effettivo di un container in esecuzione. Pertanto, un client remoto non sarebbe in grado di attivare tali bug a meno che non verifichi correttamente il GUID di un container in esecuzione e cambi il nome del computer: uno scenario improbabile.
Sfortunatamente, il terzo bug (perdita di memoria) alloca gli oggetti prima che venga effettuato qualsiasi controllo sul container richiesto. Ciò significa che un utente malintenzionato remoto (utilizzando il bug n. 4) può attivare in remoto la perdita di memoria. Chiamandolo più volte, un utente malintenzionato può causare l'esaurimento della memoria e arrestare il processo.
Impatto
Sebbene queste vulnerabilità siano classificate come vulnerabilità DoS (Denial-of-Service), hanno un impatto sulla sicurezza in quanto consentono a un utente malintenzionato di eludere le funzionalità di sicurezza.
Con il primo exploit (sezione critica), c'è un attacco DoS alla nuova interfaccia specifica. Questo problema impedirebbe la creazione di nuove istanze sandbox.
Con il secondo exploit, che può essere attivato sia da remoto che da un container, l'intero processo si arresta. Pertanto, tutte le dipendenze da LSM non funzionerebbero. Anche le funzionalità di sicurezza come Microsoft Defender Application Guard e Sandbox non funzionerebbero. Inoltre, RDP e Docker non funzionerebbero.
Figura 4: errore MDAG come mostrato in Microsoft Edge
Riepilogo
Queste vulnerabilità sono un ottimo esempio di come qualcosa che sembra semplice o insignificante possa avere impatti veramente negativi. Questa interfaccia può sembrare secondaria: ci ha presentato alcuni bug facili da attivare e con un impatto interessante.
La realtà della situazione è che gli autori di attacchi stanno comunemente utilizzando queste catene di attacco. Più qualcosa sembra insignificante, più facilmente si ignora, e questo è un ottimo terreno da sfruttare.
In concomitanza con il lavoro continuo che stiamo facendo in RPC, incoraggiamo altri ricercatori a cercare bug simili in altre interfacce RPC.
Se siete interessati ad argomenti di ricerca RPC come questo, date un'occhiata al nostro kit di strumenti RPC per ulteriori articoli e strumenti. Potete anche seguirci su Twitter per aggiornamenti in tempo reale su questa ricerca e altre ricerche che stiamo svolgendo in Akamai.