- O GPT5.6 Sol Ultra analisou a versão estável mais recente do WordPress e, em cerca de 10 horas, montou uma cadeia de ataque que vai de uma injeção de SQL pré-autenticação até a criação de uma conta de administrador e execução remota de código (RCE)
- O ponto de partida foi uma inconsistência de índices de arrays na Batch API, disponível desde o WordPress 5.6, combinando requisições Batch recursivas para contornar a restrição de GET e a validação de parâmetros
- Após causar uma injeção UNION com a string não validada
author_exclude, a cadeia coloca um post manipulado no cache em memória e explora em sequência cache de oEmbed, changeset, referência circular e hooks - Com
user_id: 1emcustomize_changeset, obtém temporariamente privilégios de administrador e reexecuta a requisição Batch via hookparse_requestpara criar um novo administrador; depois, faz upload de um ZIP de plugin com backdoor para chegar à execução de código - O custo proporcional a 50% do uso semanal de uma assinatura mensal de US$ 200 foi de cerca de US$ 25, e cresce a possibilidade de o papel humano migrar para a direção de pesquisa em alto nível, como escolher o produto e a superfície de ataque e ajustar prompts
Processo de descoberta e condições do experimento
- Um prompt que a OpenAI disse ter usado para resolver a conjectura Cycle Double Cover foi adaptado para pesquisa de segurança e fornecido ao GPT5.6 Sol Ultra
- A versão estável mais recente do WordPress foi clonada em
main/, o diretório.gitfoi removido, e um diretório vaziothird_party/foi preparado para permitir investigar código de dependências - O prompt instruía o uso de até 4 agentes em paralelo para manter diversos caminhos de ataque por pelo menos 6 horas
- Explorando parsing de entrada, conjuntos de caracteres, uploads de arquivo, tratamento de erros, caminhos internos, serialização, cache, condições de corrida, criptografia, tipos, atribuição em massa etc.
- Registrando famílias de abordagens e redistribuindo agentes para áreas menos exploradas quando eles se concentrassem demais em uma estratégia específica
- Fazendo um agente adversarial revalidar bugs concretos e reabrindo caminhos fracassados quando surgissem novos mecanismos
- A busca foi limitada a encontrar novas vulnerabilidades no próprio código-fonte, sem usar histórico de mudanças, diferenças em relação a versões corrigidas ou pistas da internet
- Para evitar configurações irreais ou pré-condições que um atacante não poderia satisfazer, o objetivo foi definido como “RCE pré-autenticação em uma implantação de produção comum usando MySQL”
- Após cerca de 6 horas, o modelo encontrou uma injeção de SQL pré-autenticação e a reproduziu em um servidor WordPress remoto padrão, extraindo o e-mail do administrador em poucos minutos
- Ao solicitar a escalada para RCE, cerca de 4 horas depois ele completou uma cadeia que sobe de uma injeção de SQL somente leitura para privilégios de administrador, sem quebra de senha nem cálculo offline
- O tempo total de trabalho foi de pouco mais de 10 horas, consumindo 50% do uso semanal. O custo proporcional da assinatura mensal de US$ 200 foi de cerca de US$ 25
- Antes da divulgação, foi dado tempo aos operadores durante o fim de semana para atualizar o WordPress; nesse intervalo, Calif e Hacktron reproduziram independentemente a cadeia completa antes de outros PoCs no GitHub
- Instâncias em operação podem verificar se estão vulneráveis em wp2shell.com
Inconsistência de validação na Batch API
- A Batch API, introduzida no WordPress 5.6, processa várias requisições virtuais de API em uma única requisição. O endpoint em si pode ser acessado sem autenticação, mas as credenciais são repassadas a cada sub-requisição
- Uma requisição REST comum é processada na seguinte ordem
- Verifica valores obrigatórios e validade com
has_valid_params() - Sanitiza valores com
sanitize_params() - Executa o callback de permissões
- Executa o callback do endpoint
- Verifica valores obrigatórios e validade com
- Para desempenho, a Batch API separa validação e execução em dois loops
- No primeiro loop, valida e sanitiza todas as requisições
- No segundo loop, verifica os resultados de validação e executa os callbacks de permissões e de endpoint
- A implementação pressupõe que os mesmos índices em
$matches, resultado do casamento de rotas, e$validation, resultado da validação, correspondem entre si - Quando uma requisição inválida entra no ramo
is_wp_error($single_request), um item é adicionado a$validation, mas não a$matchespor causa docontinue- Depois disso, todos os itens de
$matchesficam deslocados em uma posição - É possível validar os parâmetros de uma requisição com as regras de outra e depois executá-los em um handler de endpoint que originalmente não deveria recebê-los
- Depois disso, todos os itens de
- Essa inconsistência de índices permite aplicar a um endpoint com suporte a Batch o resultado de validação de outro endpoint que não sanitiza os parâmetros
Injeção de SQL em author__not_in
GET /wp/v2/postsusa a variável interna de consultaauthor__not_inpara excluir autores específicos dos resultados- Se o valor for um array,
absinté aplicado a cada elemento para transformá-lo em inteiro; se for um valor escalar, ele é inserido como está apósimplodena cláusulaNOT INdo SQL - Em chamadas normais, o problema não aparece porque o parâmetro público
author_excludeprecisa ser um array de inteiros - Usando a inconsistência de índices da Batch, é possível validar uma string
author_excludepelas regras deDELETE /wp/v2/posts/1, que não a reconhecem, e então entregá-la aGET /wp/v2/posts - A restrição da Batch API que não permite sub-requisições GET também é contornada com chamadas Batch recursivas
- Na Batch externa, provoca-se a inconsistência de índices para pular a validação do
methodda requisição interna - Na Batch interna, provoca-se de novo a inconsistência de índices para contornar a validação de
author_exclude
- Na Batch externa, provoca-se a inconsistência de índices para pular a validação do
- Ao inserir um valor como
0) OR 1=1 --, todas as linhas de posts são retornadas, confirmando a injeção - Depois, uma injeção baseada em UNION permite criar linhas no formato de
wp_postse vazar valores arbitrários do banco de dados - Senhas, tokens de redefinição, chaves de API etc. são armazenados com hash no banco de dados; portanto, se a senha do administrador não for fraca, o vazamento de dados por si só não leva à tomada da conta
Manipulação do cache de posts dentro da requisição
- O WordPress armazena objetos
WP_Postreferenciados repetidamente dentro de uma requisição em um cache em memória para reduzir idas ao banco de dados - Ao retornar uma linha falsa de post via injeção UNION, o atacante pode colocar no cache muitos campos definidos por ele, como ID do post, tipo, status, relação de pai e conteúdo
- Como a resposta da API também pós-processa o conteúdo do post, um conteúdo manipulado pode executar caminhos de código adicionais
- Esse cache desaparece ao fim da requisição, e o post falso não existe no banco de dados; portanto, a manipulação do cache sozinha não garante persistência entre requisições
Criação de linhas no banco usando cache de oEmbed
- O recurso de embeds do WordPress insere conteúdo remoto compatível por meio da sintaxe
[embed]...[/embed]no corpo de posts - Para não enviar requisições HTTP toda vez, o resultado é armazenado no banco de dados como um post do tipo
oembed_cacheemwp_posts - Ao embutir um post local do WordPress por caminho relativo, a requisição HTTP é omitida, e não há verificação de que o ID do post referenciado realmente exista
- Mesmo ao embutir um post local inexistente como
/?p=10, uma linhaoembed_cachepara esses dados pode ser criada - Se a linha criada for consultada novamente via injeção de SQL enquanto o tipo de post em memória é manipulado para
postetc., as representações no banco de dados e no cache ficam diferentes - Ao reconciliar essas duas representações, o WordPress chama
wp_update_post()e, com exceção deIDepost_contentexplicitamente especificados, prefere os valores em memória criados pelo atacante - Com isso, uma linha
oembed_cachepode ser convertida em post comum, mas nessa chamada opost_contenté sobrescrito pelo resultado do embed, então o atacante não controla também o corpo
Privilégios temporários de administrador via customize_changeset
- Rascunhos de personalização de tema são armazenados em
wp_postscomo posts especiais do tipocustomize_changeset - O
post_contentcontém JSON com valores alterados por chave de configuração, tipos e o ID do usuário que realizou a alteração - Ao aplicar um changeset, o WordPress lê o
user_idde cada item e altera temporariamente o usuário atual comwp_set_current_user() - Se um atacante aplicar um changeset com
user_id: 1, pode usar momentaneamente a identidade de administrador mesmo durante uma requisição anônima - Como a chamada de reconciliação do oEmbed mencionada acima sobrescreve
post_content, é necessário um caminho separado de chamada awp_update_post()para preservar o JSON malicioso do changeset
Preservação do conteúdo usando ciclo no pai do post
- Posts do WordPress podem ter um único pai, mas uma estrutura circular em que o próprio post ou um descendente seja definido como pai não é permitida
- O filtro
wp_insert_post_parentpercorre a hierarquia de pais para verificar ciclos e, ao encontrar um, chamawp_update_post()para alterar opost_parentdaquele post para 0 - Essa segunda chamada especifica apenas
IDepost_parent, sem sobrescreverpost_content - Ao usar a injeção de SQL para disfarçar um post em memória como um
customize_changesetcujo pai é ele mesmo, o processo de recuperação do ciclo grava no banco de dados o JSON malicioso de changeset especificado pelo atacante - Ao criar um changeset com data passada e status
future, o WordPress o aplica e, por causa deuser_id: 1, executa as alterações especificadas com privilégios de administrador - Os privilégios de administrador se mantêm apenas durante a operação do changeset e voltam a ser de convidado após a conclusão
Reexecução de toda a requisição por hook dinâmico
- Hooks do WordPress se dividem em ações (actions) e filtros (filters), permitindo que plugins intervenham em vários pontos do ciclo de vida, como login, publicação e registro de scripts
- Quando o status de um post muda, o WordPress executa uma ação dinâmica no formato
"{$new_status}_{$post->post_type}"- Para um post normal, o nome seria algo como
publish_post - Um post falso em memória pode ter status e tipo arbitrários, permitindo construir o nome de uma ação desejada que contenha ao menos um sublinhado
- Para um post normal, o nome seria algo como
- Os argumentos do hook se limitam ao ID do post e ao objeto
WP_Postdefinidos pelo atacante, então é difícil invocar diretamente uma ação arbitrária de forma útil - A cadeia de ataque manipula o status para
parsee o tipo pararequest, chamando o hookparse_request - Como
parse_requesté um hook executado no início do ciclo de vida da requisição, chamá-lo de novo faz com que a requisição original da Batch API seja reprocessada desde o início - O reprocessamento ocorre enquanto a identidade temporária de administrador definida pelo changeset ainda está ativa, de modo que requisições exclusivas de administrador que falharam na primeira execução passam a ter sucesso na segunda
Cadeia RCE concluída com duas requisições
- O exploit final usa duas requisições HTTP e atribui aos posts falsos IDs grandes o bastante para não colidirem com posts reais
-
Primeira requisição: preparar linhas persistentes
- Retorna via injeção de SQL um post falso com três embeds locais, criando 3 linhas
oembed_cachecorrespondentes aO,CeD - Os três embeds apontam para o mesmo post
S, mas usam query strings diferentes para gerar hashes distintos de cache oEmbed
- Retorna via injeção de SQL um post falso com três embeds locais, criando 3 linhas
-
Segunda requisição: montar seis posts
- Constrói os seis posts falsos seguintes no cache em memória
O: cache antigo com status/tipopublish/oembed_cachee paiCC: status/tipofuture/customize_changeset, tendo a si mesmo como pai e contendo JSON malicioso de changesetP:draft/pagecom paiDD: status/tipoparse/request, tendo a si mesmo como paiS:publish/postque fornece os dados do embedT:publish/postcontendo o embed externo- O embed de
TconsultaOe, por causa do horário de modificação antigo, força a atualização do cache deS - Quando a atualização de
Odetecta o ciclo no paiC, ela altera o pai deCpara 0 e grava no banco ocustomize_changesetem memória com o JSON malicioso - O changeset
futurecom data passada é aplicado, publicandoPcom a identidade de administrador deuser_id: 1 - A atualização de
Pencontra o ciclo no paiD, gravaDe, com o status e tipo manipulados, chama a açãoparse_request - A requisição Batch já inclui desde o início uma requisição para criar um novo administrador
- No primeiro processamento, ela falha por estar com privilégios de convidado
- Ao ser reexecutada por
parse_request, a permissão temporária de administrador ainda está ativa e ela tem sucesso - Após fazer login com a nova conta de administrador, o upload de um ZIP de plugin com backdoor leva finalmente à execução remota de código
Mudança de papéis na pesquisa de segurança com IA
- As etapas especialmente criativas da cadeia completa foram contornar a restrição de GET com chamadas Batch recursivas, combinar cache e changeset para obter privilégios de administrador, e chamar
parse_requestcom posts falsos para reexecutar a requisição - Embora não seja possível afirmar uma superioridade geral, a avaliação é que teria sido impossível para um pesquisador de segurança descobrir e completar a mesma cadeia em 10 horas sem IA
- Mesmo que o bug original da Batch fosse fornecido previamente, há dúvidas se seria possível construir uma RCE nesse prazo
- A conclusão é que o GPT5.6 Sol Ultra avançou muito em relação ao GPT5.5 na capacidade de encontrar vários gadgets de código separados e conectá-los em uma única cadeia
- À medida que o modelo assume mais do desenvolvimento técnico de exploits, as pessoas tendem a se concentrar em decidir o produto e a superfície de ataque a investigar, o tempo a investir, o rumo da pesquisa e em corrigir o modelo quando ele se desvia
- Essa competência de meta-pesquisa ainda não é bem tratada pela IA e tende a se tornar mais importante conforme aumenta a capacidade técnica dos modelos
1 comentários
Comentários do Hacker News
Não há base para afirmar que US$ 500 mil foram ou seriam pagos por esse tipo de exploit. Como o texto diz que o prompt foi ajustado cuidadosamente como se fosse uma bíblia, talvez fosse melhor vender esse prompt por US$ 500 mil
O autor trabalha na https://www.assetnote.io/, que oferece um produto de IA para varredura automática
Esses brokers geralmente não pagam uma quantia enorme de uma vez; eles vendem o acesso a atores estatais e vão pagando em parcelas enquanto o bug não é corrigido. Como a estrutura existe para evitar revenda ou esgotamento precoce, provavelmente quase ninguém poderá confirmar se alguém realmente recebeu o valor total por uma vulnerabilidade parecida
O exemplo mais próximo são adolescentes da Flórida que colocaram malware em jogos da Steam para roubar contas e acabaram pegos. Seriam pegos de qualquer forma, mas teria demorado muito mais se não tivessem se exibido nas redes sociais
Olhando https://github.com/WordPress/WordPress/commit/3a640e1c5e39aa..., vemos injeção de SQL por concatenação de strings em 2026
Em vez de executar SQL diretamente, mandam usar
dbDelta, mas exigem regras de formatação absurdamente rígidas, como colocar cada campo em uma linha, usar dois espaços entrePRIMARY KEYe escreverKEYem vez deINDEX. Não se pode usar aspas nem crases nos nomes dos campos, os tipos devem ficar em minúsculas, as palavras-chave SQL em maiúsculas, e todos os parâmetros de tamanho precisam ser especificadossprintfHavia payloads no
POSTe noGETno formato/wp/v2/widgets?author_exclude=1%29+AND+1%3D0+UNION+ALL+SELECT...Principal Software Engineer @ Bluehost,WordPress Core Committer; com um código desses, a palavra “principal” soa estranha mesmoJá cansei desse estilo de escrita FOMO. Não foi descoberto só com US$ 25; havia conhecimento especializado da área sobre onde olhar e como explorar, além de anos de material acumulado
Precisamos parar de espalhar essa narrativa de aposta e a ilusão de que todo mundo está perdendo a oportunidade
A parte surpreendente é o alto preço de uma vulnerabilidade já conhecida, e isso talvez nem seja verdade. O WordPress é frequentemente chamado de shell root remoto com funcionalidade de blog
Entendo que, para usuários comuns, é mais fácil orientar arrastar e soltar do que mandar fazer commit num repositório GitHub e compilar com Hugo. Mas, do ponto de vista de segurança, a estrutura fica só esperando surgir uma vulnerabilidade no core ou em um dos milhares de plugins para abrir uma execução remota de código como serviço
Texto interessante, e a descoberta e divulgação de exploits baseadas em LLM são uma preocupação real. Já consegui fazer um modelo gerar relativamente rápido código de escape de contêiner a partir de uma vulnerabilidade de escalonamento local de privilégio no Linux
Ainda assim, surpreende que o GPT-5.6 não tenha bloqueado o prompt por causa dos guardrails. O GPT-5.5 ou superior tende a evitar trabalho ofensivo de segurança, como Opus 4.7+/Fable, então parece possível que o autor tenha obtido da OpenAI uma aprovação de cibersegurança com guardrails relaxados
Ferramentas de teste estático de segurança de aplicações (SAST) não baseadas em IA, de antes de 2020, também detectariam muitas dessas injeções de SQL, e isso ao menos deveria ter sido encontrado em revisão de código. Fico me perguntando se o WordPress não usa revisão de código nem SAST
Um dos meus sites foi invadido por causa dessa vulnerabilidade, mas felizmente era um lugar sem usuários
O invasor criou duas contas de administrador no banco de dados e instalou um web shell de execução remota de comandos em
wp-content/plugins/wp-core, chamadowp-core-[12 caracteres aleatórios].php. Emmu-plugins, deixou o backdoorfirewall.php, que cria um administrador comGET ?sergei, e também adicionou o backdoorcache-seo-helper.php; além disso, usoufixer.phppara alterar o número da versão do WordPress para que parecesse uma versão corrigida. No fim, decidi abandonar o uso do WordPressNo fim do texto, começou a se referir às postagens com nomes estranhos, o que dificultou o entendimento. Fiquei me perguntando por que um ID foi feito como
Oe outro como0, e por que usou um único caractere e algo aparentemente aleatório comoOCPDST, em vez deEMBED_01ouABCDEFOsignificapublish/oembed_cache,Csignificafuture/customize_changeset,Psignificadraft/page,Dsignificaparse/request,Ssignificapublish/postque fornece os dados de embed, eTsignificapublish/postque contém o embed externoFico me perguntando se estão assumindo que as pessoas dispostas a pagar 500 mil dólares não têm capacidade de usar o GPT-5.6 diretamente
Eu também encontro vulnerabilidades de segurança com LLMs, mas não dá para simplesmente enviar o resultado como está e encerrar por aí; embora muita gente tente fazer isso
Se o GPT-5.6 Sol é sobre-humano não é uma pergunta simples de sim ou não. Os computadores já superam os humanos no xadrez há décadas e, por este texto, agora parecem ter superado os humanos também em compreensão de código