PHP2Shell: o que você precisa saber agora e como se preparar para o futuro Saiba mais

A CrowdStrike protege os clientes contra ameaças distribuídas por meio do PHP2Shell

Duas vulnerabilidades críticas de execução remota de código (RCE) por corrupção de memória, identificadas como CVE-2026-109618 e CVE-2026-109619, foram divulgadas no núcleo do PHP.

21 de outubro de 2026  •  Amit Serper  •  Linhas de frente

Duas vulnerabilidades críticas de execução remota de código (RCE) por corrupção de memória, conhecidas coletivamente como PHP2Shell, foram divulgadas no núcleo do PHP, o mecanismo subjacente que sustenta grande parte da web. A plataforma CrowdStrike Falcon® oferece visibilidade e cobertura de detecção prontas para uso para atividades pós-exploração de ambas as vulnerabilidades.

Identificadas como CVE-2026-109618 e CVE-2026-109619, ambas as vulnerabilidades têm uma pontuação de 9,2 (Crítica) no CVSS 4.0 e são classificadas como CWE-824: acesso a ponteiro não inicializado. Ambas permitem que um invasor remoto não autenticado execute comandos arbitrários do sistema em um servidor Web afetado por meio de HTTP.

Os dois problemas têm uma única causa raiz. Quando o PHP encontra uma condição fatal (esgotamento do memory_limit configurado, ultrapassar o tempo máximo de execução ou perda da conexão com o cliente), ele não retorna um erro pela pilha de chamadas. Em vez disso, gera um erro fatal e usa longjmp() para abandonar completamente o contexto de execução atual, interrompendo a solicitação. Qualquer função C interrompida durante a criação de uma estrutura de dados é simplesmente descartada: nenhum destrutor é executado, nenhuma operação de contabilização é revertida e nada registra que a estrutura está incompleta. Em seguida, o PHP prossegue com uma sequência de limpeza que, em condições normais, pressupõe que todas as estruturas estejam em um estado consistente.

As duas CVEs descrevem os pontos em que essa suposição falha:

  • CVE-2026-109618 (RCE por falta de memória) abrange contextos que deixam a memória da solicitação não inicializada. Uma estrutura de dados é marcada como contendo uma entrada antes que a memória necessária para armazená-la seja alocada. Quando essa alocação ultrapassa o limite de memória, a entrada fica com dados residuais do heap. A rotina de limpeza da própria solicitação que falhou lê esses bytes e, com base neles, toma decisões de desvio.
  • CVE-2026-109619 (RCE por erros fatais) abrange contextos que deixam o estado global inválido. Uma variável global no nível do módulo continua apontando para uma região do heap específica da solicitação que já foi liberada durante o encerramento e ainda indica que sua operação está ativa. Uma solicitação subsequente processada pelo mesmo processo de trabalho lê essa memória liberada ou toma decisões de desvio com base em seu conteúdo.

Em ambos os casos, o invasor controla o gatilho e, principalmente, os dados que o PHP acaba interpretando incorretamente. A CrowdStrike verificou, de ponta a ponta, que uma única solicitação POST anônima pode fazer com que um processo de trabalho do Apache mod_php inicie um processo de shell e execute um comando especificado pelo invasor, sem necessidade de autenticação, sessão ou código de aplicação vulnerável.

O que torna essas vulnerabilidades especialmente graves é sua localização na pilha de execução. O PHP analisa os dados das solicitações recebidas antes da inicialização das extensões e antes da compilação de uma única linha de código da aplicação. Portanto, a exploração independe do conteúdo hospedado no servidor. Uma aplicação mantida com extremo cuidado está exposta exatamente ao mesmo grau de risco que uma aplicação negligenciada. O ataque também é extremamente discreto: o processo de trabalho não falha, o servidor retorna uma resposta HTTP 200 padrão e os logs da aplicação não registram nada de incomum.

Em nossos testes, desenvolvemos um código de exploit que funcionou de maneira consistente nas arquiteturas x86-64 e ARM, manteve sua confiabilidade mesmo após a reinicialização dos processos de trabalho e, em hosts baseados em ARM, cada vez mais comuns em ambientes de nuvem, conseguiu contornar a randomização do layout do espaço de endereçamento (ASLR) usando apenas solicitações HTTP.

Detalhes técnicos

A causa raiz compartilhada: longjmp() sem desenrolamento da pilha

As duas vulnerabilidades estão presentes no Zend, o mecanismo central do PHP. O Zend é a implementação em C que compila o código-fonte PHP em bytecode, executa esse código em uma máquina virtual e gerencia tudo o que a linguagem precisa durante a execução: variáveis, arrays, objetos, contagem de referências e alocação de memória. Originalmente desenvolvido por Zeev Suraski e Andi Gutmans (o nome é uma combinação dos primeiros nomes dos criadores) e introduzido no PHP 4, ele não é um componente opcional nem um add-on de terceiros. O Zend está incluído em todas as compilações do PHP, motivo pelo qual seu código-fonte fica no diretório Zend/ do php-src. Na prática, isso significa que uma falha no Zend é uma falha no próprio mecanismo do PHP, que afeta todas as aplicações, frameworks e extensões executados no interpretador, independentemente de como o código foi escrito.

Dois componentes fundamentais do Zend são essenciais para os problemas descritos aqui: a HashTable, que sustenta todos os arrays do PHP, incluindo a variável superglobal $_POST, e o Gerenciador de Memória do Zend (Zend MM), o alocador de memória por solicitação do próprio PHP, que atua como intermediário entre o PHP e o sistema operacional. O caminho de tratamento de erros fatais do PHP termina em zend_bailout(), que executa um LONGJMP para sair da pilha de chamadas C atual:

// Zend/zend.c
ZEND_API ZEND_COLD ZEND_NORETURN void _zend_bailout(const char *filename, uint32_t lineno)
{
...
CG(unclean_shutdown) = 1;
...
LONGJMP(*EG(bailout), FAILURE);
}

Nada entre o erro fatal e o bloco zend_catch que o envolve é executado. Qualquer função chamadora que tenha modificado o estado e dependesse da execução de seu próprio código de limpeza deixa esse estado inalterado. Isso só é seguro se todas essas funções chamadoras forem escritas levando em consideração a possibilidade de uma interrupção abrupta, o que não acontece em vários casos.

CVE-2026-109618: memória de solicitação não inicializada, acessada durante a mesma solicitação

O exemplo mais representativo é _zend_hash_str_add_or_update_i() em Zend/zend_hash.c, a função auxiliar responsável por todas as inserções com chaves do tipo string em arrays do PHP. No caminho de inserção, ela incrementa ambos os contadores de elementos, obtém um ponteiro para o novo bucket e só então aloca uma cópia da chave:

// Zend/zend_hash.c
add_to_hash:
   idx = ht->nNumUsed++;
   ht->nNumOfElements++;
   p = ht->arData + idx;
   p->key = key = zend_string_init(str, len, GC_FLAGS(ht) & IS_ARRAY_PERSISTENT);

Se zend_string_init() ultrapassar o memory_limit, o Zend MM gerará um erro fatal que terminará em zend_bailout(). A execução nunca retorna para concluir o bucket. Nesse momento, a tabela indica uma entrada a mais do que realmente contém, e o valor, o hash e o ponteiro da chave dessa entrada correspondem aos bytes que o alocador deixou por último naquela região da memória. O array de buckets é alocado com um emalloc() não inicializado, e apenas a região de hash é explicitamente redefinida.

Durante a limpeza, php_request_shutdown() destrói as variáveis superglobais, e zend_array_destroy() percorre os nNumUsed buckets. A entrada residual é passada para i_zval_ptr_dtor(), que trata os bytes restantes como um valor com contagem de referências: desreferencia o ponteiro contido nesses bytes, decrementa a contagem de referências nesse endereço e, caso ela chegue a zero, realiza o despacho por meio de uma tabela de ponteiros de função indexada por uma etiqueta de tipo que também veio dos bytes residuais.

Para disparar essa condição, basta um corpo de solicitação POST codificado para URL, pois o PHP preenche $_POST dentro de php_hash_environment(), que é chamado por php_request_startup() antes de zend_activate_modules() e antes da compilação de qualquer script. Um campo com um nome aninhado, como ZZZZ…[x]=1 , faz com que php_register_variable_ex() insira o nome externo como uma nova chave de array. Assim, o invasor controla diretamente o comprimento da chave que precisa ser alocada. Ao dimensionar esse nome para ultrapassar o memory_limit, o invasor faz com que a interrupção ocorra precisamente durante a alocação da chave.

O controle sobre os bytes residuais decorre do comportamento determinístico do alocador. O Zend MM atende a pequenas alocações usando listas de blocos livres organizadas por tamanho, seguindo rigorosamente a ordem de último a entrar, primeiro a sair (LIFO), e nunca zera os blocos de memória recuperados. Portanto, um campo anterior na mesma solicitação, cujo valor o PHP copia, decodifica e libera imediatamente, pode ser dimensionado de modo que seu conteúdo liberado ocupe exatamente o bloco que o array corrompido reivindicará posteriormente. A entrada residual se transforma em um valor totalmente definido pelo invasor, que usamos para criar um array falsificado cujo ponteiro de destrutor é resolvido para a função system() da libc, com a string de comando armazenada no mesmo bloco.

O único vestígio de uma tentativa bem-sucedida no servidor é um erro fatal genérico no log do servidor web:

Erro fatal do PHP: O limite de memória permitido de 16777216 bytes foi esgotado (tentativa de alocar 3670048 bytes) em local desconhecido, na linha 0

O trecho in Unknown on line 0 é o indício revelador: confirma que nenhum arquivo PHP chegou a ser executado.

CVE-2026-109619: estado global inválido, acessado por uma solicitação subsequente

A segunda CVE segue o mesmo mecanismo, mas o dano ocorre em um estado definido no escopo do processo, o que desloca a janela de exploração para uma solicitação posterior. Nesse caso, a operação interrompida armazena o estado em cache em uma variável global e depende da execução de sua própria chamada de limpeza para apagá-lo. O PHP armazena em cache os contextos ativos de serialização e desserialização em variáveis globais do módulo e reutiliza o ponteiro armazenado em cache sempre que o nível de aninhamento é diferente de zero. Somente a chamada de destruição correspondente decrementa esse nível e limpa o ponteiro, e somente a rotina de inicialização de solicitações de uma extensão o redefine. O gerenciamento de sessões funciona de maneira semelhante, mantendo o identificador da sessão, o estado do manipulador, as variáveis de sessão e o estado do progresso de upload em variáveis globais, que são limpas por rotinas de encerramento específicas.

Quando um erro fatal interrompe uma dessas operações, a limpeza correspondente é ignorada. A variável global mantém seu ponteiro, e o contador de aninhamento continua indicando que a operação está ativa. Em seguida, o encerramento da solicitação libera todo o heap específico da solicitação, mas a variável global permanece, pois pertence ao processo, e não à solicitação. Com isso, o PHP mantém um ponteiro no nível do módulo que aponta para uma região da memória já liberada.

Dois fatores tornam essa condição acessível e controlável:

  • Reutilização de processos de trabalho: em implantações com processos de trabalho persistentes, como o Apache mod_php, o mesmo processo trata a solicitação seguinte. A variável global residual permanece intacta, a operação é considerada em andamento e o ponteiro para a memória liberada é lido, usado para gravação ou utilizado para determinar um desvio na execução. O conteúdo que ocupa essa região da memória nesse momento pode ser influenciado pelo invasor, pois a solicitação intermediária também é controlada por ele. O invasor escolhe seu tamanho e conteúdo, e o comportamento de alocação do Zend MM é suficientemente determinístico para posicioná-los de maneira confiável. O PHP-FPM encerra um processo de trabalho quando a inicialização de uma solicitação falha, impedindo sua reutilização em caso de interrupções durante a fase de inicialização. No entanto, uma interrupção dentro de um script já em execução mantém o processo de trabalho do FPM ativo e igualmente exposto.
  • Ordem de inicialização: php_request_startup() analisa as variáveis da solicitação antes de executar os callbacks de inicialização de solicitações das extensões. Portanto, o gerenciamento do progresso de uploads multipart executa código de sessão e serialização antes que as extensões responsáveis por essas variáveis globais tenham a oportunidade de redefini-las, permitindo que um ponteiro residual seja acessado na fase inicial de uma solicitação.

Além do esgotamento da memória, essa condição pode ser provocada por tempos limite de execução e conexões com o cliente interrompidas, situações que seguem o mesmo caminho de zend_bailout(). Consequentemente, a exploração ocorre em duas etapas: uma solicitação compromete o estado do processo de trabalho, e uma segunda dispara o uso da memória liberada.

Impacto das vulnerabilidades

A gravidade de ambos os problemas decorre de sua localização nas etapas iniciais do processamento das solicitações. Como a corrupção ocorre enquanto o PHP ainda está montando as variáveis internas da solicitação, antes da inicialização das extensões e da execução de qualquer código da aplicação, todos os endpoints PHP acessíveis em um servidor afetado representam uma superfície de ataque ativa, incluindo formulários estáticos, rotas de login e verificações de integridade. Não é necessário contornar a autenticação, roubar uma sessão nem descobrir um endpoint vulnerável. A exposição independe do CMS, framework ou dos plug-ins utilizados.

O ataque também praticamente não deixa rastros digitais. Os processos de trabalho permanecem estáveis, as respostas retornam HTTP 200 e os logs da aplicação não registram anomalias, pois nenhum código da aplicação chega a ser executado. O único vestígio no servidor é um erro fatal de limite de memória no log de erros do servidor web, que costuma ser descartado como ruído. Isso deixa os defensores sem dados de telemetria web que permitam identificar os endpoints comprometidos e tomar medidas. 

Em servidores web com vários processos, como o Apache prefork, todos os processos de trabalho são derivados de um único processo principal. Portanto, o layout de memória do qual o invasor depende permanece consistente mesmo após a reinicialização dos processos de trabalho. Essa estabilidade permite que o invasor refine os payloads sem nenhum custo. Uma tentativa malsucedida é absorvida pela recriação de um processo de trabalho, que as ferramentas de monitoramento tratam como uma operação de rotina.

As duas CVEs diferem em escopo e momento de exploração de maneiras que têm implicações operacionais importantes. A CVE-2026-109618 é autossuficiente: uma única solicitação corrompe seus próprios dados, e as consequências se manifestam durante a limpeza dessa mesma solicitação. Isso torna a exploração rápida, atômica e difícil de reconstruir posteriormente. A CVE-2026-109619 abrange várias solicitações, exigindo uma etapa de comprometimento do estado e outra de disparo contra o mesmo processo de trabalho. No entanto, ela alcança um conjunto mais amplo de estados internos e pode ser disparada por tempos limite de execução e desconexões do cliente, além do esgotamento da memória. As implantações com processos de trabalho persistentes representam a principal superfície de exposição para essa segunda categoria.

A exploração bem-sucedida de qualquer uma das vulnerabilidades permite a execução de comandos no contexto do usuário do servidor web, a base de apoio inicial normalmente utilizada para implantar web shells, coletar credenciais de bancos de dados e APIs em arquivos de configuração e realizar movimentação lateral para redes internas. A aplicação de correções é a única medida de remediação completa. Quando não é possível aplicar as correções imediatamente, o monitoramento comportamental dos endpoints é a principal linha de defesa, pois a exploração é invisível na camada HTTP, mas seus resultados não são.

Estou em risco?

Ambas as vulnerabilidades estão presentes em todas as versões do PHP das séries 7.x e 8.x. A ordem específica das operações da HashTable responsável pela CVE-2026-109618 foi introduzida por uma otimização de desempenho em outubro de 2017 e disponibilizada pela primeira vez no PHP 7.3.0 (dezembro de 2018). Os caminhos de serialização e sessão responsáveis pela CVE-2026-109619 são ainda mais antigos, remontando ao PHP 7.1 e ao PHP 5.4, respectivamente. Em outras palavras, a possibilidade de exploração existe em versões disponibilizadas do PHP há mais de sete anos. Nenhuma versão com suporte está livre dessas vulnerabilidades.

A versão, por si só, não determina a exposição. O gatilho de falta de memória precisa estar acessível enquanto o PHP analisa os dados da solicitação. Para isso, o corpo da solicitação deve poder crescer o suficiente para esgotar o limite de memória antes da execução de qualquer script. Na prática, isso significa que a configuração atende aproximadamente à seguinte condição:

 memory_limit < 4 × post_max_size

As configurações padrão do PHP (post_max_size = 8M, memory_limit = 128M) não atendem a essa condição. Portanto, uma instalação padrão não pode ser explorada pelo caminho codificado para URL que demonstramos. No entanto, muitas implantações em produção são configuradas para aceitar uploads grandes, e combinações comuns, como post_max_size = 64M com memory_limit = 128M ou post_max_size = 100M com memory_limit = 256M, atendem a essa condição. Vale observar que post_max_size pode ser definido por diretório no .htaccess ou em um host virtual. Portanto, em ambientes de hospedagem compartilhada, um único website pode adotar uma configuração vulnerável sem nenhuma alteração nas configurações gerais do servidor. A CrowdStrike reproduziu o problema reduzindo o memory_limit para 16M e aumentando o post_max_size para 64M apenas para manter o payload de teste pequeno. A mesma falha pode ser explorada com payloads maiores, considerando os limites mais altos mencionados acima.

Para verificar a versão do PHP e a configuração em execução no seu servidor, execute php --version. Para consultar os valores definidos no servidor para as opções mencionadas acima, execute o seguinte comando:
php -i | grep -E "^(memory_limit|post_max_size|upload_max_filesize|Loaded Configuration File)”.

Como a plataforma CrowdStrike Falcon® protege os clientes

A CrowdStrike adota uma abordagem em camadas para detectar atividades de exploração e pós-exploração, usando machine learning e detecções baseadas em comportamento, incluindo indicadores de ataque (IOAs). A plataforma Falcon oferece visibilidade e cobertura de detecção prontas para uso para atividades pós-exploração das vulnerabilidades do PHP. Os IOAs existentes detectam e impedem atividades comuns de pós-exploração, incluindo a implantação de web shells, processos filhos suspeitos iniciados pelo PHP ou por processos do servidor web e a execução de comandos maliciosos.

A CrowdStrike continua investigando técnicas de exploração associadas a essa vulnerabilidade.

Figura 1. Cadeia de exploração de vulnerabilidades do PHP

Identificação de versões vulneráveis do PHP com o Falcon® Exposure Management

O CrowdStrike Falcon® Exposure Management oferece visibilidade abrangente dos ambientes afetados. As capacidades de verificação de rede (Netscan) e gerenciamento da superfície de ataque externa (EASM) permitem detectar rapidamente ativos expostos à internet com base em banners. Além disso, a visibilidade proporcionada pelos sensores permite avaliar automaticamente as correções posteriores fornecidas pelos fabricantes nas distribuições Linux e nos sistemas operacionais Windows e macOS com suporte.

O Falcon® Exposure Management detecta essas CVEs em todas as versões de sensores com suporte por meio das verificações existentes do gerenciador de pacotes e de aplicações específicas. Essas verificações não exigem uma versão mínima do sensor.

A CrowdStrike também adicionou as seguintes detecções baseadas em sensores. Elas identificam instalações do PHP que podem passar despercebidas pelas verificações existentes, como instalações personalizadas ou realizadas fora de um gerenciador de pacotes. Essas detecções estão disponíveis apenas nas versões de sensores indicadas:

  • Windows (sensor Falcon 7.40 ou posterior): uma verificação de executáveis do PHP em todo o sistema, na unidade padrão. Isso permite identificar instalações adicionais do PHP que as verificações existentes talvez não detectem, incluindo instalações personalizadas.
  • Linux (sensor Falcon 7.31 ou posterior): uma verificação dos processos em execução para identificar instalações do PHP realizadas fora do gerenciador de pacotes esperado.

Os clientes que utilizam versões anteriores do sensor continuam protegidos pelas verificações do gerenciador de pacotes e de aplicações específicas. Eles só terão acesso às verificações adicionais de todo o sistema e dos processos em execução após a atualização.

Figura 2. Identificação de versões vulneráveis do PHP no Falcon® Exposure Management

Identificação de ameaças com o Next-Gen SIEM

CrowdStrike Falcon® Next-Gen SIEM: painel de resposta a ameaças emergentes

A cobertura de detecção do CrowdStrike Falcon® Next-Gen SIEM para essa ameaça (nova seção “Vulnerabilidades de execução remota de código no PHP (CVE-2026-109618/109619)” no painel Resposta a ameaças emergentes do CrowdStrike Falcon®) foi disponibilizada em produção como parte de uma versão dos pacotes de conteúdo do NG-SIEM.

O painel Resposta a ameaças emergentes é uma ferramenta de resposta direcionada, desenvolvida para oferecer aos clientes da CrowdStrike visibilidade imediata de campanhas ativas ou vulnerabilidades recém-descobertas que possam afetar seus ambientes. Ao aproveitar a ampla telemetria dos módulos da plataforma Falcon e os dados de terceiros ingeridos pelo Falcon® Next-Gen SIEM, esse painel oferece consultas de detecção e investigação de ameaças prontas para uso, permitindo que as equipes de segurança determinem rapidamente a exposição a ameaças e identifiquem ativos potencialmente comprometidos.

  • Consulte Next-Gen SIEM > Gerenciamento de logs > Painéis

Figura 3. Identificação do painel de resposta a ameaças emergentes no Falcon® Next-Gen SIEM

Modelos de regras de correlação do Falcon® Next-Gen SIEM

Adicionamos um novo modelo de regra de correlação (CRT) do Falcon® Next-Gen SIEM para detectar e responder às vulnerabilidades CVE-2026-109618 e CVE-2026-109619. Essas regras oferecem cobertura para tentativas de exploração relacionadas a essas vulnerabilidades. 

Genérico - Web - Esgotamento de memória do PHP durante a inicialização da solicitação (CVE-2026-109618 e CVE-2026-109619)

Essa detecção identifica erros fatais de esgotamento de memória do PHP que ocorrem antes do início da execução de um script PHP. Especificamente, ela busca um padrão característico que aparece nos logs de erros do servidor web como "Allowed memory size of N bytes exhausted", acompanhado de "in Unknown on line 0". O marcador "Unknown on line 0" diferencia as falhas na fase de inicialização dos erros de memória muito mais frequentes que ocorrem durante a execução de scripts, oferecendo aos defensores um sinal de maior precisão de que algo está fazendo o interpretador PHP esgotar a memória antes da execução de qualquer código da aplicação. Padrões como esse podem indicar tentativas de exploração do mecanismo do PHP. Os clientes podem consultar o modelo aqui ([US-1]  [US-2]  [US-3]  [EU-1]  [US-GOV-1]  [US-GOV-2]).

Conclusões e recomendações

A CrowdStrike está monitorando ativamente essa situação em evolução e fornecerá atualizações à medida que novas informações estiverem disponíveis. Paralelamente, continuamos desenvolvendo e implementando novas lógicas de detecção comportamental na plataforma Falcon para detectar e impedir atividades maliciosas relacionadas às vulnerabilidades divulgadas do PHP, CVE-2026-109618 e CVE-2026-109619.

Recomendamos que os clientes identifiquem todos os sistemas que executam versões vulneráveis do PHP e apliquem as atualizações de segurança mais recentes de acordo com as orientações oficiais do PHP ou do fabricante.