wp2shell (CVE-2026-63030 & CVE-2026-60137): Injeção SQL Não Autenticada no WordPress Core
Escrito por 0xM4L · Investigação de Segurança OFFCEPT
O wp2shell encadeia uma desincronização de validação do batch da REST API com um parâmetro WP_Query sem sanitização, resultando em injeção SQL anónima e não autenticada contra o WordPress Core. Provoca a fuga do nome de utilizador e do hash da password de admin. Chegar a uma shell completa a partir daí exige passos extra, não algo que qualquer dos CVEs conceda diretamente. Atualize para 6.9.5 ou 7.0.2 já.
O wp2shell foi descoberto por Adam Kues na Assetnote, parte da Searchlight Cyber, e reportado através do programa HackerOne do WordPress. Transporta dois CVEs. O CVE-2026-63030 é uma desincronização de validação no endpoint batch da REST API do WordPress. O CVE-2026-60137 é uma injeção SQL no parâmetro author__not_in do WP_Query. Encadeados, um atacante anónimo consegue executar blind SQL injection contra uma instalação WordPress por defeito e extrair dados diretamente da tabela de utilizadores, sem login, sem plugin, sem configuração especial. O investigador de segurança Andy Gill publicou uma análise completa ao nível do código do diff do patch no zsec.uk, que é a base da decomposição técnica abaixo.
O que os dois CVEs fazem realmente
- CVE-2026-63030, desincronização de validação do batch. O endpoint batch da REST API (
/wp-json/batch/v1) constrói arrays paralelos para acompanhar cada subpedido, a rota correspondente e o resultado da sua validação. Um caminho de subpedido malformado falha silenciosamente e só é adicionado a um desses arrays, não ao outro. Todos os subpedidos a seguir ficam emparelhados com o estado de match e validação da rota errada. - CVE-2026-60137, author__not_in sem sanitização. Com essa desincronização, um pedido dirigido ao endpoint público de posts pode ser despachado por uma rota cujo schema de parâmetros nunca correu, pelo que o
author_excludechega aoWP_Querycomo string crua em vez de array de inteiros. O ramo do array passa tudo porabsint(). O ramo escalar salta-o completamente e é concatenado diretamente na cláusula SQLNOT IN (...).
O resultado é blind SQL injection, não um dump de dados visível. Não há output de erros nem resultado de query refletido, apenas um canal lateral de timing: envolva a condição injetada em SLEEP() e uma resposta lenta significa que a condição era verdadeira. Chega para extrair dados carácter a carácter, incluindo username:password_hash da tabela de utilizadores, mas são algumas centenas de pedidos por achado, não um único clique.
O registo do CVE chama-lhe remote code execution, e existe um caminho para uma shell: partir offline a password recuperada, autenticar-se normalmente com ela e depois usar a funcionalidade de upload de plugins da própria conta para largar e pedir diretamente um ficheiro PHP. Mas essa segunda metade corre sobre precondições próprias (uma password quebrável, sem MFA, permissões install_plugins, file mods e system() ambos ativados) que nada têm a ver com qualquer dos CVEs. Vale a pena saber, mas os CVEs em si são o motivo do patch, por isso é nisso que este artigo se foca.
Versões afetadas e corrigidas
| Estado | Versões | Notas |
|---|---|---|
| Não afetado | 6.8.5 e anteriores | Confirmado via diff do patch ao nível do código |
| Vulnerável | 6.9.0 – 6.9.4 | Atualizar para 6.9.5 |
| Vulnerável | 7.0.0 – 7.0.1 | Atualizar para 7.0.2 |
| Corrigido | 6.9.5, 7.0.2, 7.1 Beta 2 | Alinha os arrays do batch e normaliza o author__not_in no limite SQL |
O que fazer agora
Existem PoCs públicos, e a maioria percorre a cadeia completa de ponta a ponta: blind SQLi, recuperação de credenciais, login, upload de plugin. Não espere por relatos confirmados in-the-wild. Se está sem patch e exposto à internet, assuma que já está a ser alvo de scans.
- Corrija todas as instâncias WordPress que administra. 6.9.x → 6.9.5, 7.0.x → 7.0.2. Os hosts geridos costumam enviar isto automaticamente, mas verifique, e não se esqueça dos microsites e ambientes de staging de cuja existência se esqueceu.
- Não consegue corrigir de imediato? Bloqueie o acesso anónimo a
/wp-json/batch/v1e?rest_route=/batch/v1no WAF como ponte de 24 a 48 horas. Pode partir integrações legítimas (editor de blocos, apps móveis), por isso remova-o no momento em que a versão corrigida estiver implementada. - Procure os IOCs específicos. Rajadas de pedidos
POST /wp-json/batch/v1quase idênticos com caminhoshttp://:malformados e um valorauthor_excludecontendoSLEEP,CHAR_LENGTHouSUBSTRING. Um diretório de plugin correspondente awp-content/plugins/wp2shell-*. Corpos de resposta contendoWP2SHELL_OUT_START. Qualquer destes significa que a cadeia foi além da divulgação de credenciais. - Se a sua password de admin alguma vez foi fraca ou reutilizada, rode-a já, tenha ou não encontrado IOCs, e ative o MFA se ainda não o fez. Esse único controlo quebra a cadeia mesmo depois de o SQLi já ter vazado o hash.
- Considere
DISALLOW_FILE_MODSem sites que não precisam de instalar plugins pela UI de administração. Remove uma das precondições de que esta cadeia depende, para este bug e para o próximo que chegue a uma sessão de admin por outro caminho.
Se quer que verifiquemos se o seu parque WordPress foi atingido antes de aplicar o patch, fale connosco.