FlawOpen

flawopen.com/Teardowns/cve-2026-87902-wordpress-page-template-path-traversal

● CVE-2026-87902 · CVSS 9.2[1] · 緊急
セキュリティ研究 · FlawOpen

CVE-2026-87902:WordPressページテンプレートのパストラバーサル

未検証のデコード後ページ名がテンプレート解決に使われ、未認証でローカルPHPを読み込める状態でした。RCEにはテーマとPHP環境の特定条件が必要です。[1]

💡 わかりやすい解説 (ELI5)

本を取る前に依頼を確認する図書館員を想像してください。WordPressはテンプレートへの一つの経路を確認しましたが、デコード後の名前の経路を見落としました。特殊な表記がテーマ外へ導く可能性がありました。修正では名前を検証し、許可されたディレクトリ内に制限します。[1], [2], [3]

主要な概念と専門用語

ページテンプレート解決
WordPressはPHP候補を順番に並べ、現在のテーマで最初に一致するファイルを探します。[1], [2]
パストラバーサル
入力を検証しないと、..などの要素で意図したディレクトリから出られる場合があります。[1]
ローカルPHP読み込み
サーバー上にあるPHPファイルを読み込みます。RCEには追加条件が必要です。[1]
`validate_file()`
使用前に許可されないパス要素を拒否するWordPress関数です。[3]
CWE-98
CWE-98 — include/require用ファイル名の不適切な制御:PHPファイルを選ぶ前に入力が十分制限されていません。[1], [7]

フレームワーク対応

根本原因の分析 (Root Cause)

get_page_template() は pagename をデコードして page-{name}.php を候補に加えますが、脆弱版では validate_file() を適用せず、隣接する分岐では適用していました。そのためテーマ外のパスを解決する可能性がありました。page-* というトップレベルディレクトリと読み取り可能なローカルPHPが必要で、RCEにはさらにPHP/サーバー条件が必要です。7.1.2はデコード値を検証し、locate_template()で解決パスを制限します。 WordPressのアドバイザリはこの弱点をCWE-98(PHPのinclude/requireで使うファイル名の不適切な制御)に分類し、発見と責任ある開示をRobert Resslに帰属させています。[1], [2], [3], [7]

CVE-2026-87902 の到達条件:影響範囲と条件付き RCE

導入条件一次資料が確認している内容運用上の対応
影響を受ける WordPress Core のブランチGHSA は影響を受けるバージョンをブランチごとに示しており、4.7.0–4.7.36 から 7.1.0–7.1.1 までが該当します。対応する修正版は 4.7.37(最も古いバックポート)から 7.1.2(現行ブランチ)までで、記載された各ブランチに個別の修正版があります。インストール済み Core のバージョンを確認し、該当ブランチの修正版を適用します。4.7 より前のバージョンはサポート対象へ更新します。
有効なテーマの構成有効な親テーマまたは子テーマに、名前が page- で始まる最上位ディレクトリ(例: page-templates)が必要です。有効な親テーマと子テーマの両方を確認します。有効なテーマを変更した後もこの条件を再確認します。
読み取り可能なローカル PHP ファイル選択対象の .php ファイルが有効なテーマのディレクトリ外に存在し、Web サーバーのアカウントから読み取り可能である必要があります。Web サーバーが読み取れるローカル PHP ファイルを確認します。CVE のバージョン範囲だけでファイル包含が起きたとは判断しません。
条件付き RCE の実行環境アドバイザリは追加の PHP/サーバー条件がある場合に限り RCE を説明しています。例は register_argc_argv=On の pearcmd.php、公式 PHP Docker イメージ、PHP 8.5 より前を使う既定の cPanel 構成です。実際の PHP ランタイムと設定を評価します。WordPress の影響バージョンだけでは RCE や侵害を証明できません。

ステップ・バイ・ステップの攻撃フロー

ステップ 1

入力がテンプレート選択に到達

未認証の要求が候補作成に使われるページ名を渡します。アカウントもクリックも不要です。[1]

ステップ 2

再度デコード

get_page_template()が名前を再デコードし、脆弱版はデコード後の候補を検証しませんでした。[2], [3]

ステップ 3

検索がルート外へ出る

page-*ディレクトリと読み取り可能なPHPファイルがあれば、テーマ外のファイルを解決する可能性があります。[1]

ステップ 4

影響には条件がある

RCEには追加のPHP/サーバー条件が必要です。バージョンだけでは悪用可能性や侵害を証明できません。[1]

ソースコード比較:脆弱 vs 堅牢化

この抜粋は公式7.1.1と7.1.2のタグを比較します。修正ではデコード後に検証し、同じリリースでlocate_template()の解決パスも制限します。これは上流のソース変更であり、単独の完全なパッチや攻撃の実演ではありません。[2], [3]

ソース抜粋: wp-includes/template.php — get_page_template()

脆弱な実装
if ( $pagename ) {
    $pagename_decoded = urldecode( $pagename );

    if ( $pagename_decoded !== $pagename ) {
        $templates[] = "page-{$pagename_decoded}.php";
    }
}
堅牢化されたセキュアパッチ
if ( $pagename ) {
    $pagename_decoded = urldecode( $pagename );

    if ( $pagename_decoded !== $pagename && 0 === validate_file( $pagename_decoded ) ) {
        $templates[] = "page-{$pagename_decoded}.php";
    }
}

エンジニアリング&システム堅牢化チェックリスト

参考資料