Concourse Enumeration & Attacks
Enumération et Attaques Concourse
Rôles et Autorisations des Utilisateurs
Concourse propose 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 administrateurs peuvent configurer d'autres équipes (par exemple :
fly set-team
,fly destroy-team
...). Les autorisations de ce rôle ne peuvent pas être affectées par le RBAC.propriétaire : Les propriétaires d'équipe peuvent modifier tout ce qui concerne l'équipe.
membre : Les membres de l'équipe peuvent lire et écrire dans les ressources de l'équipe mais ne peuvent pas modifier les paramètres de l'équipe.
opérateur de pipeline : Les opérateurs de pipeline peuvent effectuer des opérations sur le pipeline telles que déclencher des builds et épingler des ressources, mais ils ne peuvent pas mettre à jour les configurations du pipeline.
spectateur : Les spectateurs de l'équipe ont un accès "lecture seule" à une équipe et à ses pipelines.
De plus, les autorisations des rôles propriétaire, membre, opérateur de pipeline et spectateur peuvent être modifiées en configurant le RBAC (en configurant plus spécifiquement ses actions). En savoir plus à ce sujet sur : https://concourse-ci.org/user-roles.html
Notez que Concourse regroupe les pipelines à l'intérieur des équipes. Par conséquent, les utilisateurs appartenant à une équipe pourront gérer ces pipelines et plusieurs équipes pourraient exister. Un utilisateur peut appartenir à plusieurs équipes et avoir des autorisations différentes au sein de chacune d'elles.
Variables & Gestionnaire de Crédentials
Dans les configurations YAML, vous pouvez configurer des valeurs en utilisant la syntaxe ((_source-name_:_secret-path_._secret-field_))
.
D'après la documentation : Le nom de la source est facultatif, et s'il est omis, le gestionnaire de crédentials à l'échelle du cluster sera utilisé, ou la valeur peut être fournie statiquement.
Le champ facultatif _secret-field_ spécifie un champ sur le secret récupéré à lire. S'il est omis, le gestionnaire de crédentials peut choisir de lire un 'champ par défaut' du crédential récupéré si le champ existe.
De plus, le secret-path et le 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 sur my.secret
et le secret-field sur field:1
.
Variables Statiques
Les variables statiques peuvent être spécifiées dans les étapes des tâches :
Ou en utilisant les arguments fly
suivants :
-v
ou--var
NOM=VALEUR
définit la chaîneVALEUR
comme valeur pour la variableNOM
.-y
ou--yaml-var
NOM=VALEUR
analyseVALEUR
en tant que YAML et la définit comme valeur pour la variableNOM
.-i
ou--instance-var
NOM=VALEUR
analyseVALEUR
en tant que YAML et la définit comme valeur pour la variable d'instanceNOM
. Consultez Grouping Pipelines pour en savoir plus sur les variables d'instance.-l
ou--load-vars-from
FICHIER
chargeFICHIER
, un document YAML contenant des noms de variables et leurs valeurs, et les définit tous.
Gestion des identifiants
Il existe différentes façons de spécifier un Gestionnaire d'identifiants dans un pipeline, lisez comment faire sur https://concourse-ci.org/creds.html. De plus, Concourse prend en charge différents gestionnaires d'identifiants :
Notez que si vous avez une sorte d'accès en écriture à Concourse, vous pouvez créer des tâches pour exfiltrer ces secrets car Concourse doit pouvoir y accéder.
Énumération de Concourse
Pour énumérer un environnement Concourse, vous devez d'abord collecter des identifiants valides ou trouver un jeton d'authentification probablement dans un fichier de configuration .flyrc
.
Connexion et énumération de l'utilisateur actuel
Pour vous connecter, vous devez connaître le point de terminaison, le nom de l'équipe (par défaut
main
) et une équipe à laquelle l'utilisateur appartient :fly --target exemple login --team-name mon-equipe --concourse-url https://ci.exemple.com [--insecure] [--client-cert=./chemin --client-key=./chemin]
Obtenir les cibles configurées :
fly targets
Vérifier si la connexion de la cible configurée est toujours valide :
fly -t <cible> status
Obtenir le rôle de l'utilisateur par rapport à la cible indiquée :
fly -t <cible> userinfo
Notez que le jeton API est enregistré dans $HOME/.flyrc
par défaut, si vous pillez des machines, vous pourriez y trouver les identifiants.
Équipes et Utilisateurs
Obtenir une liste des équipes
fly -t <cible> teams
Obtenir les rôles à l'intérieur de l'équipe
fly -t <cible> get-team -n <nom-equipe>
Obtenir une liste d'utilisateurs
fly -t <cible> active-users
Pipelines
Lister les pipelines :
fly -t <cible> pipelines -a
Obtenir le yaml du pipeline (des informations sensibles peuvent être trouvées dans la définition) :
fly -t <cible> get-pipeline -p <nom-pipeline>
Obtenir toutes les variables de configuration du pipeline
for nompipeline in $(fly -t <cible> pipelines | grep -Ev "^id" | awk '{print $2}'); do echo $nompipeline; fly -t <cible> get-pipeline -p $nompipeline -j | grep -Eo '"vars":[^}]+'; done
Obtenir tous les noms de secrets des pipelines utilisés (si vous pouvez créer/modifier une tâche ou détourner un conteneur, vous pourriez les exfiltrer) :
Conteneurs et Travailleurs
Liste des travailleurs :
fly -t <cible> workers
Liste des conteneurs :
fly -t <cible> containers
Liste des constructions (pour voir ce qui est en cours d'exécution) :
fly -t <cible> builds
Attaques sur 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 obtenir tous les noms de secrets et de variables utilisés par le pipeline. Les variables 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 pourrez liste des pipelines et des rôles et simplement obtenir une session à l'intérieur du conteneur <pipeline>/<tâche>
en utilisant :
Avec ces autorisations, vous pourriez être en mesure de :
Vol de secrets à l'intérieur du conteneur
Essayer de s'échapper vers le nœud
Énumérer/Abuser du point de terminaison métadonnées cloud (à partir du pod et du 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. Consultez 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 des métadonnées cloud (à partir du pod et du nœud)
Supprimer le pipeline créé
Exécuter une tâche personnalisée
Ceci est similaire à la méthode précédente mais au lieu de modifier/créer un tout nouveau pipeline, vous pouvez simplement exécuter une tâche personnalisée (ce qui sera probablement beaucoup plus furtif) :
Échapper au nœud à partir de la 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 exactement au conteneur 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 nous échapper avec quelques petites modifications :
Comme vous l'avez peut-être remarqué, il s'agit simplement d'une évasion d'agent de publication régulière en modifiant simplement le chemin de la commande dans le nœud
Évasion vers le nœud à partir d'un conteneur Worker
Une évasion d'agent de publication régulière avec une légère modification est suffisante pour cela :
Échapper au nœud depuis le conteneur Web
Même si le conteneur Web a certaines défenses désactivées, il ne s'exécute pas en tant que conteneur privilégié commun (par exemple, vous ne pouvez pas monter et les capacités sont très limitées, donc toutes les méthodes faciles pour s'échapper du conteneur sont inutiles).
Cependant, il stocke des informations d'identification locales en clair :
Vous pouvez utiliser ces informations d'identification pour vous connecter au serveur web et créer un conteneur privilégié et vous échapper vers le nœud.
Dans l'environnement, vous pouvez également trouver des informations pour accéder à l'instance postgresql utilisée par concourse (adresse, nom d'utilisateur, mot de passe et base de données, entre autres informations) :
Abus du service Garden - Pas une véritable attaque
Il s'agit simplement de 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 travailleur de Concourse exécutera un service Garden sur le port 7777. Ce service est utilisé par le maître Web pour indiquer au travailleur ce qu'il doit exécuter (télécharger l'image et exécuter chaque tâche). Cela semble assez intéressant pour un attaquant, mais il existe quelques protections intéressantes :
Il est simplement exposé localement (127.0.0.1) et je pense que lorsque le travailleur s'authentifie à nouveau sur le Web avec le service SSH spécial, un tunnel est créé pour que le serveur Web puisse communiquer avec chaque service Garden à l'intérieur de chaque travailleur.
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 travailleurs de Concourse s'exécutent avec des privilèges de conteneur élevés :
Cependant, des techniques comme monter le périphérique /dev du nœud ou release_agent ne fonctionneront pas (car le vrai 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 du 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 du travailleur, c'est donc comme si un conteneur créait un nouveau conteneur à l'intérieur de lui.
Accéder à un conteneur privilégié en cours d'exécution
Création d'un nouveau conteneur privilégié
Vous pouvez très facilement créer un nouveau conteneur (exécuter simplement un UID aléatoire) et exécuter quelque chose dessus :
Cependant, le serveur web vérifie toutes les quelques secondes les conteneurs en cours d'exécution, et s'il en découvre un inattendu, il sera supprimé. Comme la communication se fait en HTTP, vous pourriez altérer la communication pour éviter la suppression des conteneurs inattendus:
Références
https://concourse-ci.org/vars.html
Dernière mise à jour