
JoomGallery ≤ 4.3.0 — 編集者ロールのユーザーが任意のギャラリー画像をハイジャックし、XSSペイロードを保存することで、管理者セッションの乗っ取りを可能にする
JoomGalleryのUserimageController::save()は、checkACL('edit.own', ...)ではなくcheckACL('edit', ...)をチェックしています。そのため、編集者(Editor)ロールのユーザーは、所有権に関係なく任意の画像に対してtask=userimage.save&id=NへPOSTできます(IDOR — CWE-639)。編集者ロールはグローバルにcore.editを保持しているため、管理者が所有する画像を含むサイト上のすべての画像IDで認可チェックが通過します。
さらに、フロントエンドの画像テンプレートで$this->escape()の呼び出しが欠落していることと組み合わせることで、編集者は管理者所有の画像を含む任意の画像タイトルにXSSペイロードを保存でき、すべての訪問者のブラウザでJavaScriptが実行されるようになります。これにより、管理者セッションの完全な乗っ取りとサイト全体の侵害が可能になります。
| コンポーネント | 影響を受けるバージョン | テスト環境 | 修正版 |
|---|---|---|---|
| JoomGallery (com_joomgallery) | 4.0.0 – 4.3.0 | Joomla 5.4.7 + JoomGallery 4.3.0-stable (PHP 8.2 / Apache) | 4.4.0 |
種別: オブジェクトレベル認可の欠陥 / IDOR (CWE-639) に保存型クロスサイトスクリプティング (CWE-79) を連鎖させたもの 必要な認証: 低権限の編集者(Editor)アカウント
ファイル: components/com_joomgallery/src/Controller/UserimageController.php
save()アクションは、edit.own権限ではなくedit権限を使用してACLチェックを実行します。edit権限はすべての編集者ロールユーザーにグローバルに付与されているため、作成者が誰であるかに関係なく、任意の画像IDでチェックが成功します。
USERIMAGECONTROLLER.PHP — 脆弱なコード(145行目)
// Vulnerable
if (!$this->checkACL('edit', 'image', $recordId, $parent_id, true)) { ... }
core.editは編集者グループによってグローバルに保持されているため、この条件はすべての画像IDに対してfalseと評価され、無制限の書き込みアクセスが許可されます。保存が成功すると、モデルはさらにcreated_byを攻撃者のユーザーIDに更新し、画像の所有権を攻撃者に静かに移転します。
ファイル: components/com_joomgallery/tmpl/image/default.php
フロントエンドの画像テンプレートは、$this->item->titleをalt属性のコンテキストにHTMLエンコードせずに出力します。JoomlaのJInputのSTRINGフィルタはダブルクォート文字を除去しないため、"を含むペイロードが属性を抜け出して任意のイベントハンドラを注入できます。
DEFAULT.PHP — 脆弱なコード(64行目、78行目)
// Vulnerable
item->title; ?>" ...>
ペイロードabc" onmouseover="alert(document.domain);" x="はjos_joomgallery.titleに保存され、すべてのページレンダリングでHTML属性にそのまま注入されます。保存層および表示層ではサニタイズは一切行われません。
jform[title]にXSSペイロードを入れてtask=userimage.save&id=3へPOSTする。ACLチェックは(edit.ownではなくcore.editで)通過する。サーバーはHTTP 303を返す — 403ではない。created_byが攻撃者のユーザーIDに移転される。jos_joomgallery.titleに保存される。alt="abc" onmouseover="alert(document.domain);"を描画する。XSSが発火する。管理者セッションが取得される → サイト全体が侵害される。管理者はJoomGalleryバックエンド(Joomla 5.4.7)経由でadmin_imageを作成する。画像は公開済み、承認済み、所有者はAdministrator。

GET /index.php/component/users/login — レスポンスJSONに"csrf.token":"a68c2b3a..."が含まれる。後続のログインPOSTのためにトークンを取得。

joomla_user_state=logged_inPOST /index.php/component/users/loginにCSRFトークンと編集者の認証情報を付けて送信。レスポンス: HTTP 303とSet-Cookie: joomla_user_state=logged_in。セッションクッキーを取得。

セッションクッキーを付けてGET /index.php?option=com_joomgalleryを実行。レスポンスには保存リクエスト用の新しい"csrf.token":"2d96934b..."が含まれる。

編集者がjform[title]を次の値に設定してoption=com_joomgallery&task=userimage.save&id=3へPOSTする:
abc" onmouseover="alert(document.domain);" x="
サーバーはHTTP 303(403ではない)を返し、IDORを確認できる。LocationヘッダーにはリダイレクトURL内にXSSペイロードが表示され、タイトルが受け入れられ保存されたことが確認できる。

JoomGalleryバックエンドでは、画像ID=3の所有者がEditor Userになっている。不正な保存中にcreated_byフィールドがデータベース内で静かに更新された。

jos_joomgalleryへのSQLクエリにより、XSSペイロードが保存されていることを確認 — "は"ではなく生のダブルクォートとして保存されている。保存層ではサニタイズは行われていない。

/index.php/component/joomgallery/galleryにアクセスした任意のユーザーがペイロードをトリガーする。ブラウザのalert()ダイアログが、被害者のオリジン(document.domain)でのJavaScript実行を確認する。

created_byが攻撃者に静かに再割り当てされ、監査証跡が恒久的に改変される。alt属性内の悪意のあるスクリプトは管理者のセッションクッキーを外部送信でき、攻撃者にJoomlaインストール全体への完全なバックエンドアクセスと制御を与える。S:C)で実行され、攻撃者の低権限セッションと被害者の高権限セッションの間の信頼境界を越える。