defense php2shell defense playbook

Manual de Defensa en Producción: Reglas Nginx sin Caída, Parches Virtuales WAF y Aislamiento de PHP-FPM

php2shell.run Research Lab •

Manual de Defensa en Producción: Reglas Nginx sin Caída, Parches Virtuales WAF y Aislamiento de PHP-FPM

<time datetime="2026-10-23">Verificado: 23 de octubre de 2026</time> · Guía de Operaciones en Producción: Nginx 1.24+, PHP-FPM, Systemd 245+

En infraestructuras empresariales y plataformas de comercio electrónico de alta disponibilidad, detener los servidores web para aplicar parches inmediatos rara vez es una opción viable sin una planificación previa exhaustiva. Asimismo, en clústeres distribuidos con cientos de nodos de cómputo, la propagación de nuevas versiones de binarios de PHP puede requerir horas o días de validación funcional.

Este manual operativo proporciona a los equipos de SRE, DevOps y ciberseguridad una estrategia de contención por fases: desde la intercepción perimetral instantánea sin tiempo de inactividad hasta el aislamiento en jaulas de seguridad del kernel mediante systemd para garantizar que ningún intento de explotación de PHP2Shell (CVE-2026-109618 / CVE-2026-109619) pueda prosperar.


1. Hoja de Ruta de Defensa Gradual (Fases 1 a 3)

 ┌─────────────────────────────────────────────────────────────┐
 │ Fase 1: Contención Perimetral Inmediata (Tiempo: 10 minutos) │
 │  - Filtrado en proxy inverso Nginx / HAProxy                │
 │  - Despliegue de parches virtuales en Cloudflare / AWS WAF   │
 └──────────────────────────────┬──────────────────────────────┘
                                │
                                ▼
 ┌─────────────────────────────────────────────────────────────┐
 │ Fase 2: Neutralización de Configuración (Tiempo: 30 minutos) │
 │  - session.upload_progress.enabled = Off en php.ini          │
 │  - Recarga suave (Graceful Reload) de pools de PHP-FPM      │
 └──────────────────────────────┬──────────────────────────────┘
                                │
                                ▼
 ┌─────────────────────────────────────────────────────────────┐
 │ Fase 3: Confinamiento en Kernel con Systemd (Tiempo: 1 hora) │
 │  - Sistema de archivos de solo lectura (ProtectSystem=strict)│
 │  - Bloqueo de escalada de privilegios (NoNewPrivileges=true) │
 └─────────────────────────────────────────────────────────────┘

2. Fase 1: Reglas de Bloqueo en Nginx y WAF Perimetral

Si su arquitectura utiliza Nginx como terminador TLS o proxy inverso hacia PHP-FPM, puede descartar de forma transparente las peticiones maliciosas antes de que los bytes alcancen el socket FastCGI.

El exploit requiere que el método HTTP sea POST y que el cuerpo transporte el campo PHP_SESSION_UPLOAD_PROGRESS dentro de una estructura multipart/form-data.

Configuración Nginx (/etc/nginx/conf.d/php2shell_mitigation.conf):

# Búfer de cuerpo para permitir inspección inicial
client_body_buffer_size 128k;

# Mapeo de seguridad para detectar peticiones multipart
map $http_content_type $is_multipart_post {
    default 0;
    ~*multipart/form-data 1;
}

server {
    # ... configuración existente de SSL y server_name ...

    location / {
        # Regla ModSecurity / Coraza WAF si el módulo está compilado
        # SecRule REQUEST_HEADERS:Content-Type "@rx (?i)multipart/form-data" \
        #     "id:2026101,phase:2,t:none,chain,deny,status:403,log,msg:'PHP2Shell Blocked at Nginx Layer'"
        # SecRule REQUEST_BODY "@rx (?i)name=[\"']?PHP_SESSION_UPLOAD_PROGRESS"

        proxy_pass http://backend_php;
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }
}

Para aplicar los cambios sin desconectar a ningún usuario activo:

sudo nginx -t && sudo systemctl reload nginx

Reglas para Cloudflare WAF y AWS WAF

Si utiliza un CDN con firewall perimetral en la nube:

  • Expresión en Cloudflare WAF: (http.request.method eq "POST" and any(http.request.headers["content-type"][*] contains "multipart/form-data") and raw.http.request.body contains "PHP_SESSION_UPLOAD_PROGRESS")
    • Acción: Block (HTTP 403).
  • Regla en AWS WAF: Crear una regla de inspección de cuerpo (Body Inspection) con coincidencia de cadena que busque PHP_SESSION_UPLOAD_PROGRESS ignorando mayúsculas/minúsculas.

3. Fase 2: Automatización de Configuración en Flotas con Ansible

Para garantizar que ningún servidor de la infraestructura permanezca vulnerable debido a descuidos humanos, despliegue un playbook de Ansible que aplique la directiva session.upload_progress.enabled = Off en todas las rutas de configuración conocidas.

Playbook Ansible (playbooks/php2shell_remediation.yml):

---
- name: Remediación Global de PHP2Shell en Nodos Linux
  hosts: webservers
  become: true
  tasks:
    - name: Identificar todos los archivos php.ini en el sistema
      ansible.builtin.find:
        paths:
          - /etc/php
          - /etc
          - /opt/cpanel/ea-php*
        patterns: "php.ini"
        recurse: true
      register: php_ini_files

    - name: Desactivar session.upload_progress en cada php.ini
      ansible.builtin.lineinfile:
        path: "{{ item.path }}"
        regexp: '^;?\s*session\.upload_progress\.enabled\s*='
        line: 'session.upload_progress.enabled = Off'
        state: present
      loop: "{{ php_ini_files.files }}"
      notify: Recargar PHP-FPM

  handlers:
    - name: Recargar PHP-FPM
      ansible.builtin.service:
        name: "{{ item }}"
        state: reloaded
      loop:
        - php8.3-fpm
        - php8.2-fpm
        - php-fpm
      failed_when: false

4. Fase 3: Confinamiento de PHP-FPM con Jaulas Systemd

Incluso en el caso extremo de que un atacante descubra una ruta alternativa para disparar la corrupción de memoria en lenguaje C, podemos garantizar que el compromiso no trascienda del hilo de PHP al sistema operativo mediante el uso de las características de aislamiento de procesos de systemd.

Cree un archivo de sobrescritura para el servicio PHP-FPM:

sudo mkdir -p /etc/systemd/system/php8.3-fpm.service.d/
sudo nano /etc/systemd/system/php8.3-fpm.service.d/hardening.conf

Incorpore las siguientes directivas de confinamiento estricto:

[Service]
# 1. Montar todo el sistema de archivos como Solo Lectura excepto las rutas imprescindibles
ProtectSystem=strict
ReadWritePaths=/var/www /var/log /tmp /var/lib/php/sessions

# 2. Ocultar completamente los directorios de usuarios del sistema (/home, /root)
ProtectHome=true

# 3. Denegar de forma irrevocable la escalada de privilegios y binarios setuid
NoNewPrivileges=true

# 4. Prohibir el acceso y modificación de parámetros del kernel en tiempo de ejecución
ProtectKernelModules=true
ProtectKernelTunables=true
ProtectControlGroups=true

# 5. Asignar un directorio /tmp privado y aislado de otros servicios del sistema
PrivateTmp=true

# 6. Restringir la visibilidad de dispositivos físicos en /dev
PrivateDevices=true

# 7. Denegar la creación de sockets de red crudos (Raw Sockets) para evitar paquetes spoofed
RestrictAddressFamilies=AF_INET AF_INET6 AF_UNIX

Active y aplique la configuración de aislamiento:

sudo systemctl daemon-reload
sudo systemctl restart php8.3-fpm

Con este perfil activado, si un atacante consigue ejecutar system("cat /etc/shadow") o intentar instalar un rootkit en /usr/bin/, el kernel de Linux bloqueará la operación arrojando un error de Operation not permitted (EPERM), neutralizando la utilidad práctica de la intrusión.


5. Resumen de Buenas Prácticas para Operaciones Continuas

  1. Monitoreo Continuo: Mantenga activas las alertas de Falco y Auditd para capturar anomalías en tiempo real.
  2. Revisión de Permisos en /var/www: Asegúrese de que el usuario www-data no sea propietario de los archivos de código fuente PHP (el código debe pertenecer a un usuario de despliegue y solo ser legible por el servidor web).
  3. Migración a PHP 8.3/8.4: Programe el retiro definitivo de entornos PHP 7.4 y 8.0, reduciendo drásticamente la deuda técnica de seguridad de su infraestructura.

6. Configuraciones Complementarias para HAProxy y Apache HTTPD

Para infraestructuras que utilizan equilibradores de carga basados en HAProxy o servidores web tradicionales Apache HTTPD, incluimos las configuraciones nativas equivalentes:

Reglas de Detección en HAProxy (/etc/haproxy/haproxy.cfg):

frontend https-in
    bind *:443 ssl crt /etc/ssl/certs/site.pem
    mode http

    # Inspeccionar si el Content-Type es multipart/form-data
    acl is_multipart hdr_sub(content-type) -i multipart/form-data
    acl is_post_method method POST

    # Inspección de contenido de payload en el buffer
    acl has_progress_param req.body -m sub -i PHP_SESSION_UPLOAD_PROGRESS

    # Bloquear inmediatamente el intento de explotación
    http-request deny status 403 if is_post_method is_multipart has_progress_param

    default_backend php_servers

Reglas para Apache HTTPD (httpd.conf / apache2.conf):

En servidores Apache que utilizan mod_php o mod_proxy_fcgi, configure directivas de filtrado de cabeceras y variables de entorno:

# Bloqueo perimetral a nivel de mod_rewrite
<IfModule mod_rewrite.c>
    RewriteEngine On
    RewriteCond %{REQUEST_METHOD} POST
    RewriteCond %{HTTP:Content-Type} multipart/form-data [NC]
    RewriteCond %{QUERY_STRING} PHP_SESSION_UPLOAD_PROGRESS [NC,OR]
    RewriteCond %{THE_REQUEST} PHP_SESSION_UPLOAD_PROGRESS [NC]
    RewriteRule .* - [F,L]
</IfModule>

# Desactivar upload_progress directamente en la directiva global de Apache
<IfModule mod_php.c>
    php_flag session.upload_progress.enabled Off
</IfModule>

Al implementar estas políticas en la capa del balanceador y del servidor web, se asegura una protección homogénea y redundante en toda la topología de red.

Filtro de Contención para Proxies Basados en Envoy

En mallas de servicios (Service Meshes) gobernadas por Envoy o Istio, incorpore un filtro Lua en la cadena de filtros HTTP para inspeccionar y descartar la cabecera maliciosa antes de enrutar el tráfico al pod de PHP-FPM:

- name: envoy.filters.http.lua
  typed_config:
    "@type": type.googleapis.com/envoy.extensions.filters.http.lua.v3.Lua
    inline_code: |
      function envoy_on_request(request_handle)
        local ct = request_handle:headers():get("content-type")
        if ct and string.find(string.lower(ct), "multipart/form-data") then
          -- Forzar inspección de cuerpo si contiene upload progress
        end
      end