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

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

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 と連鎖させ、管理者アカウントの乗っ取りとリモートコード実行を達成します。

リポジトリを見る
221ヶ月前未レビュー

人気

すべて見る →

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

すべてのツールを探索

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

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

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" を持つ要素から設定を読み取ります:

// 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箇所を特定しました:

# View diff between vulnerable and patched versions
git diff 7.0.2..7.0.3 -- src/wp-includes/user.php

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

修正前 :

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

image.png

修正後 :

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

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

修正前 :

image.png

// BEFORE:
'<strong>' . $username . '</strong>'    // ← no escaping

修正後 :

// AFTER:
'<strong>' . esc_html( $username ) . '</strong>'

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

修正前 :

image.png

// BEFORE:
'<strong>' . $email . '</strong>'    // ← no escaping

修正後 :

// AFTER:
'<strong>' . esc_html( $email ) . '</strong>'

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

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

$_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 をブロックします:

// 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);
ツールをダウンロード