技術 約6分で読めます

Ghost CMSのCVE-2026-26980悪用で700サイト超がClickFix配布元になった

いけさん目次

TL;DR

影響 Ghost 3.24.0〜6.19.0。公開Content API経由のSQLインジェクションから任意データ読み取り、Admin APIキー窃取、記事本文の一括改ざん

対応 Ghost 6.19.1以降へ更新。更新済みでも、露出期間中のAdmin APIキー、Content APIキー、スタッフアカウント、セッションシークレットのローテーション

確認 /ghost/api/content/filter=slug:[ または slug%3A%5B、記事末尾に追加された外部JavaScript、clo4shara[.]xyzcom-apps[.]cc への誘導、偽CAPTCHA経由のClickFix表示


Ghost CMSのCVE-2026-26980は、2月に修正されたContent APIのSQLインジェクションだった。
それが5月には、未更新のGhostサイトからAdmin APIキーを抜き、記事本文の末尾へJavaScriptローダーを差し込む攻撃に使われている。
QiAnXin XLabは、700超のドメインで汚染を確認したと書いている。

ここからデータベース読み取りで止まらず、記事本文の改ざんへ進んだ。
攻撃者は読み取ったAdmin APIキーでGhost Admin APIを呼び、既存記事をまとめて改ざんしていた。
正規サイトの本文末尾から偽Cloudflare確認ページへ誘導し、Windowsの「ファイル名を指定して実行」やPowerShellへコマンドを貼らせるClickFix攻撃につなげている。
ClickFixは、偽のエラーや人間確認の画面で「これを実行すれば直る」と促し、ユーザー自身に攻撃コマンドを実行させる手口を指す。

Content APIキーは秘密ではない

GhostのContent APIは、公開記事をテーマから読むためのAPIだ。
Content APIキーはテーマHTMLに埋め込まれる前提なので、これ自体は秘密として扱えない。
GitHub Advisoryも、Content APIキーへのアクセス制限ではこの脆弱性を緩和できないと書いている。

CVE-2026-26980の問題は、Ghost Query Languageの slug:[...] フィルタ処理と、SQLの ORDER BY 句を作る処理の間にあった。
脆弱な版では、slugの値がSQL文字列へ直接埋め込まれ、パラメータ化されていなかった。
SonicWallの解析では、filter=slug:[...]order=slug:[...] から盲目的SQLインジェクションに入り、Admin APIキー、セッションシークレット、パスワードハッシュなどを読む経路が説明されている。

修正済みの6.19.1では、生SQL文字列の連結ではなく、プレースホルダーとバインディングを使う形に変わっている。
影響範囲はGhost 3.24.0から6.19.0まで。
NVDは機密性のみHighのCVSS 7.5としているが、GitHub CNA(CVE Numbering Authority。CVE採番と一次評価を行う機関)の評価は機密性High、完全性High、可用性Lowの9.4 Criticalだ。
今回の悪用では、Admin APIキーまで抜けた時点で記事本文の改ざんに届く。運用上は、完全性への影響も含めて扱うことになる。

記事改ざんが配布経路になる

XLabが確認した流れでは、攻撃者はGhostのAdmin APIキーを取得した後、Admin APIで記事本文の末尾へJavaScriptローダーを挿入している。
このローダーは外部ドメインから次段のコードを取りに行く。
最初の波では clo4shara[.]xyz/11z77u3.php が使われ、その後 com-apps[.]cc へ切り替わったとXLabは書いている。

ここで出てくるPHPは、単純な固定リダイレクトではない。
ブラウザ指紋情報を集め、サーバー側の判断でリダイレクト、ポップアップ、ダウンロード、任意JavaScript実行を返す選別スクリプトとして動く。
セキュリティスキャナやクローラには無害なページを見せ、対象になりそうな訪問者だけを偽CAPTCHAへ送るための分岐だ。

偽CAPTCHAはCloudflareの人間確認に似せて、ユーザーへコマンドのコピーと実行を促す。
The Hacker Newsの整理では、Base64化されたコマンドをWindowsの実行ダイアログへ貼らせ、ZIP、バッチ、PowerShell、DLL、rundll32.exe へ進む流れが確認されている。
後続の波ではDLLではなくJavaScriptの悪意あるコードも使われ、最終的にWindows実行ファイルを落とす。

flowchart TD
    A["Ghost 3.24.0〜6.19.0"] --> B["Content APIの<br/>slugフィルタSQLi"]
    B --> C["Admin APIキーを読み取り"]
    C --> D["Ghost Admin APIで<br/>記事本文を一括改ざん"]
    D --> E["記事末尾の<br/>JavaScriptローダー"]
    E --> F["選別スクリプトで<br/>訪問者を分岐"]
    F --> G["偽Cloudflare確認"]
    G --> H["ClickFixで<br/>Windowsコマンド実行"]

Ghost本体へWebシェルを置く攻撃ではなく、記事コンテンツを配布元として使う点が今回の特徴だ。
サイト運営者から見ると、サーバーのプロセスやテーマファイルが変わっていなくても、公開記事そのものが攻撃コードを配る状態になる。

6.19.1へ上げた後に残るもの

Ghost 6.19.1以降への更新は最初にやる。
ただし、更新はSQLインジェクションの入口を閉じるだけで、すでに盗まれたキーや、記事本文へ書かれたJavaScriptまでは消さない。

この点は、Joomla JCEのCVE-2026-48907で扱った「更新後も不正プロファイルとPHPファイルを確認する」話に近い。
JCEではプロファイルとウェブシェルが残った。
GhostではAdmin APIキーと記事本文の改ざんが残る。

Ghost側の確認先は、まず記事本文の末尾だ。
見覚えのない <script>、外部ドメインからのローダー、iframeでの偽CAPTCHA読み込みを探す。
差し込みが記事末尾に集中するなら、最近更新された投稿だけでなく、古い公開記事も対象になる。

ログでは、/ghost/api/content/ に対する filter=slug:[、URLエンコードされた slug%3A%5B、SQLキーワード、char(randomblobCASE WHEN を含むリクエストを探す。
盲目的SQLインジェクションでは応答時間の差を使うため、普段より遅いContent APIレスポンスが連続していないかも手がかりになる。
GhostのAdmin APIに対して、短時間に大量の PUT /ghost/api/admin/posts/:id/ が並ぶ場合も、記事一括改ざんの痕跡になる。

認証情報は、Admin APIキー、Content APIキー、スタッフユーザー、セッションシークレットを入れ替える。
スタッフのパスワードハッシュが読まれた前提で、管理者パスワードも変える。
同じメールアドレスとパスワードを他のCMSやホスティング管理画面で使っているなら、そちらも分ける。

WAFで止めるなら一時策として扱う

GhostのAdvisoryは、アプリケーションレベルの回避策はないとしている。
暫定策として、リバースプロキシやWAFで filter パラメータ内の slug:[ または slug%3A%5B をブロックする方法を挙げている。
ただし、正規のslugフィルタ機能が壊れる場合がある。

WAFで止める場合も、Ghost本体を6.19.1以降へ上げるまでの時間稼ぎとして置く。
Content APIキーは公開前提なので、キーを隠す設計変更では今回の入口を塞げない。
公開ページのHTMLから取れる値を秘密として扱う設計にすると、塞ぐべき入口を見落とす。

今回の汚染は2026年5月7日にXLabが検知し、5月16日には別ドメインと検出ゼロの情報窃取マルウェアへ切り替わっていた。
最初のドメインがCloudflare側で止まっても、記事本文のローダーが残れば次のドメインへ差し替えられる。
クリーンアップでは、外部ドメイン単位のブロックだけでなく、Ghost記事本文に入ったローダーそのものを消す。

参考