PHP2Shell: como corrigir bem o PHP e reforçá-lo para a próxima falha crítica
Publicado: · 19 min de leitura · Por Oluniyi D. Ajao
A 21 de outubro de 2026, o projeto PHP lançou versões de segurança para corrigir duas falhas críticas no próprio PHP: CVE-2026-109618 e CVE-2026-109619. Ambas podem permitir que um atacante sem qualquer autenticação execute código num servidor através de pedidos web manipulados. Cinco dias depois, a agência norte-americana de cibersegurança CISA (Cybersecurity and Infrastructure Security Agency) acrescentou as duas ao seu catálogo de vulnerabilidades com exploração confirmada em ataques. As versões corrigidas são PHP 8.5.12, 8.4.27, 8.3.36 e 8.2.35.
A imprensa especializada em segurança batizou o par de «PHP2Shell». Este guia explica em termos simples o que são os dois bugs e, depois, dá a programadores e administradores de sistemas as verificações exatas a fazer: que PHP serve realmente o seu site, se a sua distribuição já fez o backport da correção, como garantir que a atualização produz efeito e o que fazer se não puder atualizar hoje. A seguir, vai além da correção. O PHP2Shell não será a última falha crítica do PHP, e a parte final deste guia trata do reforço de segurança que limita o que um atacante consegue fazer quando a próxima falha chegar antes da sua atualização.

O que é o PHP2Shell
«PHP2Shell» não é um nome oficial. O projeto PHP, os seus avisos de segurança no GitHub, os registos CVE e a CISA usam apenas os números CVE e títulos descritivos. A alcunha vem da imprensa de segurança e descreve o resultado: um pedido manipulado contra o PHP que acaba numa shell no servidor.
O aviso do projeto PHP descreve assim os dois bugs:
«RCE através de corrupção por falta de memória (OOM), CVE-2026-109618: o tratamento da falta de memória pode interromper o PHP enquanto os dados internos do pedido estão apenas parcialmente inicializados. A limpeza do pedido pode então tratar os dados inválidos como válidos.»
«RCE através de estado global obsoleto, CVE-2026-109619: o estado interno pertencente a um pedido pode sobreviver depois de a memória que o suporta ter sido libertada. Um pedido posterior tratado pelo mesmo worker pode usar esse estado obsoleto antes de a inicialização normal o repor.»
Na prática:
- A CVE-2026-109618 é atingida através do processamento, pelo PHP, de envios de formulários multipart. Um pedido construído para esgotar a memória do PHP no momento errado deixa dados internos incompletos, e a limpeza que se segue confia neles.
- A CVE-2026-109619 é um use-after-free entre pedidos. Um worker PHP que serve um pedido pode ficar com um apontador para memória que já foi libertada, e um pedido posterior nesse worker usa-o. Um dos caminhos confirmados é o progresso de upload da sessão (session upload progress), que vem ativo por predefinição e, nas palavras do aviso do GitHub, não precisa de «nenhuma chamada da aplicação a
session_start()».
A equipa de segurança do PHP atribui a ambas uma pontuação de 9,2 em 10 (crítica) no CVSS v4.0. O pormenor decisivo é o sítio onde se encontram: no próprio motor de execução do PHP e, no Apache com mod_php, podem ser acionadas antes de correr qualquer código da aplicação. Atualizar o Laravel, o Symfony, o WordPress ou os seus pacotes Composer não corrige o PHP2Shell. É o binário do PHP que tem de mudar.
Quem está exposto
O aviso do projeto PHP é direto: «todas as instalações PHP que servem pedidos web, incluindo PHP-FPM, Apache com mod_php, php-cgi, FrankenPHP e LiteSpeed». Considera o risco mais elevado no Apache com mod_php, «com um exploit demonstrado em que um pedido não autenticado pode desencadear o ataque antes de correr qualquer código da aplicação».
Os avisos do GitHub detalham a situação por configuração. A execução remota de código completa através do bug de memória foi validada contra mod_php com os limites de memória e de entrada predefinidos. O PHP-FPM é considerado vulnerável com base na análise do código-fonte, e o caminho de sessão do segundo bug foi validado tanto contra mod_php como contra FPM. O FrankenPHP é descrito como provavelmente vulnerável a ataques que não dependem da aplicação; para a interface do LiteSpeed, exploits específicos da aplicação são descritos como prováveis, mas não validados. Se o seu servidor corre PHP para a web de alguma destas formas e não está numa versão corrigida, trate-o como vulnerável.
Versões afetadas, segundo os avisos: todas as versões 8.2 anteriores à 8.2.35, todas as 8.3 anteriores à 8.3.36, todas as 8.4 anteriores à 8.4.27 e todas as 8.5 anteriores à 8.5.12. O PHP 8.1 e versões anteriores já não recebem novas versões do projeto PHP: «Algumas distribuições e fornecedores de suporte LTS disponibilizam correções com backport, por isso contacte o seu fornecedor para mais detalhes, ou atualize para uma versão suportada.» O projeto publicou também backports mínimos do código-fonte para 7.4, 8.0 e 8.1, que são patches e não versões. Note ainda que o PHP 8.2 chega ao fim de vida a 31 de dezembro de 2026, pelo que um servidor que hoje passe para a 8.2.35 precisa, de qualquer forma, de um plano de migração.
Já está a ser explorado?
Sim. A CISA acrescentou as duas CVE ao seu catálogo Known Exploited Vulnerabilities a 26 de outubro de 2026. No mesmo dia, o BleepingComputer noticiou que honeypots geridos por Adel Karimi, responsável de deteção e resposta na Perplexity, registaram uma execução remota de código confirmada contra um servidor Apache com mod_php em PHP 8.4.26, uma versão atrás da correção, e que a Amazon Threat Intelligence tinha detetado atacantes a visar as falhas quatro dias antes da publicação das correções. A Arctic Wolf reportou varrimentos em larga escala pouco depois da divulgação.
Houve código de prova de conceito divulgado antes das correções. A equipa de segurança do PHP disse ao BleepingComputer que nenhum funcionava, mas os ataques confirmados mostram que existem exploits funcionais. Parta do princípio de que scanners automáticos já estão a testar todos os endpoints PHP públicos que conseguem encontrar.
Para programadores: o que verificar
- Verifique o PHP que serve a web, não o do seu terminal. O
php -vmostra a versão do binário de linha de comandos. O seu site pode correr outra build através do FPM, do Apache ou de um painel de alojamento. Coloque no site uma página temporária que mostrePHP_VERSIONePHP_SAPI, abra-a e depois apague-a. - Reconstrua os seus contentores. Se faz o deploy do PHP em Docker, a correção está na imagem base. Reconstrua com
docker build --pullsobre uma base que traga uma versão corrigida, volte a fazer o deploy e confirme que o contentor em execução indica essa versão. - Atualizar a aplicação não resolve isto. Vale a pena atualizar a framework e o CMS, mas as falhas estão no PHP. Se está a ponderar onde alojar o WordPress, a pergunta que aqui importa é quem atualiza e reinicia o PHP, e com que rapidez.
- Em alojamento partilhado ou gerido, pergunte ao seu fornecedor. Pergunte que versão do PHP o seu site usa e se os workers foram reiniciados depois da atualização. As duas perguntas contam.
Para administradores de sistemas: a correção, passo a passo
1. Encontre todos os PHP que servem tráfego web
É frequente um servidor ter várias builds de PHP lado a lado: pacotes da distribuição, um repositório de terceiros, as builds próprias de um painel de controlo, um contentor. Faça a lista de todas e veja qual delas cada site usa de facto. No PHP-FPM, liste todos os processos master e o binário por trás de cada um:
pgrep -af 'php-fpm: master'
for p in $(pgrep -f 'php-fpm: master'); do sudo readlink /proc/$p/exe; done
Se um caminho terminar em «(deleted)», o pacote foi substituído em disco mas esse processo nunca foi reiniciado, por isso continua a correr o código antigo.
No Apache com mod_php, o módulo faz parte do próprio processo do Apache, pelo que é o pacote PHP instalado que tem de verificar.
2. Leia corretamente a versão
As distribuições fazem muitas vezes o backport das correções de segurança sem mudar o número de versão upstream, uma das razões por que a distribuição que escolhe importa (veja o nosso guia sobre como escolher o sistema operativo do seu VPS). O Ubuntu 24.04, por exemplo, corrigiu as duas CVE no pacote 8.3.6-0ubuntu0.24.04.13: continua a ser «8.3.6», mas está corrigido. Assim, uma versão abaixo da 8.3.36 num pacote da distribuição não significa, por si só, que esteja vulnerável. Quem esclarece é o tracker de segurança do seu fornecedor ou o changelog do pacote:
# Debian / Ubuntu
zgrep -cE 'CVE-2026-10961[89]' /usr/share/doc/php*-fpm/changelog.Debian.gz
# Pacotes de distribuições da família RHEL
rpm -q --changelog php-fpm | grep -cE 'CVE-2026-10961[89]'
Um resultado acima de zero significa que o pacote inclui as correções. As builds que acompanham a numeração upstream, como as de repositórios de terceiros, as de painéis de controlo e as imagens Docker oficiais, muitas vezes não indicam números de CVE, por isso nelas um zero não quer dizer nada: o teste é a própria versão. Os trackers de segurança públicos do Debian e do Ubuntu indicam a versão corrigida do pacote para cada versão da distribuição.
3. Atualize e depois reinicie todos os workers
Instalar o pacote não altera os processos que já estão a correr. Os workers do PHP-FPM e os processos filhos do Apache mantêm o código antigo em memória até serem reiniciados. Depois da atualização, reinicie o serviço por completo:
systemctl restart php8.3-fpm # ou php-fpm, conforme a sua distribuição
systemctl restart apache2 # ou httpd, se usar mod_php
Confirme depois que a atualização produziu efeito. A hora de arranque do serviço tem de ser posterior à hora de instalação do pacote:
systemctl show php8.3-fpm -p ActiveEnterTimestamp
rpm -q --last php-fpm # família RHEL
zgrep -h " upgrade .*php" /var/log/dpkg.log* | sort | tail # Debian / Ubuntu
4. Trate do PHP em fim de vida
Os servidores com PHP 8.1 ou anterior têm três opções: um backport do fornecedor (algumas distribuições e fornecedores de suporte de longa duração disponibilizam-no), os patches mínimos de código-fonte do projeto PHP para 7.4, 8.0 e 8.1, se compilar o PHP por conta própria, ou a migração para um ramo suportado. Das três, a migração é a única que também cobre a próxima falha crítica.
5. Se não puder atualizar hoje
O projeto PHP é claro quanto à inexistência de uma solução de contorno completa: «Não há mitigações completas, atualizar é a única opção segura.» Indica, ainda assim, medidas que fecham caminhos de ataque específicos enquanto agenda a atualização. Defina-as na configuração do pool do FPM ou do Apache, nunca com ini_set() nem com .user.ini, e reinicie depois todos os workers:
- Para a CVE-2026-109619:
session.upload_progress.enabled=0, que bloqueia os caminhos de sessão confirmados. No PHP 8.5, tambémfatal_error_backtraces=0. - Para a CVE-2026-109618:
memory_limit=-1, imposto ao nível administrativo. Isto retira o limite de memória do próprio PHP, por isso combine-o com um limite de memória ao nível do contentor ou do cgroup. O aviso do GitHub desaconselhaRLIMIT_ASouRLIMIT_DATA, que acionam o caminho vulnerável. O projeto PHP recomenda também limites baixos para o tamanho dos pedidos:post_max_sizeà volta de 8M onde não recebe uploads, 32M onde recebe.
Num ficheiro de pool do PHP-FPM:
php_admin_value[session.upload_progress.enabled] = 0
php_admin_value[memory_limit] = -1
Na configuração do Apache, para mod_php:
php_admin_flag session.upload_progress.enabled off
php_admin_value memory_limit -1
6. Parta do princípio de que um servidor exposto pode ter sido sondado
Se um servidor exposto à internet correu um PHP vulnerável a partir de meados de outubro, a correção fecha a porta mas não diz nada sobre quem entrou antes. A entrada no catálogo da CISA remete as agências federais dos EUA para uma triagem forense, e essa é uma fasquia sensata para toda a gente: reveja os registos web e os registos de erros do PHP à procura de pedidos POST multipart invulgares e de falhas de workers, procure ficheiros e processos do utilizador web que não tenha criado e mude as credenciais que o utilizador web conseguia ler.
Além da correção: reforce o PHP para a próxima falha
A correção resolve esta falha. Não resolve a próxima, que pode ser explorada antes de existir qualquer correção. As medidas abaixo não impedem que uma falha do PHP seja acionada. Decidem o que um atacante obtém quando isso acontece: os ficheiros de um único site, ou o servidor inteiro, as suas credenciais e tudo aquilo a que consegue chegar.
Dê a cada site o seu próprio utilizador PHP sem privilégios
Corra cada site no seu próprio pool do PHP-FPM, com o seu próprio utilizador de sistema, e nunca como root. A execução de código «como o utilizador do worker web», nas palavras do aviso do GitHub, passa então a ser execução como o utilizador de um único site, e não como o utilizador que consegue ler todos os sites do servidor:
[site1]
user = site1
group = site1
listen = /run/php/site1.sock
listen.owner = www-data
listen.group = www-data
listen.mode = 0660
pm = ondemand
pm.max_children = 10
Na família RHEL, use /run/php-fpm/site1.sock e listen.owner = nginx (ou apache). Feche o diretório de cada site aos restantes (modo 750), caso contrário os utilizadores de cada site continuam a conseguir ler os ficheiros uns dos outros.
Torne o código só de leitura e nunca execute ficheiros carregados
O utilizador PHP deve conseguir ler a aplicação, mas não alterá-la. Faça com que o código pertença a um utilizador de deploy separado e dê ao utilizador PHP permissão de escrita apenas nos diretórios que precisam dela, como os de uploads e de cache. Um worker que não consegue escrever na árvore de código não consegue reescrever a sua aplicação. Pode, ainda assim, deixar ficheiros nos diretórios com permissão de escrita, alterar a base de dados ou ficar residente num worker em execução, por isso trate todos os caminhos com permissão de escrita como não fiáveis, nunca inclua PHP a partir deles e reinicie os workers depois de um incidente. Depois, impeça a execução de PHP a partir desses diretórios. No nginx, coloque isto acima do seu bloco location principal do PHP:
location ~* ^/(?:uploads|wp-content/uploads)/.*\.(?:php\d*|phtml|phar)(?:/|$) {
deny all;
}
No mod_php, coloque php_admin_flag engine off num bloco <Directory> para o diretório de uploads.
Reduza o que o servidor expõe
- Remova as versões e extensões do PHP que nenhum site usa. Cada build instalada é mais uma para corrigir.
- Defina
expose_php = Offpara que as respostas não anunciem aos scanners a sua versão exata do PHP. - Apague todas as páginas
phpinfo(). - Limite o tamanho dos pedidos antes de chegarem ao PHP, no servidor web ou no proxy (
client_max_body_sizeno nginx). A recomendação do próprio projeto PHP para a CVE-2026-109618 inclui limites ao tamanho descodificado, ao número de campos e ao número de partes antes de os pedidos chegarem ao PHP. Limitar campos e partes exige uma regra num proxy ou numa WAF; se não recebe uploads por formulário, bloqueie aí os pedidos multipart.
Restrinja aquilo a que o PHP consegue chegar
Grande parte do que um atacante faz depois de entrar precisa de uma ligação para fora: descarregar um payload, abrir uma reverse shell, enviar dados roubados. A maioria dos workers web só precisa de chegar a uma base de dados, a uma cache e a algumas APIs conhecidas. O systemd consegue impor isso ao serviço PHP-FPM e, ao mesmo tempo, pôr os diretórios do sistema só de leitura para ele. Execute systemctl edit php8.3-fpm (ou php-fpm) e acrescente:
[Service]
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=full
IPAddressDeny=any
IPAddressAllow=localhost 10.0.0.0/8
Ajuste o IPAddressAllow ao que o PHP precisa realmente. Estas regras só olham para endereços IP: o DNS falha a menos que o seu resolver seja local (127.0.0.53 no Ubuntu) ou esteja na lista, as APIs externas precisam de ter os endereços na lista ou de um proxy de saída, e um FPM em TCP atrás de um proxy remoto precisa desse proxy na lista. O NoNewPrivileges também bloqueia o utilitário setgid que o Postfix usa para o mail(), por isso envie o correio por SMTP para um relay autorizado. Aplique isto apenas ao PHP-FPM, nunca ao apache2, ao httpd ou ao nginx. Teste em staging, reinicie e confirme que o PHP não consegue chegar a um endereço externo e que o site continua a funcionar.
Mantenha os segredos reduzidos e separados
Parta do princípio de que tudo o que o utilizador PHP consegue ler vai ser lido. Dê a cada site uma conta de base de dados apenas com os privilégios de que a aplicação precisa, guarde os ficheiros de configuração com credenciais fora da raiz web e legíveis apenas pelo utilizador desse site, e não deixe num servidor web chaves de que ele não precisa, como credenciais de backup ou tokens de fornecedores cloud. Uma intrusão passa então a custar os dados de uma base de dados, e não toda a sua infraestrutura.
Encurte o tempo até à correção
O PHP2Shell passou da correção a ataques confirmados em poucos dias. Subscreva os anúncios do projeto PHP e a lista de segurança da sua distribuição. Ative as atualizações de segurança automáticas (unattended-upgrades no Debian e no Ubuntu, dnf-automatic com apply_updates = yes na família RHEL). Confirme que cobrem o repositório de onde vem o seu PHP: no Debian e no Ubuntu, os repositórios PHP de terceiros não estão incluídos por predefinição. Combine-as com uma verificação dos serviços que ainda correm código antigo: needrestart no Debian e no Ubuntu, dnf needs-restarting -s na família RHEL. Uma atualização instalada mas ainda sem reinício deixa o servidor a correr o código vulnerável.
Saiba detetar o ataque e recuperar dele
- Envie os registos web, do PHP e do sistema para fora do servidor, para que um atacante que lá tenha entrado não os consiga apagar.
- Vigie as alterações à árvore de código com uma ferramenta de integridade de ficheiros como o AIDE. Numa árvore de código só de leitura, qualquer alteração fora de um deploy é um sinal.
- Crie alertas para quando o utilizador web arranca processos que normalmente não arranca, como uma shell interativa ou uma ferramenta de download. Defina primeiro uma referência: o
mail()do PHP arranca oshpara chamar o sendmail. - Guarde os backups num local onde o servidor web não consiga escrever nem apagar, e teste um restauro. Quando já não se pode confiar num servidor, reconstruí-lo a partir de uma imagem limpa e de um backup em bom estado é mais rápido do que limpá-lo.
Não confunda com a CVE-2026-115560
A 23 de outubro, o projeto PHP publicou um aviso informativo separado sobre um problema menos provável no progresso de upload da sessão, a CVE-2026-115560, que não faz parte do PHP2Shell e não foi corrigida nas versões de 21 de outubro. O conselho é a mesma definição: «Sugerimos que defina session.upload_progress.enabled=0 até à próxima versão regular de correção de bugs, para se proteger totalmente de um vetor de RCE improvável.»
A lista de verificação
Para o PHP2Shell, desde já:
- Liste todas as builds de PHP no servidor e qual delas serve cada site.
- Confirme que cada uma está na 8.5.12, 8.4.27, 8.3.36, 8.2.35 ou numa build do fornecedor que inclua as correções.
- Reinicie todos os pools do FPM e todas as instâncias do Apache, e compare a hora de arranque com a hora de instalação.
- Reconstrua e volte a fazer o deploy dos contentores a partir de imagens base corrigidas.
- Planeie já a saída do PHP 8.1 e anteriores, e da 8.2 antes de 31 de dezembro de 2026.
- Defina
session.upload_progress.enabled=0se não usar o progresso de upload. - Reveja os registos de qualquer servidor que tenha estado exposto a partir de meados de outubro.
Para a próxima:
- Um pool do PHP-FPM e um utilizador sem privilégios por site, nunca root.
- Código pertencente a um utilizador de deploy, só de leitura para o PHP; nenhuma execução de PHP nos diretórios de uploads.
- Builds e extensões do PHP sem uso removidas,
expose_php = Off, tamanho dos pedidos limitado antes do PHP. - Ligações de saída do PHP limitadas ao que a aplicação precisa.
- Contas de base de dados com o mínimo de privilégios, e nenhuma chave esquecida no servidor web.
- Atualizações de segurança automáticas e uma verificação de reinícios.
- Registos fora do servidor, alertas de integridade de ficheiros e backups testados em que o servidor web não consegue mexer.
Num servidor autogerido, cada um destes passos fica a seu cargo. Os Servidores Cloud da AFRICLOUD incluem acesso root total, por isso a versão do PHP, a sua atualização e o seu reinício ficam sob o seu controlo.
Fontes
- Projeto PHP: aviso de segurança e anúncios de versões, de 20 a 23 de outubro de 2026
- Avisos de segurança do PHP no GitHub: GHSA-w8hw-w9fx-j79w (CVE-2026-109618) e GHSA-6wmf-4rv5-p3rp (CVE-2026-109619)
- NVD: CVE-2026-109618 e CVE-2026-109619
- CISA: duas vulnerabilidades exploradas acrescentadas ao catálogo, 26 de outubro de 2026
- Manual do PHP: configuração de sessões em tempo de execução
- Ubuntu: CVE-2026-109618; Debian: tracker de segurança
- BleepingComputer: falhas PHP2Shell exploradas ativamente; Arctic Wolf: análise do PHP2Shell