Comprendete il rischio delle policy. Ponete domande sulle policy in linguaggio naturale. Richiedi una demo →

Published:

Tecniche avanzate per difendere AWS ExternalID e l'accesso cross-account AssumeRole

by FireMon

Il mese scorso Kesten Broughton di Praetorian Security ha pubblicato un'ottima ricerca sui prodotti di sicurezza cloud di terze parti che utilizzano la tecnica di connessione cross-account preferita da Amazon: AWS IAM Assume Role Vulnerabilities Found in Many Top Vendors. Il paragrafo di apertura offre una solida panoramica della ricerca:

In questo primo articolo della nostra serie sul cross-account-trust presenteremo i risultati relativi a 90 vendor, che mostrano come il 37% non avesse implementato correttamente l'ExternalId per proteggersi dagli attacchi confused-deputy. Un ulteriore 15% dei vendor aveva implementato correttamente l'integrazione dell'account AWS nell'interfaccia utente, ma il parametro ExternalId non veniva validato in modo adeguato sul backend, rendendo vulnerabili anche quei siti. Concluderemo discutendo le nuove superfici di attacco esposte dal trust AWS cross-account-assume-role. La nostra conclusione è che vendor e clienti dovrebbero valutare criticamente se il role trust sia il meccanismo di fiducia migliore per la loro soluzione SaaS multi-tenant.

La mia prima reazione leggendo la ricerca è stata: «perché mai qualcuno dovrebbe prendere una decisione così sbagliata?». La realtà, però, è che l'intero concetto di «esperto di sicurezza cloud» è relativamente nuovo e, se è vero che AWS affronta in modo discreto il problema del confused deputy, non copre alcune implicazioni pratiche a cui non si pensa davvero finché il prodotto non entra in contatto con il cliente. Le connessioni cross-account che utilizzano AssumeRole hanno una soluzione ingegneristica lineare, ma senza un adeguato threat modeling è estremamente facile commettere gli errori documentati nella ricerca di Kesten.

Noi di DisruptOps abbiamo preso fin da subito alcune decisioni sensate, basate sul nostro threat modeling iniziale, che ci hanno tenuti al sicuro. Dalla ricerca abbiamo comunque tratto alcuni spunti e stiamo aggiungendo ulteriori misure di hardening. Inoltre abbiamo un progetto skunkworks che eliminerà gran parte del problema, continuando a consentire l'automazione su larga scala senza richiedere accesso diretto in scrittura agli ambienti dei clienti.

Ho però un punto di disaccordo importante con l'articolo. Preferisco in qualsiasi momento le credenziali a rotazione automatica rispetto alla raccomandazione di usare credenziali statiche e vaulting. Dobbiamo ancora ricorrervi per altri provider cloud e cerchiamo costantemente modi per NON avere credenziali statiche IN NESSUN punto del nostro ambiente.

Il problema

Oggi una gamma di tipologie di applicazioni diverse deve connettersi direttamente alle API cloud. Può trattarsi di qualcosa di semplice come l'accesso a un bucket S3 o di complesso come una piattaforma di Cloud Detection and Response completamente automatizzata (giusto per fare un esempio a caso). Queste richieste necessitano di credenziali di qualche tipo, statiche (come nome utente e password, oppure una access key e una secret key IAM) o dinamiche. Le credenziali dinamiche includono un token o un altro attributo effimero a tempo limitato.

Le credenziali applicative statiche che consentono l'accesso diretto al management plane del cloud sono... una pessima idea. Per gli utenti possiamo risolvere con l'MFA, ma questo non aiuta con le applicazioni automatizzate come la nostra piattaforma di Cloud Detection and Response non poi così teorica. Tempo fa Amazon Web Services ha affrontato il problema con il concetto di IAM Role. In AWS un ruolo è essenzialmente un contenitore di autorizzazioni con due policy: cosa può fare il contenitore e chi (o cosa) può assumere il ruolo. I ruoli sono poi basati su sessioni: quando un'entità autorizzata assume il ruolo ottiene un set di credenziali (access key, secret key e session token) utilizzabili per la durata della sessione (da 1 a 24 ore).

In AWS i ruoli possono essere assunti tramite connessione SAML esterna (per gli utenti), connessioni interne «trusted» (da altri account AWS) o servizi AWS (come un'istanza EC2 o una funzione Lambda). È quindi possibile fare cose interessanti come eseguire codice in un'istanza senza che questa memorizzi mai credenziali statiche. Ciò elimina davvero molti grattacapi.

Consentire connessioni da un account che non si controlla è un po' diverso, soprattutto se quell'account è una piattaforma che serve più clienti. Piattaforme di questo tipo (va bene, noi) devono accedere a centinaia o migliaia di altri account AWS. Immaginate se un attaccante potesse ingannare la piattaforma inducendola a operare sull'account sbagliato, per esempio eseguendo una valutazione della configurazione su un account che l'utente corrente non possiede. Questa è la versione breve del problema del confused deputy: il deputy gode della fiducia di più account e l'utente lo inganna per farsi dare accesso a un account che non dovrebbe mai toccare.

L'exploit più concreto si verifica quando la piattaforma permette a un utente di inserire l'ID di un account che non controlla, aggiungerlo al proprio profilo e quindi abusare della fiducia concessa alla piattaforma.

AWS include un meccanismo di difesa contro tutto ciò, chiamato AWS ExternalID. Si tratta di un segreto condiviso arbitrario che il provider della piattaforma e il cliente si scambiano fuori banda. Questo ID è un attributo trasmesso durante ogni richiesta di assunzione del ruolo nell'account del cliente, e la role trust policy di quell'account prevede una condizione che verifica la correttezza del segreto condiviso. Se implementato correttamente, questo significa che nessuno può semplicemente aggiungere al deputy un account che non controlla, poiché l'attaccante non può stabilire né conoscere il segreto condiviso nell'account del cliente.

A meno che...

Si capisce dove porta tutto questo. Praetorian ha individuato un gran numero di provider che utilizzano AWS ExternalId predefiniti oppure consentono ai clienti di impostare un valore ExternalId non univoco per l'intero account. Ha inoltre rilevato prodotti che non verificavano nemmeno che il cliente imponesse l'External ID nella propria role trust policy. Praetorian ha trovato piattaforme attivamente sfruttabili, che consentivano attacchi confused deputy a causa di una gestione carente della sicurezza dei ruoli cross-account.

Hardening delle connessioni cross-account

Tutto questo faceva parte del nostro threat modeling in DisruptOps, quindi siamo in una buona posizione, anche se abbiamo raccolto un paio di idee in più per migliorare ulteriormente. Esaminiamo ciascun problema individuato da Praetorian e vediamo le migliori opzioni di hardening della sicurezza:

  • L'AWS ExternalID usa le impostazioni predefinite: Noi utilizziamo un ExternalID casuale.
  • L'AWS ExternalID è condiviso tra più account cliente:Noi utilizziamo un ExternalID casuale per ogni account, non per ogni cliente.
  • L'AWS ExternalID è enumerabile o indovinabile: Noi utilizziamo AWS ExternalID completamente casuali e lunghi. Non sono preoccupato per la scelta del nostro PRNG grazie al rate limiting delle API (per voi appassionati di crittografia).
  • I clienti possono impostare il proprio ExternalID e possono ripeterlo o usarne di deboli: Noi non consentiamo ai clienti di impostare il proprio ExternalID, anche se questa richiesta ci è stata rivolta più volte. È qui che, a mio avviso, alcuni altri provider si sono messi nei guai. I clienti desiderano questa funzionalità per poter automatizzare autonomamente il provisioning del prodotto; ma per farlo in sicurezza dovremmo verificare che l'ExternalID soddisfi i requisiti di lunghezza e casualità. Con la giusta validazione, probabilmente si può fare in modo sicuro.
  • La piattaforma consente di effettuare il provisioning dello stesso account AWS più volte: Noi non lo permettiamo.
  • L'IAM Role non è limitato a una singola entità nell'account del provider, ma a qualsiasi ruolo dell'account: Non ne abbiamo parlato in questo articolo, ma è possibile concedere l'accesso al ruolo «worker» nell'account del cliente da qualsiasi ruolo dell'account, oppure concedere l'accesso a una singola risorsa o a un singolo ruolo. Noi limitiamo l'accesso ai ruoli specifici che utilizziamo per l'accesso cross-account.
  • Il nome dell'IAM Role è statico su tutti gli account cliente: Il rischio, in sostanza, è che il nome utente (il nome del ruolo) sia indovinabile. Non lo considero affatto un rischio, a meno che non siano presenti altri errori molto più fondamentali. Un esempio: se un attaccante conosce il nome del ruolo e ottiene accesso all'account del cliente, potrebbe aggiungersi alla role trust policy e sfruttare quei privilegi (insegno qualcosa di simile nei miei corsi di incident response). È un rischio potenziale, ma che valutiamo piuttosto basso. Se un attaccante dispone di quel livello di accesso, con ogni probabilità può impossessarsi di qualsiasi ruolo dell'account.
  • Il provider non verifica che l'ExternalID sia stato impostato come condizione di accesso: Noi forniamo ai clienti un CloudFormation Template per il provisioning che garantisce la corretta impostazione di questo parametro. Aggiungeremo il supporto per verificare che rimanga tale, soprattutto man mano che continueremo ad aggiungere altre opzioni di provisioning (non CloudFormation) per soddisfare le esigenze dei clienti.

Kesten sembra preferire il modello a credenziali statiche e l'uso di un buon vaulting lato provider per mettere in sicurezza la connessione. Personalmente non sono d'accordo: ritengo che il modello AWS sia più sicuro fin da subito, a patto di seguire le precauzioni di base.

Attualmente adottiamo due precauzioni aggiuntive rispetto alle raccomandazioni di Praetorian:

  • Mascheriamo l'ExternalID in CloudFormation: Impostiamo l'ExternalID come parametro nei nostri template CloudFormation con l'opzione NoEcho attivata. In questo modo viene mascherato nella console, negli strumenti da riga di comando e nelle API. Ciò riduce il rischio di esposizione verso chi dispone delle autorizzazioni per eseguire CloudFormation ma non delle autorizzazioni IAM.
  • Limitiamo l'accesso al template CloudFormation al solo account di destinazione: Ciò riduce il rischio che qualcuno possa intromettersi e sottrarre l'ExternalID durante il processo di provisioning.

Anche se l'ExternalID venisse esposto, non dovrebbe avere importanza: la piattaforma deve garantire che un account di destinazione venga registrato una sola volta e solo con un ExternalID casuale. Anche conoscendo il Role Name e l'ExternalID, un attaccante non potrebbe farne nulla, poiché nella piattaforma non esiste alcun punto in cui inserire tali informazioni e AWS stessa impone che la connessione cross-account provenga dall'account attendibile. E questo non è qualcosa che si possa falsificare.

Speriamo che vedere come gestiamo questo aspetto vi suggerisca qualche idea su come procedere con i vostri strumenti. Consiglio i requisiti di registrazione dell'account e di ExternalID casuale anche se utilizzate AWS Organizations, poiché aggiungere una condizione che limiti l'accesso alla sola vostra organizzazione può comunque esporvi ad attacchi da un account a minore sicurezza verso un account a maggiore sicurezza.

E restate sintonizzati per quel nuovo progetto skunkworks! Dovremmo poterne parlare tra qualche mese ed è una vera svolta per questo tipo di problemi.

Tecniche avanzate per difendere AWS ExternalID | FireMon