CVE-2024-23897 の自己完結型かつ完全にローカルな再現です。Jenkins CLI における重大な (CVSS 3.1 基本スコア 9.8) 任意ファイル読み取りです。
このプロジェクトは、Jenkins のマイナーバージョンのみが異なる 2 つの Docker スタックを起動し、両方に対して同じ概念実証を実行し、パッチ未適用のコントローラでは脆弱性が発動し、パッチ適用済みでは沈黙することを示します。
すべては単一の Python プログラム poc.py によって駆動されます。ハーネスは環境 (Docker Compose、公式の jenkins-cli.jar クライアント、出力の逐語的キャプチャ) を調整するだけです。脆弱性自体は Jenkins 自身の Java コードに存在し、ここで再実装されることはありません。
パッチレビューと CVSS 分解を含む完全な文書分析は、report/report.pdf にあります。
Jenkins CLI は、args4j ライブラリで引数パーサを構築しています。args4j には expandAtFiles と呼ばれる機能があり、atSyntax フラグで制御され、デフォルトで有効になっています。この機能は、@/path/to/file 形式の引数を、コマンド実行前にそのファイルの内容に書き換えます。ファイルは Jenkins コントローラプロセスの権限で開かれます。3 つの CLI トランスポート (HTTP、WebSocket、SSH) すべてが同じパーサに集約されるため、任意の CLI コマンドを送信できるクライアントは、コントローラ上の任意のファイルを読み取ることができます。修正 (コミット 554f0378) では、デフォルトで false となる定数 ALLOW_AT_SYNTAX が追加され、展開をオフにしています。
これは古典的なパストラバーサルではありません。../ もエスケープすべきベースディレクトリもありません。パスは直接開かれます。結果軸では任意のファイル読み取りであり、メカニズム軸では引数展開です。
cve-2024-23897-jenkins-poc/
README.md # this file
LICENSE
poc.py # Python reproduction harness (all subcommands)
Dockerfile.vuln # jenkins/jenkins:2.426.2-lts + matrix-auth
Dockerfile.fix # jenkins/jenkins:2.426.3-lts + matrix-auth
docker-compose.vuln.yml # jenkins-vuln + attacker-vuln
docker-compose.fix.yml # jenkins-fix + attacker-fix
init.groovy.d/
01-create-users.groovy # bootstraps admin + readuser via matrix-auth
evidence/
docker-versions.txt # host Docker + Compose versions
output-vulnerable.txt # captured during `poc.py exploit`
output-fixed.txt # captured during `poc.py verify-fix`
report/
report.pdf # full written analysis
report.tex # LaTeX source (self-contained, no external figures)
| 要件 | 備考 |
|---|---|
| Docker Engine 24 以上 | 使用した正確なバージョンは evidence/docker-versions.txt に記録されています |
| Docker Compose v2 | ビルトインの docker compose プラグインとして提供 |
| Python 3.8 以上 | 標準ライブラリのみ、pip install は不要 |
| ディスク容量 | 2 つの Jenkins イメージ、temurin イメージ、matrix-auth プラグインで約 1.5 GB |
ホストに JDK は不要です。Java は攻撃者コンテナ内で実行されます。
compose ファイルは platform: linux/amd64 を指定しているため、イメージは Apple Silicon でも同じように動作します。これは運用上の選択であり、プラットフォームに依存しない脆弱性には影響しません。
リポジトリ内から実行します:
python3 poc.py up-vuln # build and start the vulnerable stack, fetch the CLI jar
python3 poc.py place-proof # write the harmless marker file inside the controller
python3 poc.py exploit # run the PoC, writes evidence/output-vulnerable.txt
python3 poc.py up-fix # tear down vuln, build and start the patched stack
python3 poc.py verify-fix # run the same PoC, writes evidence/output-fixed.txt
python3 poc.py teardown # stop and remove both stacks
初回実行は数分かかります (イメージのプルとプラグインのインストール)。以降の実行ははるかに高速です。
poc.py exploit は、キャプチャされた出力にマーカー文字列 POC-PROOF-LINE が出現した場合 (漏洩が発生) に成功と判定されます。poc.py verify-fix は逆の条件、つまりマーカーが存在しない場合に成功します。両方のサブコマンドは失敗時に非ゼロで終了するため、2 つの証拠ファイルとその終了ステータスがテスト結果そのものになります。
両方の compose ファイルは、1 つの Docker ネットワーク上で同じ 2 つのサービス (Jenkins コントローラ (被害者) と小さな eclipse-temurin:17-jre 攻撃者コンテナ) を実行します。poc.py はホスト上で実行され、subprocess を介して Docker を駆動しますが、実際の java -jar jenkins-cli.jar ... の呼び出しは攻撃者コンテナ内で実行されます。攻撃者は Jenkins データボリュームにアクセスできず、リモート攻撃者と同様にネットワーク経由でのみコントローラに到達します。
init.groovy.d/01-create-users.groovy は matrix-auth プラグインを使用して、意図的に異なる権限を持つ 2 つのアカウントを作成します。
| アカウント | 権限 | PoC での役割 |
|---|---|---|
admin | Jenkins.ADMINISTER | 「少なくとも 1 人の管理者」要件を満たすためだけに存在、攻撃には使用されない |
readuser | Jenkins.READ のみ (Overall/Read) | 認証された攻撃者 |
| anonymous | なし | 未認証の攻撃者 |
これにより、公式アドバイザリにおける Overall/Read 対 anonymous という正確な分割が再現されます。Jenkins コアのデフォルトが生み出す「ログイン済みユーザー対 anonymous」という緩い分割ではありません。したがって、完全なファイル漏洩は CVE のみに起因し、管理権限によるものではありません。
PoC は各コントローラに対して 4 つのコンテキストを実行します。以下の表は A/B 比較をまとめたものです。完全なキャプチャは evidence/ にあります。
| 観察 | 脆弱版 2.426.2 | パッチ適用版 2.426.3 |
|---|---|---|
@トークンの処理 | ファイル内容に展開される | リテラル文字列として扱われる |
readuser + connect-node | 完全なファイル開示 (3行中3行) | 開示なし |
anonymous + who-am-i / help | 認証ゲート前のパーサエラーによる部分的な漏洩 (1行目) | 開示なし |
出力内のマーカー POC-PROOF-LINE | 存在する | 存在しない |
2 つの列の間で変化する唯一の変数は Jenkins のマイナーバージョンであり、それを通じてパッチ後の atSyntax のデフォルトが変わります。したがって、逆の結果は動作の変化をコミット 554f0378 のパーサ修正に帰属させます。
Jenkins をコンテナ内で実行することは緩和策ではありません。パーサは Jenkins JVM の権限でファイルを読み取り、それらのファイルは認証情報ストアを含む同じコンテナファイルシステム内に存在します。コンテナ境界はホストを Jenkins プロセスから保護するものであり、Jenkins プロセスをそれ自身から保護するものではありません。ここでの Docker セットアップはデモンストレーション用のサンドボックスにすぎません。
パッチ適用済みリリースにアップグレードしてください: 2.442 (週次)、または 2.426.3 または 2.440.1 (LTS)。すべて 2024 年 1 月 24 日に公開されました。即時のアップグレードが不可能な場合は、システムプロパティ hudson.cli.CLICommand.allowAtSyntax を設定しないまま (デフォルト) にしておきます。これにより ALLOW_AT_SYNTAX が false に保たれ、展開が無効になります。個々の CLI トランスポートを無効にすることは部分的な対策にすぎません。3 つすべてが同じパーサに収束するためです。
すべての作業はローカルの Docker 環境で、ハーネス自身が作成する無害な 3 行のマーカーファイル (/tmp/poc-proof.txt) に対して実行されます。公開 Jenkins インスタンスはスキャンも接触もされず、secrets/master.key や credentials.xml などの実際の秘密情報が読み取られることは決してありません。
554f0378 on jenkinsci/jenkins: https://github.com/jenkinsci/jenkins/commit/554f03782057c499c49bbb06575f0d28b5200edbParserProperties.withAtSyntax): https://github.com/kohsuke/args4j