PHP2Shell: RCE crítico no interpretador PHP (CVSS 9.2)

Resumo para quem decide

O time de segurança do PHP lançou patches de emergência para duas vulnerabilidades críticas chamadas em conjunto de PHP2Shell: CVE-2026-109618 e CVE-2026-109619. Ambas são execução remota de código sem autenticação, CVSS v4 9.2, e atingem o próprio interpretador PHP, não um plugin ou framework específico. O atacante ganha execução de código com a conta do processo web. As falhas foram descobertas por pesquisadores da Calif, da OpenAI e da Wiz e afetam toda instalação PHP que atende requisições web: PHP-FPM, Apache com mod_php, php-cgi, FrankenPHP e LiteSpeed.

As versões afetadas são todas as anteriores a 8.2.35, 8.3.36, 8.4.27 e 8.5.12. A correção definitiva é atualizar para uma dessas versões. Não existe mitigação de configuração que elimine o risco por completo: os próprios avisos oficiais do PHP afirmam que atualizar é “a única mitigação completa e confiável” (GHSA-w8hw-w9fx-j79w, GHSA-6wmf-4rv5-p3rp).

O CTO da Cloudflare, Dane Knecht, confirmou que a empresa já observava tentativas iniciais de exploração contra as duas falhas antes mesmo do lançamento do patch, e que as regras de WAF implantadas pela Cloudflare são proteção temporária, não substituto do patch (BleepingComputer). Isso muda a prioridade: não é um patch para agendar na próxima janela de manutenção, é para aplicar agora.

PHP roda entre 75% e 80% de todos os sites do mundo. Segundo estimativas do VirusTotal citadas pela BleepingComputer, mais de 5,1 milhões de hosts PHP estão expostos online, com cerca de 3,8 milhões tendo pelo menos uma porta aberta. Se sua infraestrutura inclui WordPress, Joomla, Drupal ou qualquer aplicação PHP própria, trate isso como prioridade máxima de patch desta semana.

O que é o PHP2Shell

As duas falhas compartilham a mesma raiz técnica: o PHP pode deixar a memória do processo em estado corrompido quando uma requisição termina de forma anormal. A diferença está em qual caminho leva até essa corrupção.

CVE-2026-109618: corrupção de memória por falta de memória (OOM)

Quando o PHP esgota memória disponível (out of memory), ele historicamente usa a função longjmp() para abortar o processamento da requisição. O problema é que, nesse momento, a memória da requisição já está parcialmente acessível antes de estar totalmente inicializada. Se a falha de alocação acontece exatamente nessa janela, a memória fica num estado inválido que é lido e processado mais tarde pelo shutdown da requisição, resultando em use-after-free e ponteiro não inicializado.

O que torna essa falha perigosa é que existem vetores documentados para forçar OOM de forma não autenticada: uma requisição multipart sem content-length declarado contorna a checagem inicial de post_max_size; número ilimitado de headers por parte multipart consome memória desproporcional; e campos de upload vazios não contam para o limite max_file_uploads, mas ainda geram arrays de metadados que podem ser multiplicados por nomes de campo aninhados (GHSA-w8hw-w9fx-j79w).

A exploração completa foi validada contra Apache mod_php em configuração prefork padrão com múltiplos workers, e o RCE acontece antes de qualquer código da aplicação rodar. Para FPM, a equipe do PHP diz que “análise de código-fonte sustenta FPM como suscetível”, mas nenhum exploit completo nesse SAPI foi demonstrado. FrankenPHP é classificado como “provavelmente suscetível” por análise de código, e o LiteSpeed (LSAPI) como candidato condicional, também sem exploit validado.

A correção remove o uso de longjmp() para tratar OOM. No lugar, o PHP passa a usar um soft OOM adiado até o próximo ponto seguro da VM, e um hard OOM imediato via _exit() em um threshold mais alto.

CVE-2026-109619: estado global obsoleto entre requisições (stale global state)

Esta segunda falha explora um padrão diferente: estados globais do worker PHP (de erro, sessão, objeto ou extensão) que pertencem a uma requisição específica, mas não são resetados antes da memória ser liberada. Uma operação seguinte, no mesmo worker, acaba lendo ou liberando esse estado obsoleto antes da inicialização normal da próxima requisição, o que gera use-after-free. Com um layout de heap manipulado, isso vira RCE completo.

A exploração segue um modelo de producer/consumer: o producer é a operação que publica o estado de uma requisição em um local global; o consumer é a operação subsequente, no mesmo worker, que lê ou libera esse estado antes do reset normal (GHSA-6wmf-4rv5-p3rp).

Dois caminhos foram confirmados pela equipe do PHP:

  1. Em PHP 8.5, com fatal_error_backtraces habilitado, o reset do estado de backtrace é sempre perdido no shutdown. Exploração confirmada contra mod_php.
  2. No ciclo de vida de session upload progress: um OOM ou outro erro fatal durante o processamento de upload pode deixar globais de sessão apontando para heap já liberado; uma operação posterior de upload progress consome esse estado. Validado contra mod_php e FPM, inclusive com pool FPM dinâmico padrão. O detalhe que importa: session.upload_progress vem habilitado por padrão e não exige que a aplicação chame session_start() explicitamente.

Um ponto que os advisories destacam de forma explícita: mudar error_reporting, usar @ para suprimir erros, ou desabilitar display_errors/log_errors não protege contra essa vulnerabilidade. O consumer de diagnóstico INI acontece antes da inicialização da requisição, independente de como a aplicação trata erros depois.

Por que duas falhas no interpretador PHP preocupam mais que uma falha de plugin

A diferença entre uma vulnerabilidade num plugin e uma vulnerabilidade no interpretador é a superfície de impacto. Um plugin vulnerável afeta quem usa aquele plugin. Uma falha no PHP em si afeta qualquer aplicação rodando sobre aquela versão, independente de framework, CMS ou código customizado. É o mesmo raciocínio que vale para falhas na camada de infraestrutura corporativa, como mostramos na análise da vulnerabilidade crítica em sistemas SAP que permite invasão total de sistemas corporativos: quando o componente afetado está na base do stack, o raio de impacto multiplica.

O paralelo também vale para RCEs não autenticados em componentes de borda. Já cobrimos aqui o caso do zero-day no BIG-IP APM da F5 (CVE-2026-94127), que também permitia execução de código sem login. A lógica de priorização é a mesma: RCE não autenticado em componente de infraestrutura compartilhada vai para o topo da fila de patches, à frente de correções que exigem autenticação prévia ou interação do usuário.

Escala de exposição: por que isso afeta quase todo site do mundo

Os 5,1 milhões de hosts PHP expostos citados no resumo não significam que todos esses hosts sejam exploráveis da mesma forma. O CVSS v4 das duas falhas marca Attack Complexity como High (AC:H), e Attack Requirements como Present (AT:P): a exploração depende de layout de heap, e os advisories dizem que vários workers e reciclagem reduzem o sucesso por requisição, mas não removem a condição. Mas “complexo de explorar” não é o mesmo que “seguro”: a combinação de CVSS 9.2, ausência de autenticação e alcance via rede ainda justifica tratamento como incidente de prioridade máxima.

Quem está mais exposto por tipo de deployment

O risco varia bastante conforme o SAPI (a interface entre o servidor web e o PHP) usado em produção:

  • Apache mod_php: é o cenário com exploração completa confirmada para ambas as CVEs. No CVE-2026-109618, o shutdown da requisição roda incondicionalmente mesmo quando o startup falha, o que viabiliza RCE antes de qualquer código da aplicação executar. No CVE-2026-109619, diretivas por diretório via .htaccess ou php_value/php_flag são aplicadas antes da inicialização da requisição, criando o consumer necessário para a exploração.
  • PHP-FPM: menos exposto, mas não imune. Para o CVE-2026-109618, o FPM mata o processo filho quando o startup falha, o que reduz a janela de ataque, mas a equipe do PHP afirma que análise de código sustenta que FPM é suscetível, mesmo sem exploit completo demonstrado. Para o CVE-2026-109619, a exploração via session upload progress foi validada contra FPM, inclusive com pool dinâmico padrão. O caminho de diagnóstico INI depende do frontend (nginx, mod_proxy_fcgi) encaminhar parâmetros PHP_VALUE/PHP_ADMIN_VALUE.
  • FrankenPHP: classificado como “provavelmente suscetível” ao CVE-2026-109618 por análise de código, sem exploit confirmado. Para o CVE-2026-109619, o advisory diz que um consumer de configuração precoce equivalente não foi estabelecido na configuração padrão, mas ressalva explicitamente que isso não torna o ambiente imune.
  • LiteSpeed (LSAPI): candidato condicional para ambas as falhas, sem validação completa.

O próprio time do PHP resume: “consideramos o risco mais alto para mod_php, com um exploit demonstrado em que uma requisição sem autenticação dispara o ataque antes de qualquer código da aplicação rodar”. Servidores com Apache e mod_php vão primeiro na fila.

Já existe exploração ativa?

Sim, segundo a Cloudflare. Antes mesmo do lançamento oficial do patch, o CTO da empresa, Dane Knecht, confirmou que a Cloudflare já observava ataques iniciais mirando as duas vulnerabilidades. A empresa já havia implantado regras de WAF contra métodos de exploração conhecidos do PHP2Shell, mas Knecht classificou essas proteções como temporárias, não como solução (BleepingComputer).

Vale uma ressalva: essa confirmação de ataques reais vem de um terceiro, a Cloudflare, via imprensa. Os advisories oficiais do PHP não mencionam exploração ativa, apenas descrevem exploits que a própria equipe de segurança validou em laboratório, usando termos como “confirmed” e “validated”. Ou seja: o time de segurança do PHP fala de exploração comprovada em teste controlado, a Cloudflare fala de tentativas observadas em tráfego real.

O NCSC do Reino Unido recomendou atualização imediata de sistemas com PHP ou retirada de operação até a aplicação do patch, o que reforça o tom de urgência dado pelas próprias autoridades de segurança.

Como se proteger: patch definitivo e mitigações parciais

Atualizar é a única defesa completa

A correção definitiva é atualizar para PHP 8.5.12, 8.4.27, 8.3.36 ou 8.2.35, conforme a linha que sua aplicação usa. Gina Peter Banyard, core developer do PHP, declarou que nenhuma mitigação do PHP2Shell bloqueia totalmente os ataques potenciais, e que aplicar os patches oficiais é a única defesa permanente, posição que coincide com o texto dos próprios advisories (BleepingComputer).

PHP 8.1 e versões anteriores não recebem mais atualizações oficiais do projeto PHP; algumas distribuições e fornecedores com suporte LTS fornecem backports próprios. Dada a severidade do caso, o time de segurança do PHP disponibilizou backports mínimos de código-fonte para PHP 7.4, 8.0 e 8.1, voltados a quem compila a partir do código-fonte ou não conta com um vendor LTS. Existe inclusive um branch público no repositório oficial, PHP-7.4-CVE-2026-109618-109619, confirmando o backport para a linha 7.4 (php-src no GitHub).

Antes do lançamento, os desenvolvedores do PHP já tinham alertado publicamente para que administradores se preparassem para atualizar assim que o patch saísse, reservando-se o direito de antecipar o lançamento caso detalhes técnicos vazassem ou evidência de exploração ativa aparecesse.

Mitigações de configuração (e por que elas não bastam)

Os próprios advisories do PHP listam mitigações de configuração, mas são explícitos: nenhuma delas substitui o patch, são parciais e cobrem apenas caminhos específicos de exploração.

Para o CVE-2026-109618:

  • Configurar memory_limit=-1 administrativamente (via php_admin_value em FPM ou Apache) e reiniciar todos os workers.
  • Manter post_max_size conservador: 8M quando não há upload, até 32M quando há, com enforcement no frontend.
  • Reduzir max_input_nesting_level e limitar número de variáveis, partes multipart, headers e uploads.
  • Usar limites de container ou cgroup como camada de disponibilidade, preferindo política que mate apenas o worker afetado.
  • Importante: não usar RLIMIT_AS/RLIMIT_DATA, porque isso dispara justamente o caminho vulnerável do longjmp().

Para o CVE-2026-109619:

  • Em PHP 8.5, definir fatal_error_backtraces=0 administrativamente e reiniciar todos os workers, mas isso cobre apenas o caminho de backtrace.
  • Definir session.upload_progress.enabled=0 administrativamente e reiniciar workers, cobrindo apenas o caminho de sessão.
  • Eliminar erros fatais e warnings em caminhos alcançáveis pelo atacante.
  • Preferir configuração aplicada no startup (php.ini ou pool FPM) em vez de overrides por requisição.
  • Considerar pm.max_requests=1 se o impacto em performance for aceitável.
  • Tratar timeout de execução como erro fatal, reduzindo a chance de “Maximum execution time exceeded” ocorrer dentro de uma operação afetada.
  • Monitorar logs com log_errors=On e error_reporting=E_ALL, mas os próprios advisories alertam: ausência de erro nos logs não é prova de segurança.

Essas mitigações servem para ganhar tempo até o patch ser aplicado, não para substituí-lo. O histórico recente reforça por que essa distinção importa na prática: no caso do WordPress CVE-2026-87902, a exploração em massa começou poucas horas depois do patch ser publicado, o que deixa pouca margem para quem depende só de paliativos.

Perguntas frequentes

Preciso atualizar mesmo se uso FPM, não mod_php?
Sim. Embora o exploit completo do CVE-2026-109618 tenha sido demonstrado em mod_php, a equipe do PHP afirma que análise de código sustenta FPM como suscetível. Para o CVE-2026-109619, a exploração via session upload progress foi validada diretamente contra FPM, inclusive em pool dinâmico padrão.

Desabilitar display_errors ou usar @ nos erros me protege?
Não. Os advisories são explícitos: mudar error_reporting, usar @ ou desabilitar display_errors/log_errors não protege contra o CVE-2026-109619. O consumer de diagnóstico INI atua antes da inicialização da requisição, independente de como a aplicação trata erros depois.

Minha versão PHP é EOL (7.4, 8.0 ou 8.1). Existe correção?
A equipe do PHP publicou backports mínimos de código-fonte para essas três versões, dado o nível de severidade do caso, destinados a quem compila a partir do código-fonte ou não tem suporte de um vendor LTS. Confira o branch PHP-7.4-CVE-2026-109618-109619 no repositório oficial do projeto.

As mitigações de configuração resolvem enquanto eu não aplico o patch?
Resolvem parcialmente, e os próprios advisories dizem isso. Cada mitigação cobre um caminho específico de exploração, não o problema inteiro. Gina Peter Banyard, core developer do PHP, afirmou que nenhuma mitigação bloqueia totalmente os ataques potenciais e que aplicar o patch é a única defesa permanente.

Já há exploração ativa confirmada?
A Cloudflare confirmou observar ataques iniciais contra as duas falhas antes mesmo do lançamento do patch. Os advisories oficiais do PHP, por outro lado, descrevem apenas exploits validados em laboratório pela própria equipe, sem mencionar exploração ativa. Nem os advisories nem a cobertura da BleepingComputer mencionam exploit público.

Facebook
Twitter
WhatsApp