PHP2Shell e o resto: o runtime respondeu antes do seu código

A requisição chega ao Apache com mod_php. O PHP começa a montar o estado interno do request, o corpo multipart é grande o bastante para estourar o memory_limit, e o motor aborta a montagem com longjmp(). A partir daí, a linha do tempo deixa de pertencer à aplicação. O PHP executa o caminho de limpeza sobre uma estrutura de requisição que ficou pela metade e é exatamente essa meia-inicialização que a CVE-2026-109618 transforma em execução remota de código. Em 21 de outubro de 2026, o projeto PHP publicou patches de emergência para essa falha e para a CVE-2026-109619, batizadas pela comunidade de PHP2Shell. Ambas carregam CVSS v4.0 9,2. O ponto que desloca o problema de lugar: em Apache com mod_php, o pacote malicioso é processado antes de qualquer linha da aplicação rodar. Login, middleware, validação de entrada e WAF de aplicação chegam tarde demais.
A mecânica da primeira falha é uma história sobre o que acontece quando o mecanismo de escape do PHP encontra um estado que ainda não terminou de existir. O memory_limit é aplicado por meio de longjmp(), que interrompe o contexto corrente e aborta o tratamento da requisição quando uma alocação falha. O problema é que alguns contextos deixam a memória da requisição acessível antes de ela estar completamente inicializada. Se a falha de alocação ocorre nesse intervalo, a rotina de encerramento da requisição lê ou desvia o fluxo com base em memória inválida — um use-after-free clássico (CWE-416) com um caminho curto até execução de código. O parse de multipart/form-data aparece nos advisories como um dos caminhos confiáveis para levar o processo até essa condição: corpos de requisição sem Content-Length utilizável, número irrestrito de cabeçalhos por parte ou uma quantidade grande de campos de arquivo vazios são suficientes para exercitar o ponto exato em que o estado fica pela metade.
A segunda falha tem uma raiz diferente e um alcance que depende da arquitetura de execução. O PHP mantém estado global que aponta temporariamente para memória pertencente à requisição. Em um worker persistente — o modelo do PHP-FPM, do mod_php e de qualquer SAPI que reutiliza processos —, esse estado nem sempre é zerado, ou é repovoado depois de zerado, e continua apontando para memória que já foi liberada. A próxima requisição atendida pelo mesmo worker lê esse estado obsoleto antes que a inicialização normal o sobrescreva. Os produtores são condições de falha comuns: erros de parse ou compilação, erros não capturados, um require que falha, um timeout de execução ou um evento de out-of-memory. Os consumidores rodam cedo, antes do startup normal da requisição, no diagnóstico de INI e no handler de session.upload_progress. O PoC público confirma que o consumidor alcançável nos dois casos é o handler de session.upload_progress, que desserializa dados de sessão durante o startup do request — antes de qualquer código de aplicação rodar.
Mitigação e Configuração
Um exemplo do lado da configuração ajuda a materializar o segundo caminho. Considere um pool FPM com session.upload_progress.enabled no valor padrão On. A aplicação não usa upload progress, mas a diretiva está ligada porque ninguém a desligou. Uma requisição multipart especialmente construída leva o worker a um estado de erro na primeira chamada; a próxima requisição no mesmo worker consome o estado obsoleto no handler de upload progress e executa o gadget que o atacante plantou na sessão. A aplicação nunca chamou session_start(); o runtime fez isso por ela, no lugar errado e na hora errada. Um php_admin_flag [session.upload_progress.enabled] = off no pool, aplicado antes do parse da requisição, fecha essa variante específica — mas não o caminho do INI diagnostics nem o da falha de OOM.
Do lado da CVE-2026-109618, o exemplo de configuração é menos intuitivo e mais contraintuitivo. A recomendação da própria PHP Foundation para quem não pode atualizar de imediato é definir memory_limit=-1, preferencialmente de forma que a aplicação não consiga sobrescrever, como php_admin_value no pool FPM ou na configuração do Apache. Parece o oposto do instinto de hardening — aumentar o limite de memória para se proteger —, mas a lógica é remover o gatilho controlável: sem um limite finito que o atacante possa estourar com um corpo multipart, a falha de alocação que dispara o caminho do longjmp() deixa de ser acionável pelo request. A recomendação vem acompanhada de uma ressalva explícita: uma falha de alocação subjacente ainda pode alcançar o caminho vulnerável. memory_limit=-1 não é um patch; é uma redução de superfície. Um limite de cgroup ou container funciona como fronteira de disponibilidade, desde que mate apenas o worker afetado e não se deve substituí-lo por RLIMIT_AS ou RLIMIT_DATA, que podem recriar a falha de alocação que se quer evitar.
As versões afetadas formam uma faixa larga o suficiente para explicar por que o alerta saiu com urgência incomum. PHP 8.2.x antes de 8.2.35, 8.3.x antes de 8.3.36, 8.4.x antes de 8.4.27 e 8.5.x antes de 8.5.12 estão expostos. Os ramos 8.1 e anteriores não recebem release oficial compilado; o time de segurança do PHP publicou backports de código-fonte para 7.4, 8.0 e 8.1, destinados a quem compila a partir do código ou aguarda o pacote do fornecedor. Uma nota operacional que costuma morder times acostumados a ler número de versão: distribuições Linux e fornecedores de suporte de longo prazo receberam os patches antes do anúncio, e um pacote de distribuição pode carregar a correção sob um número de versão que parece antigo. O changelog do pacote é o lugar de verificar a CVE, não a string de versão.
SAPIs em Risco e Ações Operacionais
Os SAPIs em risco cobrem praticamente toda configuração que serve requisições web: mod_php, PHP-FPM, php-cgi, FrankenPHP e LiteSpeed. O mod_php aparece como a configuração de maior risco porque o PHP é um módulo do próprio processo do Apache, e a exploração demonstrada dispara antes de o código da aplicação entrar no caminho. O PHP-FPM tem um comportamento diferente que fecha uma das rotas: ele encerra o worker quando a requisição falha cedo na inicialização, o que bloqueia um caminho específico de OOM. A análise do próprio PHP sustenta que o FPM é suscetível, mas nenhum exploit completo contra FPM foi demonstrado para a primeira falha; a segunda foi levada até execução de código em ambos os SAPIs, com a ressalva de que o caminho via FPM depende de uma operação dentro da aplicação em execução. Traduzir isso para "FPM está seguro" é um erro de leitura. O worker do FPM reutiliza processos, e o estado obsoleto da segunda CVE é precisamente o tipo de coisa que a persistência do worker favorece.
A prioridade de ação, para quem opera servidor ou site em PHP, começa por responder a uma pergunta única e desconfortável: qual é a versão do runtime que está atendendo as requisições? Não a versão que aparece no composer.json, não a versão do framework, não a versão que o painel de hospedagem exibe na página inicial. A versão do binário: php -v no servidor, o phpinfo() de um ambiente controlado, ou a checagem no gerenciador de pacotes da distribuição. Depois disso, subir para 8.5.12, 8.4.27, 8.3.36 ou 8.2.35, conforme o ramo, e reiniciar todos os workers. O reinício não é um detalhe de operação: um processo antigo continua com o código vulnerável carregado em memória depois que o pacote em disco foi substituído, e o mod_php recarrega o módulo apenas com o restart do Apache. Em container, o rebuild da imagem base; em hospedagem compartilhada, cobrar o provedor com a versão específica e o prazo do patch.
Para quem ainda não consegue aplicar o patch na janela necessária, as mitigações parciais têm um valor real e um limite explícito. Nenhuma delas substitui o update. A PHP Foundation foi direta nesse ponto: as medidas reduzem a exposição a caminhos específicos de ataque, mas não tornam a instalação segura. No lado da CVE-2026-109619, session.upload_progress.enabled = 0 no php.ini ou, melhor, via php_admin_flag no pool FPM, aplicado antes do parse da requisição, e reinício de todos os workers. ini_set() e .user.ini não servem para isso porque rodam tarde demais no ciclo do request. No PHP 8.5, fatal_error_backtraces = 0 fecha o caminho confirmado de backtrace de erro fatal, também com reinício de workers. Esconder diagnósticos com error_reporting, display_errors ou @ não ajuda: o estado obsoleto é consumido antes de qualquer configuração de exibição de erro entrar em cena. Do lado da CVE-2026-109618, memory_limit = -1 (com a ressalva já discutida), post_max_size baixo (8 MB para aplicações sem upload, 32 MB com upload, mantendo upload_max_filesize abaixo), e limites no proxy ou servidor web para tamanho decodificado do request, contagem de campos, partes multipart, cabeçalhos e profundidade de aninhamento — incluindo requisições sem Content-Length utilizável. pm.max_requests = 1 reduz a janela de reúso de estado entre requisições, ao custo de performance que precisa ser pesado no contexto de cada deployment.
Hardening e Conclusão
O endurecimento do php.ini que já era recomendado antes do PHP2Shell continua valendo e não foi substituído por ele. expose_php = Off, display_errors = Off em produção com logs em arquivo, e disable_functions cobrindo exec, passthru, shell_exec, system, proc_open, popen e o que a aplicação comprovadamente não usa. A lista de 2026 vai além dessas cinco e inclui pcntl_exec, dl, ini_set e wrappers de arquivo que raramente são necessários em produção. Uma ressalva que aparece na documentação de hardening e que vale repetir: configurar disable_functions apenas no php.ini pode ser insuficiente se a aplicação ou o pool tiverem meios de sobrescrever a diretiva em runtime; a configuração precisa ser aplicada também no nível do pool FPM ou da configuração do servidor. O OWASP PHP Configuration Cheat Sheet continua sendo a referência de base para o conjunto de diretivas, e a lógica do PHP2Shell não o torna obsoleto — ela apenas mostra que nenhuma diretiva de aplicação alcança um bug que vive no motor, antes do código da aplicação.
O que o PHP2Shell não muda é o restante do cenário de ataque que já matava aplicações PHP havia anos. SQL injection por concatenação de string, XSS por saída sem encoding contextual, inclusão remota de arquivo, funções perigosas habilitadas e dependências desatualizadas continuam no Top 10 do OWASP e no dia a dia de invasão. Prepared statements com PDO e parâmetros bindados, validação por allowlist no input, encoding contextual na saída e password_hash/password_verify continuam sendo a linha de base. A diferença que o PHP2Shell introduz é de camada: uma aplicação impecável do ponto de vista de código não fecha um caminho que o runtime abre antes de a aplicação existir no fluxo da requisição. O patch do framework não atualiza o binário do PHP. O update do WordPress não atualiza o mod_php. O scanner de dependências do composer.lock não enxerga a versão do interpretador. A pergunta que o alerta de 21 de outubro deixa para os times não é sobre a qualidade do código da aplicação. É sobre quem, no processo de operação, tem a responsabilidade explícita de manter o runtime que serve as requisições na versão que não responde ao pacote errado.
Aprofunde-se no tema de forma estruturada — supervisão, auditoria e o que o sistema realmente expõe — consultando a coleção Engenharia de Software Assistida por IA, disponível na Amazon e no Kindle Unlimited.
- Acesse a landing page da série
- ✅ Cupom de 30% OFF:
LEIA30
Fontes de pesquisa consultadas
- The PHP Foundation — Security release 21/10/2026 (8.5.12/8.4.27/8.3.36/8.2.35) e orientação oficial de mitigação
- GitHub Security Advisories — GHSA-w8hw-w9fx-j79w (CVE-2026-109618) e GHSA-6wmf-4rv5-p3rp (CVE-2026-109619)
- ThaiCERT — Alerta PHP2Shell, 22 out. 2026
- CyCognito — Emerging Threat: php2shell (CVE-2026-109618, 109619), 21 out. 2026
- Qualys — Two Critical PHP Flaws Could Hand Attackers Your Web Servers, 21 out. 2026
- The Hacker News — PHP Issues Emergency Patches for Two Critical PHP2Shell RCE Flaws as Exploit Attempts Emerge, 21 out. 2026
- UCC-CERT — CVE-2026-109618 & CVE-2026-109619: Critical PHP RCE Vulnerabilities, 22 out. 2026
- N-able — PHP Foundation issues unusual advance notice for security patches, 21 out. 2026
- GitHub —
joshuarosato/php2shell-poc, PoC e documentação técnica de reprodução - AWS Security Bulletin — PHP web runtimes (CVE-2026-109618 and CVE-2026-109619), 21 out. 2026
- OWASP PHP Configuration Cheat Sheet e Secure Coding Practices
- Referências de hardening de
disable_functionseopen_basedirnophp.inie no pool FPM (2026)
#PHP #Seguranca #PHP2Shell #CVE #Ciberseguranca #WebSecurity #DevOps #Backend #OWASP #Atualizacao #RCE #RuntimeSecurity #PHPFPM #ModPHP
About the author
- Engenheiro de Software Sênior e Arquiteto de Soluções com anos de experiência construindo sistemas distribuídos e de alta criticidade. Autor da série de livros "Engenharia de Software Assistida por IA". Escrevo sobre arquitetura de software, ecossistema .NET, engenharia de contexto, MCP e como utilizar agentes de IA para criar sistemas robustos. Livros na Amazon: https://www.amazon.com.br/stores/author/B0H7VR7VS3/allbooks ---------------- Senior Software Engineer & Solution Architect with over 20 years of experience building scalable, distributed systems. Author of "Engenharia de Software Assistida por IA". Writing about modern software architecture, .NET, context engineering, MCP, and AI-assisted development. Books on Amazon: https://www.amazon.com.br/stores/author/B0H7VR7VS3/allbooks
Comments
Join the discussionNo comments yet. Be the first to comment.





