Concourse Enumeration & Attacks
Concourse Enumeration & Attacks
User Roles & Permissions
Concourse vient avec cinq rôles :
Concourse Admin : Ce rôle est uniquement attribué aux propriétaires de l'équipe principale (équipe concourse initiale par défaut). Les admins peuvent configurer d'autres équipes (par exemple :
fly set-team
,fly destroy-team
...). Les permissions de ce rôle ne peuvent pas être affectées par RBAC.owner : Les propriétaires d'équipe peuvent modifier tout au sein de l'équipe.
member : Les membres de l'équipe peuvent lire et écrire au sein des ressources de l'équipe mais ne peuvent pas modifier les paramètres de l'équipe.
pipeline-operator : Les opérateurs de pipeline peuvent effectuer des opérations de pipeline telles que déclencher des builds et épingler des ressources, cependant ils ne peuvent pas mettre à jour les configurations de pipeline.
viewer : Les visualisateurs d'équipe ont un accès "lecture seule" à une équipe et à ses pipelines.
De plus, les permissions des rôles owner, member, pipeline-operator et viewer peuvent être modifiées en configurant RBAC (en configurant plus spécifiquement ses actions). Lisez-en plus à ce sujet ici : https://concourse-ci.org/user-roles.html
Notez que Concourse groupe les pipelines à l'intérieur des équipes. Par conséquent, les utilisateurs appartenant à une équipe pourront gérer ces pipelines et plusieurs équipes peuvent exister. Un utilisateur peut appartenir à plusieurs équipes et avoir des permissions différentes à l'intérieur de chacune d'elles.
Vars & Credential Manager
Dans les configurations YAML, vous pouvez configurer des valeurs en utilisant la syntaxe ((_source-name_:_secret-path_._secret-field_))
.
Selon la documentation : Le source-name est optionnel, et s'il est omis, le gestionnaire de credentials à l'échelle du cluster sera utilisé, ou la valeur peut être fournie statiquement.
Le _secret-field optionnel_ spécifie un champ sur le secret récupéré à lire. S'il est omis, le gestionnaire de credentials peut choisir de lire un 'champ par défaut' du credential récupéré si le champ existe.
De plus, le secret-path et secret-field peuvent être entourés de guillemets doubles "..."
s'ils contiennent des caractères spéciaux comme .
et :
. Par exemple, ((source:"my.secret"."field:1"))
définira le secret-path à my.secret
et le secret-field à field:1
.
Static Vars
Les vars statiques peuvent être spécifiées dans les étapes de tâches :
Or en utilisant les fly
arguments suivants :
-v
ou--var
NAME=VALUE
définit la chaîneVALUE
comme valeur pour la varNAME
.-y
ou--yaml-var
NAME=VALUE
analyseVALUE
comme YAML et le définit comme valeur pour la varNAME
.-i
ou--instance-var
NAME=VALUE
analyseVALUE
comme YAML et le définit comme valeur pour la var d'instanceNAME
. Voir Grouping Pipelines pour en savoir plus sur les vars d'instance.-l
ou--load-vars-from
FILE
chargeFILE
, un document YAML contenant le mappage des noms de var aux valeurs, et les définit tous.
Gestion des Identifiants
Il existe différentes manières de spécifier un Gestionnaire d'Identifiants dans un pipeline, lisez comment dans https://concourse-ci.org/creds.html. De plus, Concourse prend en charge différents gestionnaires d'identifiants :
Notez que si vous avez un certain type d'accès en écriture à Concourse, vous pouvez créer des jobs pour exfiltrer ces secrets car Concourse doit pouvoir y accéder.
Énumération Concourse
Pour énumérer un environnement concourse, vous devez d'abord rassembler des identifiants valides ou trouver un jeton authentifié, probablement dans un fichier de configuration .flyrc
.
Connexion et énumération de l'utilisateur actuel
Pour vous connecter, vous devez connaître l'endpoint, le nom de l'équipe (par défaut
main
) et une équipe à laquelle l'utilisateur appartient :fly --target example login --team-name my-team --concourse-url https://ci.example.com [--insecure] [--client-cert=./path --client-key=./path]
Obtenez les cibles configurées :
fly targets
Vérifiez si la connexion cible configurée est toujours valide :
fly -t <target> status
Obtenez le rôle de l'utilisateur par rapport à la cible indiquée :
fly -t <target> userinfo
Notez que le jeton API est enregistré par défaut dans $HOME/.flyrc
, en fouillant une machine, vous pourriez y trouver les identifiants.
Équipes & Utilisateurs
Obtenez une liste des Équipes
fly -t <target> teams
Obtenez les rôles au sein de l'équipe
fly -t <target> get-team -n <team-name>
Obtenez une liste des utilisateurs
fly -t <target> active-users
Pipelines
Lister les pipelines :
fly -t <target> pipelines -a
Obtenez le yaml du pipeline (des informations sensibles peuvent être trouvées dans la définition) :
fly -t <target> get-pipeline -p <pipeline-name>
Obtenez toutes les vars déclarées dans la config du 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
Obtenez tous les noms de secrets de pipelines utilisés (si vous pouvez créer/modifier un job ou détourner un conteneur, vous pourriez les exfiltrer) :
Conteneurs & Travailleurs
Lister travailleurs :
fly -t <target> workers
Lister conteneurs :
fly -t <target> containers
Lister builds (pour voir ce qui est en cours d'exécution) :
fly -t <target> builds
Attaques Concourse
Brute-Force des Identifiants
admin:admin
test:test
Énumération des Secrets et des paramètres
Dans la section précédente, nous avons vu comment vous pouvez obtenir tous les noms et vars des secrets utilisés par le pipeline. Les vars peuvent contenir des informations sensibles et le nom des secrets sera utile plus tard pour essayer de les voler.
Session à l'intérieur d'un conteneur en cours d'exécution ou récemment exécuté
Si vous avez suffisamment de privilèges (rôle de membre ou plus), vous serez en mesure de lister les pipelines et les rôles et d'obtenir simplement une session à l'intérieur du conteneur <pipeline>/<job>
en utilisant :
Avec ces permissions, vous pourriez être en mesure de :
Voler les secrets à l'intérieur du conteneur
Essayer de s'échapper vers le nœud
Énumérer/Abuser de l'endpoint de métadonnées cloud (depuis le pod et depuis le nœud, si possible)
Création/Modification de Pipeline
Si vous avez suffisamment de privilèges (rôle de membre ou plus), vous pourrez créer/modifier de nouveaux pipelines. Vérifiez cet exemple :
Avec la modification/création d'un nouveau pipeline, vous pourrez :
Voler les secrets (en les affichant ou en accédant au conteneur et en exécutant
env
)Échapper au nœud (en vous donnant suffisamment de privilèges -
privileged: true
)Énumérer/Abuser de l'endpoint cloud metadata (depuis le pod et depuis le nœud)
Supprimer le pipeline créé
Exécuter une tâche personnalisée
C'est similaire à la méthode précédente, mais au lieu de modifier/créer un tout nouveau pipeline, vous pouvez juste exécuter une tâche personnalisée (ce qui sera probablement beaucoup plus discret) :
Évasion vers le nœud depuis une tâche privilégiée
Dans les sections précédentes, nous avons vu comment exécuter une tâche privilégiée avec concourse. Cela ne donnera pas au conteneur exactement le même accès que le drapeau privilégié dans un conteneur docker. Par exemple, vous ne verrez pas le périphérique du système de fichiers du nœud dans /dev, donc l'évasion pourrait être plus "complexe".
Dans le PoC suivant, nous allons utiliser le release_agent pour échapper avec quelques petites modifications :
Comme vous l'avez peut-être remarqué, il s'agit simplement d'une évasion de release_agent régulière en modifiant simplement le chemin de la cmd dans le nœud.
Évasion vers le nœud depuis un conteneur Worker
Une évasion de release_agent régulière avec une légère modification suffit pour cela :
Évasion vers le nœud depuis le conteneur Web
Même si le conteneur web a certaines défenses désactivées, il ne fonctionne pas comme un conteneur privilégié commun (par exemple, vous ne pouvez pas monter et les capacités sont très limitées, donc toutes les façons simples de s'échapper du conteneur sont inutiles).
Cependant, il stocke des identifiants locaux en texte clair :
Vous pouvez utiliser ces identifiants pour vous connecter au serveur web et créer un conteneur privilégié et échapper au nœud.
Dans l'environnement, vous pouvez également trouver des informations pour accéder à l'instance postgresql que concourse utilise (adresse, nom d'utilisateur, mot de passe et base de données parmi d'autres informations) :
Abuser du service Garden - Pas une véritable attaque
Ce ne sont que quelques notes intéressantes sur le service, mais comme il n'écoute que sur localhost, ces notes n'auront aucun impact que nous n'avons pas déjà exploité auparavant.
Par défaut, chaque worker concourse exécutera un service Garden sur le port 7777. Ce service est utilisé par le Web master pour indiquer au worker ce qu'il doit exécuter (télécharger l'image et exécuter chaque tâche). Cela semble plutôt bon pour un attaquant, mais il y a quelques bonnes protections :
Il est exposé uniquement localement (127..0.0.1) et je pense que lorsque le worker s'authentifie contre le Web avec le service SSH spécial, un tunnel est créé afin que le serveur web puisse communiquer avec chaque service Garden à l'intérieur de chaque worker.
Le serveur web surveille les conteneurs en cours d'exécution toutes les quelques secondes, et les conteneurs inattendus sont supprimés. Donc, si vous voulez exécuter un conteneur personnalisé, vous devez manipuler la communication entre le serveur web et le service garden.
Les workers concourse s'exécutent avec des privilèges élevés de conteneur :
Cependant, des techniques comme monter le périphérique /dev du nœud ou release_agent ne fonctionneront pas (car le véritable périphérique avec le système de fichiers du nœud n'est pas accessible, seulement un virtuel). Nous ne pouvons pas accéder aux processus du nœud, donc s'échapper du nœud sans exploits de noyau devient compliqué.
Dans la section précédente, nous avons vu comment s'échapper d'un conteneur privilégié, donc si nous pouvons exécuter des commandes dans un conteneur privilégié créé par le travailleur actuel, nous pourrions s'échapper vers le nœud.
Notez qu'en jouant avec concourse, j'ai remarqué que lorsqu'un nouveau conteneur est créé pour exécuter quelque chose, les processus du conteneur sont accessibles depuis le conteneur de travailleur, donc c'est comme un conteneur créant un nouveau conteneur à l'intérieur de lui.
Entrer dans un conteneur privilégié en cours d'exécution
Créer un nouveau conteneur privilégié
Vous pouvez très facilement créer un nouveau conteneur (il suffit d'exécuter un UID aléatoire) et d'exécuter quelque chose dessus :
Cependant, le serveur web vérifie toutes les quelques secondes les conteneurs qui sont en cours d'exécution, et si un conteneur inattendu est découvert, il sera supprimé. Comme la communication se fait en HTTP, vous pourriez altérer la communication pour éviter la suppression de conteneurs inattendus :
Références
https://concourse-ci.org/vars.html
Last updated