Cold Hard Cache - Elusione della sicurezza dell'interfaccia RPC con l'abuso della cache
Analisi riassuntiva
I ricercatori di Akamai hanno individuato due importanti vulnerabilità nei servizi RPC di Microsoft Windows a cui hanno assegnato i codici CVE-2022-38034 e CVE-2022-38045 con punteggi base, rispettivamente, di 4,3 e 8,8 .
Le vulnerabilità sfruttano un difetto di progettazione che consente di aggirare i callback di sicurezza MS-RPC tramite la memorizzazione nella cache.
Abbiamo verificato che la vulnerabilità esiste nei computer Windows 10 e Windows 11 senza patch.
Le vulnerabilità sono state responsabilmente segnalate a Microsoft e descritte nella Patch Tuesday di ottobre.
Il processo di rilevamento delle vulnerabilità è assistito da uno strumento di automazione e da una metodologia sviluppati dai ricercatori Akamai.
Forniamo una POC (Proof of Concept) delle vulnerabilità e degli strumenti utilizzati per la nostra ricerca nell' archivio dei kit di strumenti RPC.
Introduzione
L'MS-RPC è uno dei capisaldi sistema operativo Windows. Rilasciato negli anni '90, da allora è stato integrato nella maggior parte dei componenti del sistema. La gestione del servizio? RPC. Lsass? RPC. COM? RPC. Anche alcune operazioni di dominio sul controller di dominio utilizzano l'RPC. Considerando la popolarità ormai diffusa dell'MS-RPC, ci si aspetterebbe che sia stato attentamente esaminato, documentato e studiato.
In effetti, non proprio. Sebbene la documentazione di Microsoft sull'utilizzo dell'RPC sia abbastanza buona, non è stato scritto molto di più sull'argomento e ancor meno è stato scritto dai ricercatori che esaminano l'RPC, in particolare sulla sua sicurezza. Ciò potrebbe può essere probabilmente dovuto all'elevata complessità dell'RPC (non solo l'MS-RPC, anche se è stato sicuramente aggiunto alla combinazione da Microsoft) che rende la ricerca e la comprensione un compito difficile.
Ma noi siamo sempre pronti per la sfida, quindi abbiamo deciso di esaminare in modo approfondito l'MS-RPC. Non solo perché è un argomento di ricerca interessante, ma anche per le sue implicazioni in termini di sicurezza: anche oggi, le tecniche di attacco comuni si basano sull'RPC (T1021.003 avviene tramite l' MS-COM, T1053.005 tramite l' MS-TSCH, T1543.003 tramite l' MS-SCMR, per citarne alcuni). Nell'MS-RPC sono integrati meccanismi di sicurezza, ma cosa succede se sono presenti vulnerabilità che possono consentire di aggirarli o di abusarne, oppure consentire l'abuso di un servizio RPC esposto per avere un impatto indesiderato sui computer?
In effetti, siamo riusciti a trovare un modo per aggirare un meccanismo di sicurezza tramite la memorizzazione nella cache. Attraverso di esso, abbiamo individuato alcuni servizi di cui potremmo abusare per l'escalation dei privilegi su server remoti, senza molte condizioni richieste (che analizzeremo più avanti nel post) Al momento, possiamo condividere informazioni su due esempi realistici di potenziale sfruttamento, WksSvc e SrvSvc. Pubblicheremo aggiornamenti sulle altre vulnerabilità che abbiamo rilevato una volta terminato il relativo processo di divulgazione.
In questo post del blog, ci concentreremo sul meccanismo di callback di sicurezza dei server RPC, su come può essere aggirato dalla memorizzazione nella cache e su come abbiamo automatizzato la nostra ricerca per contrassegnare i servizi Windows come potenzialmente vulnerabili. I nostri strumenti di automazione, così come il relativo output non elaborato, sono disponibili anche nel nostro kit di strumenti RPC, condiviso nel nostro archivio GitHub. Il nostro archivio include anche link ad altri riferimenti utili e al lavoro svolto da altri ricercatori su cui abbiamo fatto affidamento.
Callback di sicurezza
Prima di esaminare le vulnerabilità stesse, è importante fare luce su uno dei meccanismi di sicurezza più fondamentali implementati da MS-RPC: i callback di sicurezza. I callback di sicurezza consentono agli sviluppatori di server RPC di limitare l'accesso a un'interfaccia RPC, nonché di applicare la propria logica per consentire l'accesso a utenti specifici, applicare vari tipi di autenticazione o trasporto oppure impedire l'accesso a specifici numeri di operazioni (opnum) (le funzioni esposte dal server sono rappresentate tramite opnum, ovvero numeri di operazione).
Questo callback viene attivato dal runtime RPC ogni volta che il client richiama una funzione esposta sul server.
Nella nostra ricerca, ci siamo concentrati sull'interazione remota client-server. Ne parliamo poiché le implementazioni del codice lato server di runtime RPC differiscono tra un endpoint ALPC e un endpoint remoto come una named pipe.
Memorizzazione nella cache
Il runtime RPC implementa la memorizzazione nella cache del risultato del callback di sicurezza per garantire un miglior livello di performance e utilizzo. Ciò significa sostanzialmente che il runtime proverà a utilizzare una voce memorizzata nella cache prima di chiamare ogni volta il callback di sicurezza. Esaminiamo l'implementazione.
Prima di richiamare il callback di sicurezza, RPC_INTERFACE::DoSyncSecurityCallback verifica se esiste una voce cache chiamando il comando OSF_SCALL::FindOrCreateCacheEntry,
che effettua le seguenti operazioni:
Recupera il contesto di sicurezza del client da SCALL (un oggetto che rappresenta una chiamata client).
Recupera il dizionario di memorizzazione nella cache dal contesto di sicurezza del client.
Utilizza il puntatore dell'interfaccia come chiave del dizionario. Il valore è la voce memorizzata nella cache.
Se non esiste alcuna voce della cache, ne crea una.
Una voce della cache presenta tre campi importanti: il numero di procedure nell'interfaccia, una bitmap e la generazione dell'interfaccia.
Durante il ciclo di vita di un server RPC, l'interfaccia può essere modificata, ad esempio se il server chiama RpcServerRegisterIf3 su un'interfaccia esistente, questo a sua volta chiama il comando RPC_INTERFACE::UpdateRpcInterfaceInformation, che aggiorna l'interfaccia e incrementa la generazione dell'interfaccia. In questo modo comunica alla cache che deve essere "reimpostata" poiché le voci della cache potrebbero provenire dall'interfaccia precedente.
Il meccanismo di memorizzazione nella cache può avvenire in due modalità: tramite l'interfaccia (che è il comportamento predefinito) e tramite una chiamata.
Memorizzazione nella cache basata sull'interfaccia
In questa modalità, la memorizzazione nella cache funziona in base all'interfaccia. Ciò significa che dal punto di vista della memorizzazione nella cache, non c'è differenza tra due chiamate a due funzioni diverse purché si trovino sulla stessa interfaccia.
Per sapere se la voce della cache può essere utilizzata invece di chiamare il callback di sicurezza, il runtime RPC confronta la generazione dell'interfaccia salvata nella voce della cache con la generazione dell'interfaccia effettiva. Poiché l'inizializzazione della voce memorizzata nella cache azzera la generazione dell'interfaccia, la prima volta che viene eseguito il confronto, le generazioni dell'interfaccia saranno diverse e quindi verrà chiamato il callback di sicurezza. Se il callback è stato restituito correttamente, il runtime RPC aggiornerà la generazione dell'interfaccia della voce della cache (e quindi verrà "contrassegnata" come riuscita in modo da poter accedere all'interfaccia senza richiamare il callback di sicurezza). La prossima volta che il client chiama una funzione sulla stessa interfaccia, verrà utilizzata la voce della cache.
Memorizzazione nella cache basata su una chiamata
Questa modalità viene utilizzata quando l'interfaccia RPC è registrata con il flag RPC_IF_SEC_CACHE_PER_PROC. In questa modalità, la memorizzazione nella cache si basa su una bitmap che tiene traccia delle procedure a cui il callback di sicurezza ha consentito l'accesso. Pertanto, se il client ha invocato la funzione Foo e il callback di sicurezza è stato restituito correttamente, avremo una voce della cache per Foo. Se il client invoca Bar, verrà richiamato il callback di sicurezza.
Requisiti di memorizzazione nella cache
Quindi, cosa ci serve per il funzionamento della memorizzazione nella cache? Innanzitutto, dobbiamo chiarire un po' di terminologia. L'MS-RPC rappresenta una connessione logica tra un client e un server tramite un handle di associazione. Il client e il server possono manipolare i dati di associazione utilizzando funzioni designate.
È possibile autenticare un'associazione. Ciò si verifica quando il server registra le informazioni di autenticazione (chiamando il comando RpcServerRegisterAuthInfo), quindi il client imposta le informazioni di autenticazione sull'associazione. Ciò consente al server di recuperare informazioni sull'identità del client. L'output di questo processo di autenticazione è un oggetto del contesto di sicurezza creato per il client.
L'intero meccanismo di memorizzazione nella cache si basa su questo contesto di sicurezza. Ciò significa che, se l'associazione non è autenticata, non viene creato un contesto di sicurezza per il client e quindi la memorizzazione nella cache non è abilitata. Affinché la memorizzazione nella cache funzioni, sia il server che il client devono registrarsi e impostare le informazioni di autenticazione.
Ma cosa succede se il server non ha registrato le informazioni di autenticazione? È comunque possibile abilitare la memorizzazione nella cache? Presentazione del multiplexing.
Multiplexing
Fino alla versione 1703 di Windows 10, un servizio poteva condividere lo stesso processo svchost con altri servizi. Questo comportamento influisce sulla sicurezza dell'MS-RPC poiché alcuni degli oggetti di runtime RPC sono condivisi tra tutte le interfacce. Ad esempio, durante la registrazione di un endpoint (come la porta TCP 7777), è possibile utilizzare questo endpoint per accedere a tutte le interfacce in esecuzione nello stesso processo. Pertanto, ora è possibile accedere anche ad altri servizi che prevedono l'accesso in locale. Questo processo è anche descritto in questa pagina da Microsoft.
Sebbene il fatto che gli endpoint siano multiplex sia già in qualche modo noto e documentato, vorremmo descrivere un altro comportamento simile: il multiplexing SSPI. Durante la registrazione delle informazioni di autenticazione, il server deve specificare il servizio di autenticazione da utilizzare. Il servizio di autenticazione è un SSP (Security Support Provider) , ossia un pacchetto che elabora le informazioni di autenticazione ricevute dal client. Nella maggior parte dei casi, sarà NTLM SSP, Kerberos SSP o Microsoft Negotiate SSP, che sceglie la migliore opzione disponibile tra Kerberos e NTLM.
Il runtime RPC salva le informazioni di autenticazione a livello globale. Ciò significa che, se due server RPC condividono lo stesso processo e uno di essi registra le informazioni di autenticazione, anche l'altro server disporrà delle informazioni di autenticazione. Un client può ora autenticare l'associazione quando accede a ciascuno dei server. Dal punto di vista della sicurezza, ai server che non hanno registrato le informazioni di autenticazione, e che quindi potrebbero non aver previsto l'autenticazione dell'associazione dei client o che avvenisse la memorizzazione nella cache, è possibile applicare queste ultime.
CVE-2022-38045: srvsvc
Con le nostre nuove conoscenze sui callback di sicurezza RPC e sulla memorizzazione nella cache, abbiamo deciso di verificare se sia possibile effettivamente abusare del meccanismo nella realtà. Siamo tornati a srvsvc, in cui abbiamo già individuato una vulnerabilità che sfrutta un errore precedente.
Il servizio srvsvc espone l'interfaccia MS-SRVS . Il servizio Server (denominato anche LanmanServer) è un servizio Windows responsabile della gestione delle condivisioni SMB. Le condivisioni sono risorse (file, stampanti e strutture di directory) rese accessibili in rete da un server CIFS (Common Internet File System). In sostanza, le condivisioni di rete consentono agli utenti di utilizzare altri dispositivi sulla rete per eseguire varie attività quotidiane.
Quando abbiamo esaminato il callback di sicurezza del servizio srvsvc, abbiamo notato che la funzione potrebbe avere un'altra vulnerabilità, diversa da quella che avevamo già individuato. Diamo un'occhiata alla logica di callback di sicurezza:
Come osservato sopra, il callback di sicurezza di srvsvc ha la seguente logica:
Se un client remoto tenta di accedere a una funzione compresa tra 64 e 73 (incluso), negare l'accesso
Se un client remoto, che non è un account cluster, tenta di accedere a una funzione compresa tra 58 e 63 (incluso), negare l'accesso
Quindi, in sostanza, ai client remoti viene impedito di accedere a queste particolari funzioni dell'interfaccia. Questo controllo di intervallo suggerisce che le funzioni limitate sono sensibili e dovrebbero essere invocate solo dai processi (locali) previsti.
Sebbene questo controllo tenti di impedire l'accesso remoto a queste funzioni, un criminale che sferra attacchi da remoto può aggirare questo controllo abusando della memorizzazione nella cache. Innanzitutto, un criminale di questo tipo invoca una funzione che non si trova in questo intervallo, ossia una funzione disponibile in remoto. Poiché la funzione di callback di sicurezza restituisce RPC_S_OK, il runtime RPC memorizzerà nella cache il risultato con esito positivo. Dato che l'interfaccia non è registrata con il flag RPC_IF_SEC_CACHE_PER_PROC, la memorizzazione nella cache avverrà in base all'interfaccia. Di conseguenza, la prossima volta che il criminale chiama una qualsiasi funzione sulla stessa interfaccia, verrà utilizzata la voce della cache e verrà concesso l'accesso. Ciò significa che il criminale può ora invocare le funzioni locali a cui non dovrebbe avere accesso e il callback di sicurezza non verrà affatto chiamato.
Il servizio srvsvc non registra le informazioni di autenticazione e, pertanto, in circostanze normali, i client non possono autenticare l'associazione e quindi la memorizzazione nella cache non è abilitata. Come risulta evidente, il servizio srvsvc condivide lo stesso processo svchost con altri servizi quando il server ha meno di 3,5 GB di RAM. I servizi "AD Harvest Sites and Subnets Service" e "Configurazione Desktop remoto" registrano le informazioni di autenticazione e, pertanto, il servizio srvsvc è ora vulnerabile agli attacchi alla cache.
In questo scenario specifico, un criminale può accedere a funzioni limitate con i numeri di operazione 58-74. Una delle cose che un criminale può fare con queste funzioni è forzare l'autenticazione del computer remoto.
Inizio di una "caccia al tesoro"
Dopo aver compreso che l'abuso del meccanismo di memorizzazione nella cache del callback di sicurezza può produrre vulnerabilità reali, abbiamo deciso di provare a individuare altre interfacce che potrebbero risultare vulnerabili a un attacco di memorizzazione nella cache. Tuttavia, individuare tutte le interfacce manualmente sarebbe stata un'attività lunga e difficile, quindi volevamo trovare un modo per automatizzarla.
Sono possibili due approcci da adottare per cercare le interfacce RPC: tramite i processi in esecuzione o tramite il file system.
Con i processi in esecuzione, possiamo esaminare i server RPC già caricati in memoria, sia su un server remoto, interrogando il mapper dell'endpoint remoto (con rpcmap o rpcdumpdi Impacket, ad esempio) o locale (utilizzando strumenti come RpcView o RpcEnum). Questo approccio, però, presenta un problema: mancheranno eventuali interfacce non attualmente caricate e non saremo in grado di esaminare le interfacce client, poiché non sono registrate.
In alternativa, possiamo recuperare il file system di Windows e cercare le interfacce RPC compilate al loro interno. Per ogni interfaccia, esaminiamo le informazioni di registrazione analizzando gli argomenti passati a RpcServerRegisterIf. Questo è un approccio simile a quanto fatto in RpcEnum, ma recuperiamo il file system invece della memoria.
Nella nostra ricerca abbiamo scelto il metodo del file system per includere le interfacce che non erano necessariamente caricate in memoria. Abbiamo scritto vari script e strumenti per automatizzare il processo, che sono disponibili nel nostro archivio di kit di strumenti RPC.
Per trovare le interfacce con la memorizzazione nella cache abilitata, non è necessario analizzare l'interfaccia RPC stessa: tutte le informazioni richieste possono essere estratte dalla chiamata di registrazione del server RPC. La funzione di registrazione accetta la struttura dell'interfaccia RPC, i flag di registrazione e il puntatore di callback di sicurezza. Tuttavia, l'analisi della struttura dell'interfaccia RPC può fornire informazioni utili, come le funzioni esposte dall'interfaccia o se è utilizzata da un server o client RPC. Sebbene siamo principalmente interessati ai server RPC (dove potrebbe esistere una vulnerabilità), i client RPC forniscono ottime informazioni sulla chiamata del server, a cui possiamo fare riferimento per lo sfruttamento.
La struttura dell'interfaccia del server RPC è documentata, quindi non dobbiamo ipotizzare i suoi campi. Inoltre, il campo delle dimensioni e la sintassi di trasferimento sono costanti (in realtà ci sono due possibili sintassi di trasferimento: DCE NDR e NDR64, ma ci siamo imbattuti solo in DCE NDR).
Trovare tutte le strutture dell'interfaccia RPC cercando queste due costanti (usando Yara o espressioni regolari) è semplice. Una volta trovate, possiamo utilizzare il campo delle informazioni interprete per vedere quale funzionalità implementa il server.
Ma mancano ancora informazioni sul callback di sicurezza dell'interfaccia (se esiste) e se è memorizzato nella cache. Pertanto, dobbiamo rivolgerci ai nostri fidati amici, i disassemblatori. Ogni disassemblatore che si rispetti deve disporre di una funzionalità xrif, quindi trovare tutte le chiamate delle funzioni di registrazione dell'interfaccia in un server RPC, è facile. A questo punto, dobbiamo solo analizzare gli argomenti della chiamata di funzione per estrarre l'indirizzo della struttura dell'interfaccia (in modo da poter eseguire un confronto con i nostri dati del server RPC recuperati), l'indirizzo di callback di sicurezza (se esiste) e i flag dell'interfaccia RPC.
Abbiamo pubblicato i nostri script di scraping, che fanno esattamente questo; sono disponibili nel nostro kit di strumenti RPC, insieme al relativo output di Windows Server 2012 e Server 2022.
CVE o niente di fatto
Tutte queste metodologie e teorie sono interessanti, ma producono davvero risultati?
La risposta è sì. Esistono più di 120 interfacce sia con callback di sicurezza che con memorizzazione nella cache, molti dei quali non documentati. Questo, di per sé, non deve destare preoccupazione, poiché la maggior parte delle volte il callback di sicurezza non sarà fortemente influenzato dalla memorizzazione nella cache. In genere, i controlli effettuati dal callback di sicurezza vengono eseguiti su valori che non sono memorizzabili nella cache, come la sequenza del protocollo di trasporto (ad es. TCP) o il livello di autenticazione. Qualsiasi modifica qui richiede comunque un nuovo contesto di sicurezza, poiché è necessario stabilire una nuova connessione, che reimposta la cache e annulla qualsiasi possibile aggiramento della memorizzazione nella cache.
Abbiamo individuato alcune vulnerabilità tramite questo approccio di ricerca. Al momento possiamo descriverne solo una, poiché le altre sono ancora in fase di divulgazione.
WksSvc
CVE-2022-38034 Punteggio CVSS: 4,3
WksSvc espone l'interfaccia MS-WKST . Il servizio è responsabile della gestione delle appartenenze ai domini, dei nomi dei computer e delle connessioni ai redirector di rete SMB, come i server di stampa SMB. Osservando il callback di sicurezza dell'interfaccia, possiamo vedere che alcune funzioni sono trattate in modo diverso dalle altre.
Anche per le funzioni il cui numero di operazione è compreso tra 8 e 11 viene verificato se vengono invocate da un client locale, il che significa che non è consentito chiamarle in remoto. Ma dal momento che abbiamo la memorizzazione nella cache, cosa accadrebbe se chiamassimo prima una funzione diversa, che è consentita in remoto, e poi chiamassimo una delle funzioni limitate?
Indovinato: saremmo in grado di chiamare le funzioni limitate localmente da remoto grazie alla memorizzazione nella cache del risultato della prima chiamata. La domanda adesso è: queste funzioni sono abbastanza importanti da garantire che siano limitate ai soli client locali?
Le funzioni esposte sono NetrUseAdd, NetrUseGetInfo, NetrUseDele NetrUseEnum. Se suonano familiari, è perché sono accessibili tramite netapi32.dll (vedere NetUseAdd, ad esempio).
Questo comportamento è positivo, poiché ci dà un indizio su cosa possiamo fare con questo attacco. Vale a dire, possiamo connettere il server remoto a una cartella condivisa di rete di nostra scelta e persino mapparlo a una lettera di unità logica di nostra scelta, in modo simile all'uso della rete. Una coincidenza? Probabilmente no.
Sono pertanto possibili due scenari di attacco:
1. Possiamo richiedere l'autenticazione sulla nostra cartella condivisa e quindi inoltrarla a un server diverso per un attacco di inoltro NTLM o archiviare i token e decifrare la password offline.
2. Possiamo mascherare un file server esistente (o fingere che sia uno nuovo) con file interessanti o utili. Dal momento che quei file sono sotto il nostro controllo, possiamo utilizzarli come arma nel modo che riteniamo più adatto, per consentirci, auspicabilmente, di infettare l'utente mirato.
Questi due scenari, e la possibilità di chiamare da remoto funzioni limitate in locale, sono stati sufficienti a Microsoft per classificare questa vulnerabilità come EoP, con un punteggio di 4,3.
Ma non finisce qui: abbiamo ancora alcune precisazioni da fare.
Il contesto di sicurezza
Il server RPC in WksSvc non esegue alcuna registrazione di autenticazione autonomamente. Se il servizio è in esecuzione autonomamente, non sarà possibile effettuare alcuna autenticazione lato client (si verificherà l'errore RPC_S_UNKNOWN_AUTHN_SERVICE). Pertanto, il servizio deve essere in esecuzione con altri servizi per abusare anche del multiplexing SSPI. Ciò limita le nostre versioni di Windows interessate a quelle precedenti alla versione 1703 di Windows 10o a versioni più recenti in esecuzione con meno di 3,5 GB di RAM.
Sessioni di accesso
Un altro problema, che è integrato nel modo in cui funzionano le cartelle mappate in rete, è che sono limitate alla sessione di accesso dell'utente che le crea. Poiché è necessario anzitutto eseguire l'accesso per ottenere l'associazione di sicurezza e la memorizzazione nella cache, significa che creeremo sempre una sessione di accesso diversa rispetto alla sessione (interattiva) esistente sul computer di destinazione. A tutti gli effetti, significa che la nostra vulnerabilità non ha effetto. La mappatura di rete che creiamo è sotto la nostra sessione di accesso di breve durata e non quella creata da un utente normale quando ha effettuato l'accesso al computer, quindi non sarà visibile.
Per superare questo ostacolo, abbiamo dovuto esaminare in modo più approfondito il codice di NetrUseAdd. Come risulta evidente, esistono alcuni flag a cui possiamo passare NetrUseAdd per richiedere di creare la mappatura nello spazio dei nomi globale, che interessa tutti gli utenti. Tali flag si trovano anche nel file di intestazione disponibile LMUse.h:
Con i flag, il nostro codice ora crea con successo una mappatura globale, che influenzerà la sessione interattiva, terminando il nostro tentativo di sfruttamento.
Riepilogo
L'MS-RPC è un protocollo ampio e complesso e gestisce anche alcune delle funzionalità principali di Windows. Sebbene disponga di funzionalità di sicurezza che gli sviluppatori possono utilizzare per proteggere i propri server RPC, è un argomento interessante per i ricercatori di sicurezza proprio perché contiene una vulnerabilità che può avere un impatto sulla sicurezza.
Nonostante ciò, non sono state condotte molte ricerche pubbliche sull'argomento. In questo post del blog, abbiamo affrontato un ampio meccanismo di sicurezza nell'MS-RPC (i callback di sicurezza) e abbiamo trovato una modalità di elusione nella memorizzazione nella cache dei risultati di callback. Abbiamo anche descritto in dettaglio la nostra metodologia di ricerca per trovare server RPC vulnerabili e abbiamo dimostrato alcuni dei nostri risultati con articoli sulle vulnerabilità.
Ci auguriamo che questo post e l' archivio di kit di strumenti RPCincluso possano risultare utili ad gli altri per la ricerca sui server RPC e i meccanismi di sicurezza.