Vi serve il cloud computing? Iniziate subito

Sfruttamento di una vulnerabilità spoofing critica in Windows CryptoAPI

Akamai Wave Blue

scritto da

Tomer Peled e Yoni Rozenshein

January 25, 2023

Tomer Peled

scritto da

Tomer Peled

Tomer Peled è Security Researcher in Akamai. Nell'ambito delle sue mansioni quotidiane, si occupa di condurre ricerche su vari argomenti, dalla vulnerabilità ai componenti interni dei sistemi operativi. Nel tempo libero, adora cucinare, praticare il Krav Maga e giocare al PC.

Onda blu di Akamai

scritto da

Yoni Rozenshein

Yoni Rozenshein è sviluppatore e ricercatore di basso livello in Akamai. Negli ultimi anni, si è occupato di driver (e, di conseguenza, del debugging delle schermate blu ed errori del kernel) per Guardicore (acquisita da Akamai). Adora i componenti interni del sistema operativo, la matematica e la crittografia e ne può parlare facilmente per ore.

I certificati svolgono un ruolo importante nella verifica dell'identità online, rendendo questa vulnerabilità redditizia per i criminali.
I certificati svolgono un ruolo importante nella verifica dell'identità online, rendendo questa vulnerabilità redditizia per i criminali.

Editoriale e contributi aggiuntivi di Tricia Howard

Analisi riassuntiva

  • L'Akamai Security Intelligence Group ha recentemente analizzato una vulnerabilità critica in Windows CryptoAPI che è stata segnalata dalla National Security Agency (NSA) e dal National Cyber ​​Security Center (NCSC) a Microsoft.

  • La vulnerabilità, a cui è stato assegnato un CVE-2022-34689, ha un punteggio CVSS di 7,5. È stata corretta nell'agosto 2022, ma è stata annunciata pubblicamente nella Patch Tuesday di ottobre 2022.

  • Secondo Microsoft, la vulnerabilità consente a un criminale di spacciarsi per un'entità legittima. 

  • La causa principale del bug è il presupposto che la chiave di indice della cache dei certificati, che è basata su MD5, sia priva di collisioni. Dal 2009, è noto che la la resistenza alle collisioni di MD5 sia danneggiata,

  • Il flusso di attacco è duplice. La prima fase richiede di ottenere un certificato legittimo, modificarlo e fornire la versione modificata alla vittima. La seconda fase prevede la creazione di un nuovo certificato il cui MD5 collide con il certificato legittimo modificato e l'utilizzo del nuovo certificato per falsificare l'identità del soggetto del certificato originale.

  • Abbiamo cercato applicazioni reali che utilizzano CryptoAPI in un modo vulnerabile a questo attacco di spoofing. Finora, abbiamo scoperto che le vecchie versioni di Chrome (v48 e versioni precedenti) e le applicazioni basate su Chromium possono essere sfruttate. Crediamo che ci siano obiettivi più vulnerabili in natura e la nostra ricerca è ancora in corso.

  • Abbiamo scoperto che è stata applicata una patch a meno dell'1% dei dispositivi visibili nei data center, lasciando i restanti senza protezione dallo sfruttamento di questa vulnerabilità.

  • In questo post del blog, forniamo una spiegazione dettagliata del potenziale flusso di attacco e delle relative conseguenze, nonché una PoC (Proof-of-Concept ) che dimostra l'attacco completo. Forniamo anche una OSQuery per rilevare le versioni vulnerabili della libreria CryptoAPI.

Background

Tre mesi fa, nella nostra analisi della Patch Tuesday di ottobre 2022, abbiamo condiviso una descrizione di base a una vulnerabilità critica di spoofing in Windows CryptoAPI: CVE-2022-34689. Secondo Microsoft, questa vulnerabilità consente a un utente malintenzionato di "falsificare la propria identità ed eseguire azioni come l'autenticazione o la firma del codice come certificato di destinazione".

CryptoAPI è l'API utilizzata in Windows per la gestione di tutto ciò che riguarda la crittografia. In particolare, gestisce i certificati, dalla lettura e l'analisi alla convalida rispetto alle autorità di certificazione (CA) verificate. I browser utilizzano anche CryptoAPI per la convalida del certificato TLS, un processo di controllo dell'icona con lucchetto che tutti devono eseguire.

Tuttavia, la verifica del certificato non è univoca per i browser e viene utilizzata anche da altri client TLS, come l'autenticazione web di PowerShell, curl, wget, gestori FTP, EDR e molte altre applicazioni. Inoltre, i certificati di firma del codice vengono verificati su eseguibili e librerie e i certificati di firma del driver vengono verificati durante il caricamento dei driver. Come si può immaginare, una vulnerabilità nel processo di verifica dei certificati è molto redditizia per i criminali, poiché consente loro di mascherare la propria identità e aggirare le protezioni di sicurezza critiche.

Questa non è la prima volta che la National Security Agency ha rivelato una vulnerabilità in CryptoAPI. Nel 2020, ha individuato e segnalato CurveBall (CVE-2020-0601). Lo sfruttamento di CurveBall o del CVE-2022-34689 causa lo spoofing dell'identità; ma mentre CurveBall influenzato molte applicazioni, il CVE-2022-34689 ha più prerequisiti e quindi ha un ambito più limitato di obiettivi vulnerabili.

Dettagli della vulnerabilità

Per analizzare la vulnerabilità, abbiamo prima cercato di individuare il codice corretto. Abbiamo utilizzato BinDiff, un popolare strumento di differenziazione binaria, per osservare le varie modifiche al codice di CryptoAPI. In crypt32.dll, è cambiata solo una funzione: CreateChainContextFromPathGraph, Questa funzione include un confronto di due certificati: uno che viene ricevuto come input e un altro che risiede nella cache dei certificati dell'applicazione ricevente (maggiori informazioni su questa cache più avanti).

Il controllo delle modifiche ha rivelato che sono stati aggiunti controlli memcmp  alla funzione in due posizioni (Figura 1).

Il controllo delle modifiche ha rivelato che sono stati aggiunti controlli memcmp alla funzione in due posizioni (Figura 1). Figura 1: Codice aggiunto a CreateChainContextFromPathGraph nella patch (evidenziato)

Prima della patch, la funzione determinava se un certificato ricevuto è già nella cache (e quindi verificato) solo in base alla rispettiva identificazione personale MD5. Dopo la patch, l'aggiunta memcmp richiede che i contenuti effettivi dei due certificati corrispondano completamente. 

A questo punto, abbiamo teorizzato che se un criminale fosse in grado di fornire un certificato dannoso il cui MD5 entra in collisione con un certificato che si trova già nella cache dei certificati della vittima, sarebbe in grado di aggirare il controllo vulnerabile e rendere attendibile il proprio certificato dannoso (Figura 2).

Flusso di attacco Figura 2: Flusso di attacco di alto livello

Cache dei certificati CryptoAPI

CryptoAPI può utilizzare una cache per i certificati finali ricevuti per migliorare le performance e l'efficienza. Questo meccanismo è disabilitato per impostazione predefinita. Per abilitarlo, lo sviluppatore dell'applicazione deve fornire determinati parametri a CertGetCertificateChain, la funzione API di Windows che alla fine comporta il codice vulnerabile (Figura 3).

CertGetCertificateChain, la funzione API di Windows che alla fine comporta il codice vulnerabile (Figura 3). Figura 3: La dichiarazione della funzione CertGetCertificateChain

CertGetCertificateChain riceve diversi parametri interessanti:

  • hChainEngine : un oggetto configurabile utilizzato per controllare la modalità di convalida dei certificati

  • pCertContext : il contesto del certificato di input, una struttura dati creata utilizzando il certificato di input dalla funzione WinAPI CertCreateCertificateContext

  • dwFlags : i flag che specificano ulteriori configurazioni

  • ppChainContext : l'oggetto di output che contiene (tra gli altri campi) lo stato di attendibilità; vale a dire, l'esito della verifica della catena

Per abilitare il meccanismo di memorizzazione nella cache per i certificati finali, lo sviluppatore deve impostare il flag CERT_CHAIN_CACHE_END_CERT in dwFlags, oppure creare un motore di catena personalizzato e impostare il flag CERT_CHAIN_CACHE_END_CERT nel relativo campo dwFlags .

Per comprendere come viene implementata e utilizzata la cache, diamo un'occhiata alla funzione FindIssuerObject che estrae il certificato dalla cache. In generale, la funzione si comporta nel modo seguente:

  1. Calcola l'indice del bucket del certificato di input nella cache in base ai quattro byte meno significativi della sua identificazione personale MD5.

  2. Se esiste nella cache, la funzione confronta l'intera identificazione personale MD5 del certificato memorizzato nella cache e il certificato di input.

  3. Se le identificazioni personali corrispondono (riscontro nella cache), il certificato di input viene considerato attendibile e restituito. Da questo momento in poi, l'applicazione utilizza gli attributi del certificato di input (come la chiave pubblica, l'emittente, ecc.) e non il certificato memorizzato nella cache,

  4. Se le identificazioni personali non corrispondono (mancato riscontro nella cache), passa al certificato successivo nel bucket, confronta la relativa identificazione personale MD5 e ripete l'operazione.

Microsoft si fida intrinsecamente della validità dei certificati memorizzati nella cache e non esegue ulteriori controlli di validità dopo che un certificato finale è stato trovato nella cache. Questo, di per sé, è un'ipotesi di lavoro ragionevole. Tuttavia, il codice presuppone ulteriormente che due certificati siano identici se le relative identificazioni personali MD5 corrispondono. Questo è un presupposto errato che può essere sfruttato e ha dato origine della patch.

Per supportare la nostra ipotesi, abbiamo scritto una piccola applicazione che utilizza CertGetCertificateChain ed eseguito il debug del flusso di verifica del certificato in crypt32.dll. Utilizzando WinDbg, abbiamo simulato uno scenario in cui l'identificazione personale MD5 del nostro certificato (autofirmato) corrisponde a un certificato legittimo già presente nella cache. Come mostrato nella Figura 4, il nostro certificato creato era attendibile.

Come mostrato nella Figura 4, il nostro certificato creato era attendibile. Figura 4: Registri che mostrano che il certificato memorizzato nella cache e il nostro certificato creato sono stati entrambi considerati attendibili da CryptoAPI

Aggirando solo un controllo, abbiamo fatto credere a Windows che il nostro certificato dannoso fosse legittimo.

Come è possibile sfruttare la vulnerabilità

La creazione di un certificato con un'identificazione personale MD5 che corrisponde esattamente a un dato valore MD5 viene definito attacco preimage e, dal punto di vista informatico, è irrealizzabile anche oggi. Tuttavia, è possibile generare in modo efficiente due certificati con due prefissi scelti che finiranno per avere le stesse identificazioni personale MD5; questo tipo di attacco viene chiamato collisione con prefisso scelto.

Scegliendo questo percorso, dovremo in qualche modo fornire due certificati all'applicazione vittima. Un certificato verrà firmato, verificato e memorizzato correttamente nella cache (lo chiameremo "certificato di destinazione modificato"). Verrà generato in modo da facilitare un attacco di collisione con prefisso scelto. Il secondo certificato (che chiameremo "certificato dannoso") conterrà l'identità falsificata. Entrerà in collisione con l'identificazione personale MD5 del primo certificato (figura 5).

Entrerà in collisione con l'identificazione personale MD5 del primo certificato (figura 5). Figura 5: L'identificazione personale MD5 del certificato dannoso entrerà in collisione con quella del certificato di destinazione modificato

Spoofing di certificati tramite collisioni MD5

Le collisioni con l'MD5 ci riportano indietro di circa 14 anni, al tempo in cui Beyoncé pubblicò "Single Ladies", Obama fu eletto presidente per la prima volta e le collisioni MD5 vennero utilizzate per la prima volta per falsificare i certificati SSL. C'è una grande differenza tra quel primo attacco e lo scenario di cui ci occupiamo oggi: lo scenario precedente ha attaccato le firme MD5, ma nell'attuale vulnerabilità abbiamo a che fare con le identificazioni personali MD5. Esaminiamo le differenze.

Secondo RFC 5280sezione 4.1, un certificato è una sequenza ASN.1 con due sezioni (Figura 6):

  • tbsCertificate (o certificato "da firmare"): questa è la parte che contiene tutti i dettagli relativi all'identità (oggetto, chiave pubblica, numero di serie, EKU, ecc.). Questa è la parte firmata.

  • signatureAlgorithm e signatureValue : questi campi comprendono la firma del TBS.

   Certificate  ::=  SEQUENCE  {
        tbsCertificate       TBSCertificate,
        signatureAlgorithm   AlgorithmIdentifier,
        signatureValue       BIT STRING  }

Figura 6: La sequenza ASN.1 che definisce i certificati

La firma Di un certificato è quindi una struttura integrata all'interno del certificato, che firma solo la parte TBS del certificato.. D'altra parte, l'identificazione personale di un certificato è un hash dell' intero certificato (inclusa la firma).

Quindi, se potessimo modificare qualsiasi parte del certificato al di fuori del TBS senza invalidare il certificato modificheremmo l'identificazione personale senza cambiare la firma, Se il parser analizza correttamente la firma e il TBS è invariato, il certificato sarà comunque considerato valido e firmato, anche se l'intera struttura del certificato è cambiata (Figura 7).

 Se il parser analizza correttamente la firma e il TBS è invariato, il certificato sarà comunque considerato valido e firmato, anche se l'intera struttura del certificato è cambiata (Figura 7). Figura 7: I dati aggiunti al di fuori del TBS non influiscono sulla validità del certificato

Collisioni con prefisso scelto MD5: una breve panoramica

Supponete di avere due stringhe arbitrarie, A e B, della stessa lunghezza. Quindi, due stringhe, C e D, possono essere calcolate efficientemente, in modo che

MD5(A || C) = MD5(B || D)

dove || indica la concatenazione delle stringhe.

Inoltre, non è solo il risultato finale dell'MD5 che sarà lo stesso, ma anche lo stato interno dell'MD5 dopo l'aggiunta di C o D. Pertanto, se prendete un qualsiasi suffisso E, avrete

MD5(A || C || E) = MD5(B || D || E)

(a condizione che lo stesso suffisso E sia aggiunto su entrambi i lati).

Creare spazio per i blocchi di collisione

In quanto criminali, dovremo generare un certificato che sembri valido, ma contenga anche spazio per i blocchi di collisione (le stringhe C e D descritte nella spiegazione sopra). Questo ci consentirà di creare il nostro certificato dannoso (con la stessa identificazione personale MD5), che utilizzeremo successivamente.

Secondo RFC 5280, sezione 4.1.1.2, la struttura di signatureAlgorithm tramite l'

  AlgorithmIdentifier  ::=  SEQUENCE  {
        algorithm               OBJECT IDENTIFIER,
        parameters              ANY DEFINED BY algorithm OPTIONAL  }

Il campo dei parametri per l'algoritmo RSA (in base a RFC 3279) DEVE essere il tipo ASN.1 NULL". In altre parole, RSA non utilizza i parametri della firma ma accetta NULL come valore. È possibile che CryptoAPI ignori questo campo per le firme RSA?

Per inserire byte segnaposto in questo campo (come preparazione per i blocchi di collisione), abbiamo provato a cambiare il suo tipo ASN.1 da NULL a BIT STRING. Testandolo rispetto a CryptoAPI e OpenSSL, funziona : il certificato è ancora considerato valido. La firma è invariata e ininterrotta perché non abbiamo modificato il TBS. (Ovviamente, l'identificazione personale MD5 cambia).

Collisioni dell'identificazione personale MD5 del certificato

Ora possiamo collegare le cose e fornire una ricetta per manipolare un certificato esistente già firmato in modo che entri in collisione con l'identificazione personale MD5 di un certificato dannoso.

  1. Basta prendere un certificato finale firmato RSA legittimo, come il certificato TLS di un sito web (il nostro "certificato mirato").

  2. Modificare eventuali campi interessanti (oggetto, estensioni, EKU, chiave pubblica, ecc.) nella parte TBS del certificato per creare il certificato dannoso. Nota: non tocchiamo la firma, quindi il certificato dannoso è firmato in modo errato. La modifica della chiave pubblica è importante in questo caso poiché consente al criminale di firmare il certificato come dannoso.

  3. Modificare il campo dei parametri del campo signatureAlgorithm di entrambi i certificati, in modo che ci sia spazio sufficiente per inserire i blocchi di collisione MD5 (C e D nella spiegazione sopra) che iniziano nello stesso offset di entrambi i certificati.

  4. Troncare entrambi i certificati nella posizione in cui devono essere posizionati i blocchi di collisione MD5.

  5. Eseguire un calcolo della collisione del prefisso scelto MD5 e copiare il risultato nei certificati.

  6. Concatenare il valore della firma del certificato legittimo (suffisso E nella spiegazione sopra) a entrambi i certificati incompleti.

Un esempio reale

Avendo compreso le collisioni MD5, ora possiamo tentare di sfruttare questo CVE con un obiettivo reale. Tra le numerose applicazioni che abbiamo controllato, siamo riusciti a trovare un obiettivo vulnerabile: Chrome v48. (Questa applicazione è vulnerabile semplicemente perché passa il flag CERT_CHAIN_CACHE_END_CERT a CertGetCertificateChain. Anche altre applicazioni basate su Chromium di quel periodo sono vulnerabili a questo CVE.

Per poter sfruttare questa vulnerabilità, abbiamo prima dovuto creare due certificati con la stessa identificazione personale MD5, operazione che abbiamo eseguito utilizzando HashClash (figura 8).

Per poter sfruttare questa vulnerabilità, abbiamo prima dovuto creare due certificati con la stessa identificazione personale MD5, operazione che abbiamo eseguito utilizzando HashClash (Figura 8). Figura 8: Generazione di due certificati con la stessa identificazione personale MD5 utilizzando la collisione con prefisso scelto

Abbiamo quindi dovuto trovare un modo per inserire il nostro certificato mirato modificato nella cache di Chrome. Questo è stato complicato da fare poiché è impossibile fornire un certificato senza conoscerne la chiave privata.

In TLS 1.2, ci sono due fasi di verifica rilevanti:

  1. Il messaggio Server Key Exchange : questo messaggio può essere creato solo da qualcuno che conosce la chiave privata del certificato, poiché è firmato dal certificato

  2. Il messaggio Server Handshake Finished : questo messaggio include una verifica antimanomissione di tutti i precedenti messaggi di handshake

(TLS 1.3 è diverso e non ci siamo concentrati su di esso).

Ricordate, nella prima fase dell'attacco vogliamo inserire il certificato modificato nella cache del certificato finale di Chrome.

Utilizzando uno script Python come proxy, eseguiamo un attacco MITM (Machine-In-The-Middle):

  1. il nostro server MITM dannoso dialoga con il server reale e riflette i primi messaggi dell'handshake TLS alla vittima.

  2. Nel messaggio del certificato del server, il nostro server MITM dannoso modifica il messaggio del server reale e sostituisce il certificato di destinazione reale con il certificato modificato.

  3. Il messaggio Server Key Exchange può essere riflesso senza modifiche.

  4. Il nostro server dannoso non può semplicemente inoltrare il messaggio Server Handshake Finished, perché l'handshake è stato effettivamente manomesso. Pertanto, terminiamo la connessione.

Per verificare il messaggio Server Key Exchange, Chrome deve caricare il certificato modificato con CryptoAPI e, quindi, verrebbe inserito nella cache. Chrome non considera la connessione interrotta come un problema di sicurezza TLS: potrebbe trattarsi solo di un problema di rete casuale. Chrome tenta di riconnettersi e questa volta, invece di riflettere i messaggi dal sito web reale, il server dannoso fornirà un sito web con il certificato dannoso. Chrome salterà l'intero processo di verifica perché ritiene che il certificato sia già nella cache. Il risultato sarà una visita diretta a un sito web Microsoft apparentemente legittimo (Figure 9 e 10). Il flusso di sfruttamento completo può essere visualizzato nel nostro video.

Visita diretta a un sito web Microsoft apparentemente legittimo (Figure 9 e 10). Fig. 9: Flusso di attacco completo su Chrome v48
Certificato dannoso Fig. 10: Il Chrome vulnerabile considera attendibile il nostro certificato dannoso

Rilevamento

Forniamo una OSQuery per rilevare le versioni vulnerabili di crypt32.dll, la libreria vulnerabile (Figura 11). I clienti di Akamai Guardicore Segmentation possono utilizzare la funzionalità Insight combinata a a una query per cercare le risorse vulnerabili..

Tenete presente che affinché una risorsa sia vulnerabile deve avere una versione senza patch di crypt32.dll ed eseguire un'applicazione vulnerabile. (Fino ad oggi, abbiamo riscontrato che solo Chrome v48 è vulnerabile).

  WITH product_version AS (
  WITH os_minor AS (
    WITH os_major AS (
      SELECT substr(product_version, 0, instr(product_version, ".")) as os_major, substr(product_version, instr(product_version, ".")+1) as no_os_major_substr
      FROM file
      WHERE path = "c:\windows\system32\crypt32.dll"
    )
    SELECT substr(no_os_major_substr, instr(no_os_major_substr, ".")+1) as no_os_minor_substr, substr(no_os_major_substr, 0, instr(no_os_major_substr, ".")) as os_minor, os_major
    FROM os_major
  )
  SELECT
    CAST(substr(no_os_minor_substr, instr(no_os_minor_substr, ".")+1) AS INTEGER) AS product_minor,
    CAST(substr(no_os_minor_substr, 0, instr(no_os_minor_substr, ".")) AS INTEGER) AS product_major,
    CAST(os_minor AS INTEGER) AS os_minor,
    CAST(os_major AS INTEGER) AS os_major
  FROM os_minor
)
SELECT
  CASE 
    WHEN os_major = 6 AND os_minor = 3 THEN "not supported"
    WHEN (
        (product_major = 20348 AND product_minor >= 887)
        OR
        (product_major = 17763 AND product_minor >= 3287)
        OR
        (product_major = 14393 AND product_minor >= 5291)
        OR
        (product_major >= 19041 AND product_minor >= 1889)
    )
    THEN
      "patched"
    ELSE
      "not patched"
  END is_patched
FROM product_version

Conclusione

I certificati svolgono un ruolo importante nella verifica dell'identità online, rendendo questa vulnerabilità redditizia per i criminali. Ma sebbene fosse contrassegnata come critica, alla vulnerabilità è stato assegnato solo un punteggio CVSS di 7,5. Riteniamo che ciò sia dovuto all'ambito limitato delle applicazioni vulnerabili e dei componenti di Windows in cui sono soddisfatti i prerequisiti di vulnerabilità.

Detto questo, c'è ancora molto codice che utilizza questa API e potrebbe essere esposto a questa vulnerabilità, garantendo una patch anche per le versioni ritirate di Windows, come Windows 7.

Vi consigliamo di applicare ai vostri server ed endpoint Windows l'ultima patch di sicurezza rilasciata da Microsoft. Per gli sviluppatori, un'altra opzione per mitigare questa vulnerabilità consiste nell'utilizzare altre WinAPI per ricontrollare la validità di un certificato prima di utilizzarlo, ad esempio CertVerifyCertificateChainPolicy. Tenete presente che le applicazioni che non utilizzano la memorizzazione nella cache del certificato finale non sono vulnerabili.

Il nostro codice PoC è disponibile nel nostro archivio GitHub. Potete inoltre tenervi aggiornati su tutte le pubblicazioni dell'Akamai Security Intelligence Group tramite il nostro account Twitter.



Akamai Wave Blue

scritto da

Tomer Peled e Yoni Rozenshein

January 25, 2023

Tomer Peled

scritto da

Tomer Peled

Tomer Peled è Security Researcher in Akamai. Nell'ambito delle sue mansioni quotidiane, si occupa di condurre ricerche su vari argomenti, dalla vulnerabilità ai componenti interni dei sistemi operativi. Nel tempo libero, adora cucinare, praticare il Krav Maga e giocare al PC.

Onda blu di Akamai

scritto da

Yoni Rozenshein

Yoni Rozenshein è sviluppatore e ricercatore di basso livello in Akamai. Negli ultimi anni, si è occupato di driver (e, di conseguenza, del debugging delle schermate blu ed errori del kernel) per Guardicore (acquisita da Akamai). Adora i componenti interni del sistema operativo, la matematica e la crittografia e ne può parlare facilmente per ore.