Avvelenamento della cache GitHub

Tutti i messaggi

Sai cosa succede sotto il cofano del tuo CI? Senza una comprensione approfondita, potresti essere vulnerabile agli attacchi innovativi alla catena di fornitura. Questo articolo descrive un simile attacco.

La memorizzazione nella cache viene utilizzata per accelerare i processi. Invece di creare o scaricare ripetutamente un pacchetto, conserva semplicemente e automaticamente gli artefatti per il riutilizzo. Tuttavia, le cache potrebbero essere avvelenate. Ad esempio, uno strumento dannoso utilizzato in un flusso di lavoro di test può avvelenarne la cache. Successivamente, un altro flusso di lavoro che utilizza la stessa cache potrebbe essere interessato. Se questo flusso di lavoro ha privilegi più elevati, questo è di fatto un metodo per orientare un attacco. In questo post riportiamo un attacco sperimentale su una pipeline CI GitHub, ma una vulnerabilità logica simile esiste in altri prodotti CI.

L'attacco si svolge nel modo seguente: 

  1. Un utente malintenzionato pubblica uno strumento dannoso o un'azione Github che viene rilevata da un flusso di lavoro ignaro in Github.
  2. Il flusso di lavoro è progettato con una cache
  3. Il payload dannoso modifica la cache per includere dati dannosi.
  4. Altri flussi di lavoro che richiedono questa cache da questo momento potrebbero essere interessati.

In risposta alla nostra divulgazione, GitHub ha affermato di non avere un piano per rafforzare la funzionalità della cache contro questo tipo di attacco. 

Proponiamo una mitigazione firmando crittograficamente il valore hash del contenuto della cache e verificando la firma prima dell'uso. Per saperne di più sull'attacco e sulle mitigazioni, continua a leggere.

Attacco di avvelenamento della cache

Cache di GitHub

spesso riutilizzano gli stessi output o le dipendenze scaricate da un'esecuzione all'altra (ad esempio, i pacchetti scaricati dagli strumenti di gestione dei pacchetti e delle dipendenze come Maven, Gradle, npm e Yarn. Questi pacchetti sono generalmente conservati in una cache locale delle dipendenze scaricate).

Per ottimizzare le esecuzioni CI, GitHub concede l'accesso a una risorsa cache che può essere utilizzata nella pipeline tramite un'API. Le voci nella cache sono una combinazione chiave-valore, dove le chiavi sono basate su stringhe e i valori sono file o directory che si desidera memorizzare nella cache.

Usando il azione/cache L'azione Git in qualsiasi punto dell'elemento della configurazione verrà eseguita in due passaggi: un passaggio avrà luogo durante il eseguire il processo quando viene chiamato e l'altro avrà luogo dopo flusso di lavoro (se l'azione di esecuzione ha restituito un errore di cache).

  • Esegui l'azione – viene utilizzato per cercare e recuperare la cache. La ricerca viene eseguita utilizzando la chiave della cache, con il risultato che può essere un successo nella cache (esito positivo, dati trovati nella cache) o un errore nella cache. Se trovati, i file e le directory vengono recuperati dalla cache per l'uso attivo. Se il risultato è cache-miss, i file e le directory desiderati vengono scaricati come se fosse la prima volta che vengono richiamati.
  • Pubblica l'azione del flusso di lavoro – utilizzato per salvare la cache. Se il risultato della chiamata alla cache nell'azione run restituisce un cache-miss, questa azione salverà lo stato corrente delle directory che vogliamo memorizzare nella cache con la chiave fornita. Questa azione avviene automaticamente e non è necessario chiamarla esplicitamente.

Autorizzazioni della cache GitHub

Forniscono restrizioni di accesso isolamento della cache e sicurezza creando un confine logico tra diversi rami (ad esempio: una cache creata per il ramo Caratteristica-A [con la base main] non sarebbe accessibile a una richiesta pull per il ramo Caratteristica-B [con la base principale]).

L'azione cache innanzitutto cerca una chiave negli hit della cache e ripristina le chiavi nel ramo contenente il file esecuzione del flusso di lavoro. Se non ci sono riscontri nel ramo corrente, l'azione cache cerca la chiave e ripristina le chiavi nel ramo padre e nei rami upstream.

L'accesso a una cache è limitato al ramo (corrente e principale), il che significa che l'accesso è fornito a tutti flussi di lavoro operanti in corre di detto ramo.

Un'altra nota importante è che GitHub non consente modifiche una volta inviate le voci: le voci della cache sono record di sola lettura.

Ambito CI di GitHub

GitHub ambiti consentono di specificare esattamente quale tipo di accesso è necessario per varie attività. L'ambito della CI di GitHub ha diversi ambiti, ciascuno con il proprio insieme di valori e funzionalità:

  • Macchina virtuale (VM) per ogni lavoro
  • Autorizzazioni di lavoro
  • Ambiti del flusso di lavoro
  • Viene eseguito il flusso di lavoro
  • rami Git
  • Token di identità del flusso di lavoro
  • e altri

L'ambito della cache di GitHub è stato definito in modo tale da interrompere alcune delle altre restrizioni sull'ambito (es: la cache GitHub può influenzare più flussi di lavoro).

L'attacco

Abbiamo utilizzato un esempio di CI che includeva due flussi di lavoro. Questo esempio mostra come un attacco può passare da un flusso di lavoro con permessi bassi a uno con permessi elevati.

  • Test unitario flusso di lavoro che esegue strumenti di test unitario e di copertura del codice. Presumiamo che uno degli strumenti sia dannoso o vulnerabile all'esecuzione di codice in modalità remota. Il flusso di lavoro deve utilizzare il file azione/cache Azione Git. Qualsiasi flusso di lavoro può accedere alla cache.
  • Rilasciare il flusso di lavoro crea e rilascia l'artefatto dell'applicazione. Questo flusso di lavoro utilizza una cache per ottimizzare utilizzando le dipendenze Golang.

I test unitario il flusso di lavoro utilizza un'azione dannosa che aggiunge una voce della cache con contenuto dannoso modificando una libreria di registrazione Golang (go.uber.org/zap@v1) per aggiungere la stringa "Libreria BAD" alla descrizione dell'artefatto dell'applicazione.

Successivamente, la rilasciare il flusso di lavoro utilizza questa voce della cache avvelenata. Di conseguenza, il codice dannoso viene iniettato nel file binario e nell'immagine Golang creati. La cache rimane avvelenata finché la chiave di accesso non viene scartata (solitamente attivata dagli aggiornamenti delle dipendenze). La stessa cache avvelenata influenzerà chiunque altro flusso di lavoro, eseguire ile ramo figlio utilizzando la stessa chiave di cache.

Nel test che abbiamo effettuato, siamo riusciti a inserire la stringa 'BAD Library' nella descrizione dell'immagine:

Biblioteca pessima

Questo era nella versione 0.4.1. Successivamente, abbiamo aggiornato il tag e ricostruito l'immagine più volte e abbiamo osservato che nella descrizione rimaneva la dicitura "Libreria errata".

Divulgazione di GitHub

La risposta di Github alla nostra divulgazione è stata che l'azione Git,  azione/cache si comporta come previsto e non hanno intenzione di restringere l'ambito della cache.
Sebbene il team GitHub non consideri questo comportamento problematico, consigliamo ai professionisti DevSecOps di diffidare di questo vettore di attacco.

Fattori attenuanti

  1. Non utilizzare le cache nel rilascio o in flussi di lavoro importanti.
  2. Esegui i flussi di lavoro in sequenza, con il flusso di lavoro attendibile in esecuzione per primo per assicurarti che la cache venga creata in un flusso di lavoro attendibile.
  3. Vendi le tue dipendenze: il vendor in GoLang è un metodo per garantire che tutti i pacchetti di terze parti utilizzati nel progetto Go siano coerenti per tutti coloro che sviluppano per quell'applicazione. In questo modo, le dipendenze memorizzate nella cache rimarranno valide per tutti i rami del progetto. Altre lingue potrebbero non supportare questo metodo.
  4. Firma il valore della cache in modo crittografico e verifica la firma prima dell'uso.
    Scribe mitiga tali attacchi firmando in modo granulare qualsiasi oggetto o directory in un flusso di lavoro come una cache. Prima del rilascio, puoi utilizzare Scribe per verificare che per creare il rilascio sia stata utilizzata solo una cache generata da un flusso di lavoro attendibile.

Sommario 

In questo post, abbiamo delineato un attacco di avvelenamento della cache nei flussi di lavoro CI, che è nascosto agli occhi del team DevSecOps, e abbiamo discusso le mitigazioni.

Questo contenuto è offerto da Scribe Security, un fornitore leader di soluzioni di sicurezza end-to-end per la catena di fornitura di software, che offre sicurezza all'avanguardia per artefatti di codice e processi di sviluppo e distribuzione del codice attraverso le catene di fornitura di software. Per saperne di più.