Segurança

PHP2Shell: a vulnerabilidade crítica no motor do PHP que pode comprometer metade da web

CVE-2026-109618 e CVE-2026-109619 (PHP2Shell) são duas falhas críticas de corrupção de memória no próprio motor PHP, com CVSS 9.2, que permitem RCE não autenticado por um simples POST — independentemente da aplicação. Mecanismo, exploração em estado real e como verificar se estás vulnerável.

Duas falhas críticas no próprio motor do PHP — não numa aplicação específica — permitem execução remota de código não autenticada com um único pedido HTTP. Afetam WordPress, Drupal, Joomla e qualquer outra coisa escrita em PHP, independentemente de o código da aplicação estar certo.


No dia 21 de outubro de 2026 o projeto PHP publicou correções de emergência para duas vulnerabilidades críticas de corrupção de memória, rapidamente apelidadas PHP2Shell. Não são bugs de um CMS ou de um plugin: são bugs no interpretador PHP em si, no código que processa qualquer pedido HTTP antes de uma única linha da aplicação ser executada.

Isto importa-te mesmo que não escrevas PHP. Se alojas, giras ou simplesmente usas um site WordPress, Drupal, Joomla, Magento ou qualquer aplicação PHP — a vulnerabilidade está por baixo da aplicação, não dentro dela.

O que é o PHP2Shell

CVE-2026-109618 e CVE-2026-109619 são duas falhas distintas, com CVSS v4.0 de 9.2 cada, classificadas como CWE-824 (acesso a ponteiro não inicializado/pendente). Ambas permitem a um atacante não autenticado executar comandos arbitrários no servidor web através de HTTP simples — sem sessão, sem conta, sem precisar de um endpoint vulnerável da aplicação.

A causa raiz comum, segundo a análise da CrowdStrike, está em como o PHP trata condições fatais: quando o interpretador encontra um erro fatal — esgotar o memory_limit, exceder o max_execution_time, perder a ligação ao cliente — a rotina interna zend_bailout() usa um longjmp para abortar o pedido imediatamente, saltando a limpeza normal de memória. As estruturas internas ficam num estado inconsistente, mas o código de encerramento assume que estão íntegras.

A partir daqui as duas CVEs seguem caminhos diferentes:

  • CVE-2026-109618 (dentro do mesmo pedido): ao inserir uma chave num array, o PHP incrementa o contador de elementos antes de alocar memória para essa chave. Se a alocação ultrapassar o memory_limit nesse instante exato, a entrada do array fica com bytes residuais do heap em vez de um ponteiro válido. Na fase de encerramento, o PHP lê essa entrada corrompida e trata esses bytes como se fossem um valor com contagem de referências — desreferenciando um "ponteiro" cujo conteúdo o atacante pode influenciar moldando o tamanho dos campos enviados no corpo do pedido POST. O único rasto típico no log é um erro fatal de memória com a assinatura in Unknown on line 0, que indica que a falha ocorreu antes de qualquer script da aplicação correr.
  • CVE-2026-109619 (entre pedidos, em workers persistentes): um erro fatal interrompe uma operação de serialização ou de sessão antes da limpeza. Um ponteiro global do PHP fica a apontar para memória já libertada, mas o contador de aninhamento continua a indicar que a operação está "ativa". Em modelos de worker persistente — como o mod_php do Apache — o próprio processo PHP sobrevive ao pedido que falhou. O pedido seguinte, no mesmo worker, reutiliza esse ponteiro pendente. Como o atacante também controla esse segundo pedido, consegue colocar dados próprios na memória entretanto reaproveitada. A funcionalidade usada para desencadear isto — session.upload_progress — está ativa por omissão desde o PHP 5.4.0.

A CrowdStrike afirma ter verificado, de ponta a ponta, que um único pedido POST anónimo é suficiente para que um worker mod_php do Apache arranque um processo de shell, em arquiteturas x86-64 e ARM64.

Há ainda uma nuance de configuração: segundo a mesma análise, a falha só fica praticamente alcançável quando memory_limit é relativamente baixo face a post_max_size — algo próximo de memory_limit < 4 × post_max_size. Os valores por omissão do PHP (8 MB e 128 MB) não cumprem essa condição, mas combinações comuns em alojamento partilhado (por exemplo 64 MB e 128 MB) cumprem-na perfeitamente.

Importa notar que a autoria original da descoberta não está totalmente esclarecida. Parte da imprensa de segurança atribui a investigação de base à Wiz Research; outras fontes (Tanium, SecurityWeek) referem que os detalhes técnicos vazaram publicamente antes da coordenação prevista com o projeto PHP, num site externo, e que a watchTowr afirma ter reproduzido de forma independente a CVE-2026-109618 sem detalhar o método. Trata-se de uma divulgação atribulada, com relatos que nem sempre coincidem entre si.

Quem está afetado

Toda a gente que corre PHP. Literalmente. Como a falha está no motor e é acionada antes de qualquer código da aplicação compilar, qualquer endpoint PHP de um servidor exposto é superfície de ataque — não importa se é WordPress, um script caseiro de 20 linhas ou uma API em Laravel.

Ramo Versões afetadas Corrigida em
PHP 8.2 anteriores a 8.2.35 8.2.35
PHP 8.3 anteriores a 8.3.36 8.3.36
PHP 8.4 anteriores a 8.4.27 8.4.27
PHP 8.5 anteriores a 8.5.12 8.5.12
PHP ≤ 8.1, 7.x sem correção oficial fim de vida — migrar

As correções saíram a 21 de outubro de 2026. Relatos públicos (sem causa-raiz totalmente confirmada) sugerem que o código afetado existe desde o PHP 5 — mas só os quatro ramos com suporte ativo receberam patch. Se ainda tens PHP 7.4, 8.0 ou 8.1 em produção, não há correção oficial à espera: a única opção séria é migrar para um ramo suportado.

Foi demonstrada RCE não autenticada contra instalações WordPress, Drupal e Joomla — não porque estes CMS tenham um bug próprio, mas porque corriam em PHP vulnerável.

Exploração em estado real

A cronologia é preocupante:

  • Detalhes técnicos vazaram antes da data de coordenação prevista para a divulgação, obrigando o projeto PHP a antecipar o lançamento das correções.
  • A Amazon reportou ter detetado tentativas de exploração quatro dias antes das correções estarem disponíveis.
  • A watchTowr registou exploração ativa na sua rede global de honeypots já a 21 de outubro.
  • A Patchstack recebeu relatos de parceiros de alojamento com incidentes de exploração antes do lançamento oficial do patch (até às 07:34 UTC do dia 21).
  • A Cloudflare (CTO Dane Knecht) confirmou ataques precoces e publicou regras de WAF contra vetores conhecidos.
  • Na Holanda, organizações — incluindo o NCSC — retiraram sites do ar por precaução, depois de uma PoC ter vazado antecipadamente.
  • A Sansec previu exploração em massa a começar cerca de 15 minutos depois da publicação dos patches — o tempo que leva a fazer reverse engineering ao diff do código.
  • A Modat e a VirusTotal contabilizaram milhões de serviços/IPs a correr PHP, mas a maioria das versões visíveis é de ramos já sem suporte (ex.: PHP 7.4) — estes números não confirmam quantos hosts estão realmente vulneráveis.

Até à data de publicação deste artigo, não há IOCs (indicadores de comprometimento) públicos e confirmados. Existem PoCs publicadas no GitHub (por exemplo poc0_109618.py e poc1_109619.py, atribuídas a J4ck3LSyN), mas a Insikt Group da Recorded Future não as validou de forma independente, e o próprio autor de uma delas já invalidou a teoria em que se baseava. Não publicamos aqui nenhum exploit funcional — pelas razões que explicamos a seguir.

Como funciona o ataque (sem exploit funcional)

É propositado não publicarmos código de exploração pronto a correr. Não porque o mecanismo seja secreto — já está descrito em detalhe por várias empresas de segurança, e resumimos acima — mas porque:

  1. As PoCs públicas conhecidas até agora são de fiabilidade duvidosa (uma delas assenta numa premissa que o próprio autor já desmentiu);
  2. Explorar com sucesso exige moldar o conteúdo residual do heap de forma determinística, o que depende de detalhes de alocador, arquitetura e configuração — não é um "copy-paste";
  3. A exploração em massa já está em curso; publicar mais um exploit funcional só acrescentaria combustível, sem valor defensivo adicional.

Dito isto, para entenderes o que procurar nos teus logs, a sequência conceptual de um ataque à CVE-2026-109619 (o vetor mais documentado) é:

Pedido 1 — "envenenar" o worker
  POST /qualquer-endpoint.php HTTP/1.1
  Content-Type: multipart/form-data; boundary=...

  [campo com PHP_SESSION_UPLOAD_PROGRESS a forçar
   uma falha fatal a meio da serialização da sessão —
   p. ex. upload interrompido / limite de tempo excedido]

  → o worker mod_php sobrevive à falha, mas fica com
    um ponteiro global a apontar para memória já libertada

Pedido 2 — disparar o use-after-free, no MESMO worker
  POST /qualquer-endpoint.php HTTP/1.1

  [corpo desenhado para realocar exactamente o bloco
   de memória libertado no pedido 1, com bytes que o
   PHP volta a interpretar como estruturas internas —
   incluindo, no cenário demonstrado pela CrowdStrike,
   um ponteiro para exec/system]

  → o processo PHP arranca um shell

Não há aqui nenhum payload funcional: faltam de propósito os valores exactos de boundary, os tamanhos de campo, o offset do heap e o shellcode/comando final — é precisamente a parte que exige engenharia inversa cuidadosa ao binário/versão alvo, e que não vamos facilitar.

A CVE-2026-109618 é mais simples na forma (um único pedido, sem precisar de reutilizar worker), mas exige controlar com precisão o tamanho do nome de um campo de array no corpo POST para que a falha de alocação de memória aconteça exactamente no momento certo — novamente, algo que depende da configuração (memory_limit/post_max_size) e do alocador do Zend Memory Manager da versão em causa.

Como verificar se o teu sistema está vulnerável

1. Confirma a versão do PHP

# Linha de comandos
php -v

# Servidor web (se exposto — considera desativar isto em produção)
curl -sI https://teu-site.exemplo | grep -i x-powered-by

# Pacote do sistema (Debian/Ubuntu)
dpkg -l | grep -E '^ii\s+php[0-9.]*-?(fpm|cli)?\s' 

# RHEL/Fedora
rpm -qa | grep -i ^php

# Dentro de um contentor, pela tag da imagem
docker inspect <container> --format '{{.Config.Image}}'
Copy

Confirma contra a tabela de versões corrigidas acima. Atenção a cópias de PHP "embutidas" que o php -v da shell não vê: XAMPP, WampServer, Laragon no Windows, PHP compilado à mão por uma aplicação de terceiros, ou uma imagem Docker com tag fixa que ninguém atualizou.

2. Verifica a configuração de risco

php -i | grep -E '^(memory_limit|post_max_size|session.upload_progress.enabled)'
Copy

Se memory_limit for inferior a cerca de 4× o post_max_size, a combinação está dentro da janela de configuração que a CrowdStrike descreve como mais facilmente explorável para a CVE-2026-109618. Isto não significa que combinações fora dessa janela estejam seguras — é apenas um sinal de risco adicional, não uma prova de segurança.

3. Procura sinais nos logs (deteção, não confirmação)

Não há IOCs públicos confirmados, mas vale a pena rever, desde 20 de outubro de 2026:

  • Erros fatais de PHP com a assinatura PHP Fatal error: ... in Unknown on line 0 (indica falha antes de qualquer script correr — consistente com a CVE-2026-109618);
  • Crashes ou reinícios inesperados de workers php-fpm ou mod_php;
  • Pedidos multipart/form-data anómalos ou malformados, sobretudo com campos de PHP_SESSION_UPLOAD_PROGRESS;
  • Processos filho inesperados gerados a partir do PHP ou do servidor web (sh, bash, binários desconhecidos) — um EDR com deteção comportamental apanha isto mesmo sem assinatura específica da vulnerabilidade;
  • Picos de erros 500 sem alteração de código correspondente.

Como a exploração ocorre abaixo da camada de aplicação, os logs da aplicação (framework, CMS) podem não registar nada de anómalo — a investigação tem de olhar para os logs do servidor web e do gestor de processos PHP.

Mitigação

  1. Atualiza o PHP. É a única correção completa. Prioriza sistemas expostos à internet. Depois de atualizar, reinicia o PHP-FPM e o servidor web — o binário novo só entra em efeito depois do reinício dos workers.
  2. Sem patch imediato disponível?
    • Restringe o acesso externo ao que for possível (rede, firewall, Cloudflare/WAF — a Cloudflare e a VulnCheck já publicaram regras contra os vetores conhecidos).
    • Desativa parcialmente um dos vetores: session.upload_progress.enabled=0, definido no php.ini ou num pool do FPM (o ini_set() e o .user.ini não funcionam para esta diretiva — tem de ser configuração de arranque, com reinício a seguir). Isto mitiga apenas parte da CVE-2026-109619; não tem qualquer efeito sobre a CVE-2026-109618.
    • Nenhuma destas medidas substitui a atualização — são só para ganhar tempo.
  3. Ramos em fim de vida (PHP ≤ 8.1): não têm correção oficial. Planeia a migração — não há mitigação de configuração que resolva isto de forma fiável.
  4. Depois de corrigir: se encontraste algum dos sinais da secção anterior num sistema que esteve exposto e sem patch, trata-o como potencialmente comprometido — ativa resposta a incidentes e roda credenciais, tokens e chaves de API que possam ter sido expostos pela fuga de memória (a vulnerabilidade também permite ler memória de outros pedidos, mesmo sem chegar a RCE).

Conclusão

O PHP2Shell não é "mais um CVE de WordPress". É uma falha no motor que serve uma fração enorme da web — e a janela entre o vazamento dos detalhes e a disponibilidade do patch já bastou para exploração em estado real antes da correção oficial. Se giras alguma coisa em PHP, a prioridade é simples: confirma a versão, atualiza, reinicia os workers, e só depois te preocupes com mitigações de configuração.

Não vamos publicar um exploit pronto a usar — o mecanismo já está descrito com detalhe suficiente para quem precisa de o entender para se defender, e mais do que isso só ajudaria quem já está a tentar atacar.


Fontes

Partilhar

Comentários 0

Ainda sem comentários — sê o primeiro.