
Análise independente de causa raiz e prova de conceito para injeção SQL não autenticada até RCE no WordPress (CVE-2026-63030 + CVE-2026-60137), com laboratório Docker e documentação detalhada da cadeia de exploração.
O núcleo do WordPress divulgou o wp2shell em 17/07/2026 como uma RCE crítica não autenticada que afeta instalações padrão (WordPress 6.9.0-6.9.4, 7.0.0-7.0.1, sem necessidade de plugins). O descobridor original (Searchlight Cyber) reteve os detalhes técnicos no momento da divulgação. Este repositório documenta uma análise independente de causa raiz derivada inteiramente do diff do código-fonte do núcleo do WordPress entre as versões vulnerável (6.9.4) e corrigida (6.9.5), além de verificação ao vivo em um laboratório local.
Crédito: a reprodução aqui segue o formato da requisição de sergiointel/wp2shell-poc, confirmado byte a byte e verificado contra o diff real do patch.
root_cause_analysis.md - análise completa: os dois bugs encadeados, o código vulnerável exato, o diff do patch, a cadeia de exploração e a verificação ao vivo antes/depois contra uma instância real do WP 6.9.4.poc_upstream.py - cópia não modificada do PoC original do sergiointel (apenas SQLi).poc_upstream_rce.py - o PoC do sergiointel atualizado cerca de 22 horas após a divulgação, adicionando escalonamento de privilégio não autenticado e RCE sem quebrar nenhuma credencial. Veja a seção 7 de root_cause_analysis.md.poc_extract.py / poc_extract2.py - uma variante ajustada (SLEEP() maior, limite de tempo mais alto, teto de busca de caracteres mais longo) necessária para obter um sinal confiável em um laboratório Dockerizado/com proxy onde o SLEEP(0.15) padrão do script original se perdia no jitter da rede.docker-compose.yml - inicie o mesmo laboratório WordPress 6.9.4 + MariaDB usado para verificação.payload.json, response.json, response_patched.json - uma requisição artesanal replicando a estrutura do PoC e as respostas brutas do servidor, antes e depois de aplicar os arquivos de patch 6.9.5 no lugar.Um segundo PoC, escrito de forma independente (github.com/Icex0/wp2shell-poc), foi revisado por completo e descreve o mesmo mecanismo de causa raiz de forma independente, corroborando a análise abaixo. Usando sua extração de SQLi cega baseada em conteúdo (booleana, não baseada em tempo), o hash da credencial do admin deste laboratório foi recuperado com 100% de precisão e, usando essa credencial conhecida, uma cadeia completa de SQLi para RCE foi demonstrada ao vivo: injeção de SQL para extração de credenciais, webshell por upload de plugin autenticado, execução de código como www-data. Veja a seção 6 de root_cause_analysis.md.
Esse caminho de quebra de credencial é real, mas não é o único. O PoC do sergiointel foi posteriormente atualizado para alcançar RCE não autenticado sem quebra de credencial alguma, usando UNION SELECT para forjar linhas falsas no banco de dados que o próprio código de Customizer/nav-menu/oEmbed-cache do WordPress considera confiável o suficiente para permitir que uma requisição não autenticada de criação de usuário seja bem-sucedida. Veja a seção 7 de root_cause_analysis.md. A injeção de SQL não autenticada sozinha é suficiente para RCE total em uma instalação padrão.
CVE-2026-63030 (confusão de rotas do REST batch, CWE-436):
WP_REST_Server::serve_batch_request_v1() adiciona a um array $matches[] via push simples, mas pula a adição quando o caminho de uma sub-requisição falha ao ser analisado (por exemplo, uma entrada deliberadamente malformada "http://:"). Esse único pulo dessincroniza $matches[] de $requests[] em um índice para cada entrada seguinte. No momento do despacho, $matches[$i] não corresponde mais a $requests[$i], então o código acaba executando os dados de uma requisição sob um manipulador correspondente de uma rota diferente daquela contra a qual foi realmente validada.
CVE-2026-60137 (injeção de SQL): WP_Query::get_posts() só executou a sanitização absint() em author__not_in dentro de um ramo is_array(). Um valor escalar de string pulava a sanitização completamente e era concatenado diretamente em ... post_author NOT IN ($value).
Encadeados: o bug de confusão de rotas permite que um atacante faça uma requisição declarada contra /wp/v2/categories (que não reconhece author_exclude, então nunca é sanitizada) ser realmente despachada através do controlador posts (que lê author_exclude e o encaminha para WP_Query). Nenhuma autenticação necessária.
Sobre a alegação de RCE: confirmado ao vivo que a injeção de SQL cega não autenticada funciona e pode exfiltrar conteúdo arbitrário do banco de dados, incluindo wp_users.user_pass. Uma única passada de extração baseada em tempo em um laboratório virtualizado teve ruído significativo de erro de bit (~94% de precisão por caractere nos testes aqui); um oráculo baseado em conteúdo (booleano) não compartilhou esse modo de falha e recuperou o mesmo campo com 100% de precisão. A RCE foi confirmada por dois caminhos distintos: quebrar um hash de senha recuperado (condicional à força da senha) e uma cadeia de gadgets não autenticada de escalonamento de privilégio usando linhas forjadas por UNION no banco de dados que o próprio código de Customizer/nav-menu/oEmbed-cache do WordPress considera confiável (incondicional, sem necessidade de comprometer credenciais). Veja a seção 7 de root_cause_analysis.md.
docker compose up -d
# wait for WordPress install wizard to be reachable on :8890, then complete setup
HTTP_PROXY=http://127.0.0.1:8080 HTTPS_PROXY=http://127.0.0.1:8080 \
python3 poc_extract2.py http://localhost:8890 "SELECT DATABASE()"
Veja root_cause_analysis.md para a análise técnica completa.
O WordPress publicou correções oficiais: 6.9.5 e 7.0.2 (7.1 Beta 2 para o branch beta), lançadas em 17/07/2026. Atualize imediatamente se você estiver executando uma versão afetada. Este repositório é publicado para fins defensivos e educacionais após o patch já ser público. Execute qualquer um dos códigos aqui apenas contra sistemas que você possui ou possui autorização explícita para testar.