Guia de Mitigação de Emergência PHP2Shell: Desativar session.upload_progress e Backports EOL
Guia de Mitigação de Emergência PHP2Shell: Desativar session.upload_progress e Backports EOL
<time datetime="2026-10-23">Verificado em 23 de outubro de 2026</time>
Quando CVE-2026-109618 e CVE-2026-109619 (PHP2Shell) atingem uma frota com centenas de servidores, um upgrade completo e imediato raramente é possível sem risco. Dependências de pacotes, invalidação de OPcache, compatibilidade de extensões e janelas de manutenção se interpõem. Ao mesmo tempo, a RCE não autenticada permanece enquanto a engine não for corrigida. Este guia descreve três caminhos testados, com diferentes níveis de intervenção.
- Opção A — hardening sem downtime via
php.ini(cerca de 30 segundos) - Opção B — filtragem na borda via Nginx ou Cloudflare
- Opção C — compilar o backport upstream
1d7dec1para PHP 8.1 EOL
1. Triagem e árvore de decisão
[ ALERTA: PHP2Shell (CVE-2026-109618 / 109619) ]
│
┌──────────────────┴──────────────────┐
│ Você controla php.ini / container? │
└──────────────────┬──────────────────┘
│
┌────────────────┴────────────────┐
▼ SIM ▼ NÃO (apenas proxy/CDN)
┌───────────────────────────┐ ┌───────────────────────────┐
│ Estratégia A (preferida) │ │ Estratégia B (perímetro) │
│ Callback off no php.ini │ │ bloquear multipart no │
│ sem downtime, ~30 s │ │ Nginx / Cloudflare │
└─────────────┬─────────────┘ └───────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────┐
│ PHP 8.1 e anteriores (EOL): Estratégia C │
│ ➜ compilar commit 1d7dec1 ou usar backport do vendor │
└─────────────────────────────────────────────────────────┘
A grande vantagem da Opção A: a vulnerabilidade depende exclusivamente do callback RFC1867. Desativá-lo remove toda a superfície de ataque sem alterar código da aplicação.
2. Opção A: Hardening sem downtime via php.ini
2.1 Configuração global
; /etc/php/8.3/fpm/php.ini
; Desativa o callback de tracking RFC1867
session.upload_progress.enabled = Off
session.upload_progress.cleanup = On
O caminho varia por distribuição. Não assuma; verifique:
php --ini
php-fpm -i | grep -i 'Loaded Configuration File'
2.2 Forçar no nível do pool
; /etc/php/8.3/fpm/pool.d/www.conf
php_admin_flag[session.upload_progress.enabled] = off
php_admin_flag não pode ser sobrescrito por ini_set() na aplicação. Em ambientes multi-tenant é essencial, caso contrário um único vHost pode anular a proteção.
2.3 Reload graceful e verificação
sudo systemctl reload php8.3-fpm
sudo systemctl status php8.3-fpm --no-pager
sudo kill -USR2 "$(cat /var/run/php-fpm.pid)"
php -r 'echo "upload_progress=", ini_get("session.upload_progress.enabled"), PHP_EOL;'
php-fpm -tt 2>&1 | grep -i upload_progress
Após o reload, a saída deve ser vazia ou 0. No FPM, verifique Local Value e Master Value, pois configs de pool podem sobrescrever a global.
2.4 Por que uploads normais não quebram
session.upload_progress é uma função de conforto de 2012 para barras de progresso. Não faz parte do pipeline real de arquivos. $_FILES, move_uploaded_file(), verificações de MIME e limites de tamanho permanecem inalterados. SPAs modernas usam a File API do navegador e eventos de progresso XHR/Fetch, independentes dessa diretiva.
2.5 Plano de rollback
sudo cp /etc/php/8.3/fpm/php.ini /etc/php/8.3/fpm/php.ini.bak-2026-10-23
sudo cp /etc/php/8.3/fpm/php.ini.bak-2026-10-23 /etc/php/8.3/fpm/php.ini
sudo systemctl reload php8.3-fpm
Um reload graceful mantém conexões existentes, mas pode causar latência breve dependendo de OPcache e sessão. Execute em janela de baixo tráfego e monitore a taxa de 5xx.
3. Opção B: Filtragem na borda via Nginx ou CDN
Quando não há acesso ao backend, a filtragem no perímetro é a alternativa. É solução temporária, não reparo.
# /etc/nginx/conf.d/php2shell.conf
map $http_content_type $php2shell_multipart {
default 0;
"~*multipart/form-data" 1;
}
server {
client_max_body_size 8m;
client_body_buffer_size 128k;
location ~ ^/(api|health|static|assets)/ {
if ($php2shell_multipart = 1) { return 415; }
if ($arg_PHP_SESSION_UPLOAD_PROGRESS) { return 403; }
if ($http_php_session_upload_progress) { return 403; }
client_max_body_size 256k;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_pass unix:/run/php/php8.3-fpm.sock;
fastcgi_read_timeout 20s;
}
location ^~ /upload/ {
client_max_body_size 64m;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_pass unix:/run/php/php8.3-fpm.sock;
}
}
sudo nginx -t && sudo systemctl reload nginx
No Cloudflare, crie regra WAF customizada combinando Content-Type: multipart/form-data com a presença de PHP_SESSION_UPLOAD_PROGRESS. Lembre-se de que atacantes podem codificar, fragmentar ou mover o nome do campo para headers. A regra é sinal, não cerca. Adicione rate-limits por IP de origem e bloqueie temporariamente IPs com muitos 415.
Também considere terminação TLS e cadeias de proxy. Se um CDN termina a conexão e repassa sem checagem, o worker PHP não vê o IP original. Para rate-limits e Fail2ban, confie em X-Forwarded-For ou CF-Connecting-IP apenas se o origin for acessível exclusivamente via proxy. Caso contrário, atacantes podem forjar headers e contornar bloqueios. Endureça o origin com firewall limitando às redes do proxy e rejeite conexões diretas da internet.
4. Opção C: Patch EOL PHP 8.1 (commit 1d7dec1)
PHP 8.1 não recebe mais releases upstream. O core team forneceu o patch mínimo 1d7dec1, que altera duas coisas.
4.1 Mudanças principais
/* ext/session/session.c — callback permanentemente desativado */
- if (!PS(rfc1867_enabled)) {
+ if (!PS(rfc1867_enabled) || true) {
return retval;
}
/* Zend/zend_alloc.c — OOM encerra imediatamente */
- zend_error_noreturn(E_ERROR, "Allowed memory size exhausted");
+ _exit(1);
A segunda mudança encerra o worker imediatamente em OOM ou overflow de inteiro. Isso remove a janela de tempo em que UAF e heap grooming poderiam ocorrer. O preço é um aborto duro do processo; disponibilidade cede lugar à integridade. Ajuste pm.max_children, emergency_restart_threshold e monitoramento conforme.
4.2 Procedimento de build
sudo apt build-dep php8.1 -y 2>/dev/null || sudo dnf builddep php -y
git clone --depth 1 -b PHP-8.1-CVE-2026-109618-109619 https://github.com/php/php-src.git
cd php-src
./buildconf --force
./configure --prefix=/usr/local/php81-patched --enable-fpm --with-fpm-user=www-data --with-fpm-group=www-data --with-openssl --with-zlib --enable-mbstring
make -j"$(nproc)"
sudo make install
sudo /usr/local/php81-patched/sbin/php-fpm -t
4.3 Verificação antes da produção
/usr/local/php81-patched/bin/php -v
/usr/local/php81-patched/bin/php -r 'echo ini_get("session.upload_progress.enabled"), PHP_EOL;'
systemctl daemon-reload
systemctl restart php81-patched-fpm
curl -sS https://sua-dominio.example/health -o /dev/null -w '%{http_code}\n'
Um interpretador compilado por você transfere a responsabilidade por futuras atualizações de segurança totalmente para sua equipe. Prefira backports de vendor (Ubuntu Pro, CloudLinux, Debian LTS). O build próprio é a última opção para ambientes que não podem migrar.
5. FAQ operacional
P1: session.upload_progress.enabled = Off quebra formulários de upload legados?
Apenas a barra de progresso desaparece. Sucesso, tratamento de erro, limites de tamanho e validação de MIME permanecem.
P2: Como confirmar que a mitigação funcionou?
CLI: php -r 'var_dump(ini_get("session.upload_progress.enabled"));'. FPM: página temporária phpinfo() verificando Local e Master Value. Adicionalmente, use regras Suricata/Falco para detectar tentativas de gatilho.
P3: Por que um limite de tamanho de body no WAF não basta? Payloads reais do PHP2Shell frequentemente têm 450–500 bytes. Limites de 2 MB ou 5 MB não capturam nada. A desativação no nível da engine é o único corte confiável.
P4: 8.5.12 / 8.4.27 / 8.3.36 / 8.2.35 são totalmente seguros? Para as duas CVE publicadas, contêm as correções. Se você não usa upload progress, mantenha-o desativado mesmo após o patch para minimizar a superfície de ataque.
P5: Preciso reverter a configuração após o patch? Não. A desativação pode permanecer. Documente a decisão para que não seja revertida acidentalmente em um futuro pedido de feature.
6. Comunicação em incidente
Medidas técnicas não bastam. Informe clientes de hosting, áreas de negócio e gestão em três níveis: (1) mitigação ativa, (2) cronograma de patch e janela de manutenção, (3) relatório final com risco residual. Para clientes com aplicações PHP próprias, forneça um folheto curto explicando a desativação de session.upload_progress e os comandos de verificação. Assim evita-se avalanche de suporte e garante-se que configurações por tenant também sejam ajustadas.
Documente também quais sistemas estão protegidos apenas pela Opção B. Esses sistemas têm maior risco residual e devem ser priorizados no backlog de patch. Um relatório de status simples por servidor — patcheado, mitigado, desprotegido — é a base para qualquer decisão subsequente.