Concourse Enumeration & Attacks
Enumerazione e Attacchi a Concourse
Ruoli Utente e Permessi
Concourse ha cinque ruoli:
Concourse Admin: Questo ruolo è dato solo ai proprietari del team principale (team concourse iniziale predefinito). Gli admin possono configurare altri team (ad esempio:
fly set-team
,fly destroy-team
...). I permessi di questo ruolo non possono essere influenzati da RBAC.owner: I proprietari del team possono modificare tutto all'interno del team.
member: I membri del team possono leggere e scrivere all'interno degli asset del team ma non possono modificare le impostazioni del team.
pipeline-operator: Gli operatori di pipeline possono eseguire operazioni sulla pipeline come avviare build e fissare risorse, ma non possono aggiornare le configurazioni della pipeline.
viewer: Gli spettatori del team hanno accesso "sola lettura" a un team e alle sue pipeline.
Inoltre, i permessi dei ruoli owner, member, pipeline-operator e viewer possono essere modificati configurando RBAC (configurando in modo più specifico le sue azioni). Leggi di più al riguardo in: https://concourse-ci.org/user-roles.html
Si noti che Concourse raggruppa le pipeline all'interno dei Team. Pertanto gli utenti appartenenti a un Team saranno in grado di gestire quelle pipeline e potrebbero esistere diversi Team. Un utente può appartenere a diversi Team e avere permessi diversi all'interno di ognuno di essi.
Variabili e Gestore delle Credenziali
Nei file di configurazione YAML è possibile configurare i valori utilizzando la sintassi ((_source-name_:_secret-path_._secret-field_))
.
Da documentazione: Il nome-sorgente è facoltativo, e se omesso, verrà utilizzato il gestore delle credenziali a livello di cluster, oppure il valore può essere fornito staticamente.
Il facoltativo _secret-field_ specifica un campo sul segreto recuperato da leggere. Se omesso, il gestore delle credenziali potrebbe scegliere di leggere un 'campo predefinito' dal credenziale recuperato se il campo esiste.
Inoltre, il secret-path e secret-field possono essere circondati da doppi apici "..."
se contengono caratteri speciali come .
e :
. Ad esempio, ((source:"my.secret"."field:1"))
imposterà il secret-path su my.secret
e il secret-field su field:1
.
Variabili Statiche
Le variabili statiche possono essere specificate nei passaggi delle attività:
Gestione delle credenziali
Ci sono diversi modi in cui un Gestore delle credenziali può essere specificato in un pipeline, leggi come in https://concourse-ci.org/creds.html. Inoltre, Concourse supporta diversi gestori delle credenziali:
Nota che se hai un tipo di accesso in scrittura a Concourse puoi creare lavori per esfiltrare quei segreti poiché Concourse deve essere in grado di accedervi.
Enumerazione di Concourse
Per enumerare un ambiente di Concourse è necessario prima raccogliere credenziali valide o trovare un token autenticato probabilmente in un file di configurazione .flyrc
.
Accesso e enumerazione dell'utente corrente
Per effettuare l'accesso è necessario conoscere il endpoint, il nome del team (di default è
main
) e un team a cui l'utente appartiene:fly --target example login --team-name my-team --concourse-url https://ci.example.com [--insecure] [--client-cert=./path --client-key=./path]
Ottenere target configurati:
fly targets
Verificare se la connessione al target configurato è ancora valida:
fly -t <target> status
Ottenere il ruolo dell'utente rispetto al target indicato:
fly -t <target> userinfo
Nota che il token API è salvato in $HOME/.flyrc
per impostazione predefinita, se saccheggi una macchina potresti trovarci le credenziali.
Team e Utenti
Ottenere un elenco dei Team
fly -t <target> teams
Ottenere ruoli all'interno del team
fly -t <target> get-team -n <team-name>
Ottenere un elenco degli utenti
fly -t <target> active-users
Pipeline
Elenca le pipeline:
fly -t <target> pipelines -a
Ottieni il file yaml della pipeline (potrebbero essere presenti informazioni sensibili nella definizione):
fly -t <target> get-pipeline -p <pipeline-name>
Ottieni tutte le variabili di configurazione della pipeline
for pipename in $(fly -t <target> pipelines | grep -Ev "^id" | awk '{print $2}'); do echo $pipename; fly -t <target> get-pipeline -p $pipename -j | grep -Eo '"vars":[^}]+'; done
Ottieni tutti i nomi segreti delle pipeline utilizzati (se puoi creare/modificare un lavoro o dirottare un container potresti esfiltrarli):
Contenitori e Workers
Elenco dei workers:
fly -t <target> workers
Elenco dei contenitori:
fly -t <target> containers
Elenco delle builds (per vedere cosa è in esecuzione):
fly -t <target> builds
Attacchi a Concourse
Forza bruta delle credenziali
admin:admin
test:test
Enumerazione di segreti e parametri
Nella sezione precedente abbiamo visto come è possibile ottenere tutti i nomi dei segreti e delle variabili utilizzati dal pipeline. Le variabili potrebbero contenere informazioni sensibili e il nome dei segreti sarà utile in seguito per cercare di rubarli.
Sessione all'interno di un contenitore in esecuzione o eseguito di recente
Se si dispone di sufficienti privilegi (ruolo di membro o superiore) sarà possibile elencare i pipeline e i ruoli e semplicemente ottenere una sessione all'interno del contenitore <pipeline>/<job>
utilizzando:
Con queste autorizzazioni potresti essere in grado di:
Rubare i segreti all'interno del container
Provare a fuggire al nodo
Enumerare/Abusare del endpoint dei metadati cloud (dalla pod e dal nodo, se possibile)
Creazione/Modifica del Pipeline
Se hai abbastanza privilegi (ruolo di membro o superiore) sarai in grado di creare/modificare nuovi pipeline. Controlla questo esempio:
Con la modifica/creazione di un nuovo pipeline sarai in grado di:
Rubare i segreti (tramite il loro echo o accedendo al container e eseguendo
env
)Scappare al nodo (dandoti abbastanza privilegi -
privileged: true
)Enumerare/Abusare del endpoint dei metadati cloud (dal pod e dal nodo)
Eliminare il pipeline creato
Eseguire un Compito Personalizzato
Questo è simile al metodo precedente ma invece di modificare/creare un intero nuovo pipeline puoi solo eseguire un compito personalizzato (che probabilmente sarà molto più furtivo):
Fuga al nodo da un compito privilegiato
Nelle sezioni precedenti abbiamo visto come eseguire un compito privilegiato con Concourse. Questo non darà al contenitore lo stesso accesso del flag privilegiato in un contenitore docker. Ad esempio, non vedrai il dispositivo del filesystem del nodo in /dev, quindi la fuga potrebbe essere più "complessa".
Nel seguente PoC useremo il release_agent per fuggire con alcune piccole modifiche:
Come avrai notato, si tratta solo di una fuga regolare dell'agente di rilascio modificando semplicemente il percorso del cmd nel nodo
Fuga verso il nodo da un contenitore Worker
Una fuga regolare dell'agente di rilascio con una piccola modifica è sufficiente per questo:
Fuga al nodo dal contenitore Web
Anche se il contenitore web ha alcune difese disabilitate, non viene eseguito come un comune contenitore privilegiato (ad esempio, non è possibile montare e le abilitazioni sono molto limitate, quindi tutti i modi facili per fuggire dal contenitore sono inutili).
Tuttavia, memorizza le credenziali locali in chiaro:
Puoi utilizzare quelle credenziali per effettuare il login sul server web e creare un container privilegiato e scappare al nodo.
Nell'ambiente puoi trovare anche informazioni per accedere all'istanza di postgresql che concourse utilizza (indirizzo, nome utente, password e database tra le altre informazioni):
Abuso del servizio Garden - Non un vero attacco
Queste sono solo alcune note interessanti sul servizio, ma poiché è in ascolto solo su localhost, queste note non avranno alcun impatto che non abbiamo già sfruttato in precedenza.
Per impostazione predefinita, ogni worker di Concourse eseguirà un servizio Garden sulla porta 7777. Questo servizio è utilizzato dal Web master per indicare al worker cosa deve eseguire (scaricare l'immagine e eseguire ogni attività). Questo sembra abbastanza vantaggioso per un attaccante, ma ci sono alcune buone protezioni:
È esposto solo localmente (127.0.0.1) e penso che quando il worker si autentica nuovamente sul Web con il servizio SSH speciale, viene creato un tunnel in modo che il server Web possa comunicare con ogni servizio Garden all'interno di ogni worker.
Il server Web controlla i contenitori in esecuzione ogni pochi secondi, e i contenitori inesperati vengono eliminati. Quindi, se si desidera eseguire un contenitore personalizzato, è necessario manomettere la comunicazione tra il server Web e il servizio Garden.
I worker di Concourse vengono eseguiti con privilegi elevati del contenitore:
Tuttavia, tecniche come il montaggio del dispositivo /dev del nodo o release_agent non funzioneranno (poiché il dispositivo reale con il filesystem del nodo non è accessibile, solo uno virtuale). Non possiamo accedere ai processi del nodo, quindi sfuggire al nodo senza sfruttare il kernel diventa complicato.
Nella sezione precedente abbiamo visto come sfuggire da un contenitore privilegiato, quindi se possiamo eseguire comandi in un contenitore privilegiato creato dal worker corrente, potremmo sfuggire al nodo.
Si noti che giocando con Concourse ho notato che quando viene generato un nuovo contenitore per eseguire qualcosa, i processi del contenitore sono accessibili dal contenitore worker, quindi è come se un contenitore creasse un nuovo contenitore al suo interno.
Entrare in un contenitore privilegiato in esecuzione
Creazione di un nuovo container privilegiato
È possibile creare facilmente un nuovo container (eseguire un UID casuale) ed eseguire qualcosa su di esso:
Tuttavia, il server web controlla ogni pochi secondi i contenitori in esecuzione e, se ne viene scoperto uno inaspettato, verrà eliminato. Poiché la comunicazione avviene tramite HTTP, potresti alterare la comunicazione per evitare l'eliminazione dei contenitori inaspettati:
Riferimenti
https://concourse-ci.org/vars.html
Last updated