
Análise e implementação de ponta a ponta da vulnerabilidade RCE corrigida do WordPress - CVE-2026-60137 e CVE-2026-63030

todos os PoCs exploram os mesmos dois bugs - a confusão de rotas do REST batch (CVE-2026-63030) e a injeção SQL de author__not_in (CVE-2026-60137) - com o mesmo formato de batch duplamente aninhado. O que difere entre eles é o caminho de RCE escolhido, as pré-condições de ambiente e os padrões de segurança. Este documento indica, concretamente, onde a implementação deste repositório se situa nesse panorama.
checkshell| Capacidade | Este repositório | Icex0/wp2shell-poc | sergiointel/wp2shell-poc | 0xsha/wp2shell | Variante OUTFILE [4] |
|---|---|---|---|---|---|
| Leitura SQLi cega/por timing pré-autenticação | sim | sim | sim (timing) | sim | sim |
| Leitura UNION in-band (1 requisição/valor) | sim | sim | - [1] | - [1] | - |
| Leitura baseada em erro (EXTRACTVALUE) | sim | sim | - | - | - |
| Canal UNION sobrevive ao cache de objetos persistente | sim (base esvaziada) | não - falsos negativos na sonda [2] | não documentado | não - pré-condição documentada [3] | n/a [5] |
| RCE pré-autenticação sem quebra de hash | sim (ponte SQLi-para-admin) | sim (mesma ponte) | sim (origem da ponte) | sim (mesma ponte) | sim, via INTO OUTFILE [5] |
| Pré-condições extras para RCE | nenhuma além da instalação padrão | nenhuma (em hosts sem cache de objetos) | nenhuma (mesma) | nenhuma (mesma) | privilégio FILE do MySQL + caminho gravável pela web compartilhado com o mysqld |
| Verificação não destrutiva / validação de correção | sim (tripleto de marcadores; sem payload por padrão) | sim | não | sim (block_cannot_read) | sim (batch de marcadores) |
| Atribuição/marcação de User-Agent | sim, em todos os comandos | não | não | flag de transporte | não |
| Limpeza automática (webshell + admin gerado) | sim | sim | não documentado | apenas webshell com proteção por token | dropper removido [5] |
| Guia de detecção para blue team | sim, a partir de uma execução em produção | não | não | matriz de laboratório em vez disso | notas de mitigação |
| Dependências | somente stdlib | somente stdlib | arquivo único | somente stdlib, arquivo único | pacote Python ≥3.10 |
[1] Apenas timing/cego como canal de leitura; a primitiva de fake-post UNION existe dentro da ponte, mas não é exposta como oráculo de extração.
[2] A sonda ingênua de disponibilidade (0) UNION SELECT …) é silenciosamente descartada durante a hidratação do cache de objetos, available() retorna falso, e toda a ponte pré-autenticação é abortada - veja §1.
[3] O próprio README do projeto lista "sem cache de objetos persistente (Redis/Memcached)" em Pré-condições.
[4] Variante pública espelhada no Sploitus (link abaixo): leitura cega mais um dropper INTO OUTFILE como etapa de RCE, usando um carrier de categorias per_page=-1.
[5] O caminho de RCE por OUTFILE não depende da renderização de fake-posts, então caches de objetos não o bloqueiam - quem o bloqueia são o privilégio FILE do MySQL e um diretório gravável compartilhado. Hospedagem gerenciada quase nunca concede FILE ao usuário do banco do WordPress, e secure_file_priv costuma estar definido.
A primitiva de fake-post UNION depende de como o WP_Query retorna linhas:
wp_posts; uma linha injetada via UNION torna-se um WP_Post diretamente. A falsificação é renderizada.Em hosts com cache de objetos persistente, uma base de resultados populada coloca o WP_Query em modo dividido. A sonda padrão usada pelos PoCs públicos -
0) UNION SELECT <forged row> -- -
post_author NOT IN (0) corresponde a todas as linhas), então atrás de um cache de objetos a linha forjada evapora: a sonda de disponibilidade gera falsos negativos, available() retorna falso, e toda a ponte pré-autenticação é reportada como "morta" em um host que, na verdade, é totalmente explorável. O unificador público documenta o mesmo limite ao listar "sem cache de objetos persistente" como uma pré-condição rígida.Este repositório esvazia a base de resultados em vez disso:
1) AND 1=0 UNION ALL SELECT <forged row> -- -
Com zero linhas na base, a linha forjada é a única linha; a consulta permanece em modo de linha completa; nenhuma busca de hidratação é executada. Uma única palavra-chave injetada (AND 1=0) é toda a diferença entre "canal UNION morto" e "RCE pré-autenticação completo" em hosts com cache de objetos - que são a maioria dos ambientes gerenciados de WordPress em produção. O diagnóstico, a matriz de sondas (per_page × forma de injeção).
Nota de escopo: o canal de leitura cego/por timing não é sensível ao cache de objetos (contar linhas no SQL não envolve hidratação de fake-posts), então a leitura cega de todos os PoCs funciona em qualquer lugar. O que o cache de objetos mata nos outros PoCs é especificamente a parte dependente de UNION: a extração in-band e a ponte SQLi-para-admin.
Uma segunda lição relacionada, documentada no estudo de caso: quando ambos os canais funcionam, trate a leitura UNION in-band como autoritativa - o oráculo de timing em produção produziu inversões de bits sob jitter em um valor que a leitura in-band resolveu de forma inequívoca.
Existem três caminhos de RCE pré-autenticação entre os PoCs públicos:
| Caminho | Usado por | Pré-condições extras |
|---|---|---|
Ponte SQLi-para-admin (forjar linhas oEmbed/changeset/nav → POST /wp/v2/users → login → upload de plugin) | este repositório, sergiointel (origem), Icex0, 0xsha | nenhuma além de uma instalação padrão |
Dropper INTO OUTFILE (escrever um arquivo PHP via SQLi, buscá-lo para obter um shell) | Variante OUTFILE [4] | privilégio FILE do MySQL, secure_file_priv permitindo-o, e um diretório gravável pelo mysqld e servido pelo servidor web |
Recuperação de hash → quebra → login (despejar user_pass, quebrar offline, depois upload de plugin) | todos (como fallback) | o hash bcrypt precisa realmente ser quebrado ($wp$2y$, hashcat -m 35500) - lento, muitas vezes nunca |
Este repositório implementa a ponte: ela não exige privilégios de banco além do que o WordPress já tem, funciona quando as camadas de banco e web não compartilham nada, e não deixa nenhum arquivo para o caminho baseado no privilégio FILE depender. O trade-off é a complexidade - a ponte é um grafo de posts envenenados de sete linhas - que é exatamente onde o falso negativo de cache de objetos da §1 costumava esconder o caminho.
Construído para execução contra sistemas de produção sob autorização, não apenas laboratórios:
check é não destrutivo por padrão - fingerprint passivo mais um batch de marcadores benigno; nenhum payload SQL é enviado a menos que --confirm-sqli seja fornecido. Após a correção, o tripleto de marcadores que desaparece serve também como validação da correção.--user-agent em todos os comandos para que todo o tráfego de exploração seja identificável nos logs (uma regra prática de engajamentos que as ferramentas públicas não adotam por padrão).author_exclude → author__not_in, a primitiva UNION de WP_Post falso e o conceito de ponte via customizer são todas técnicas públicas (linhagem reconhecida abaixo).INTO OUTFILE), espelhada no Sploitus - https://sploitus.com/exploit?id=7CD079AD-E27B-5C54-A696-60635BFDB241