php2shell: las dos vulnerabilidades críticas de PHP y qué hacer hoy en tus servidores
CVE-2026-109618 y CVE-2026-109619 permiten ejecutar código sin autenticarse. Cómo saber si te afecta, actualizar bien, mitigar si no puedes y vigilar.
El 20 de octubre se filtraron dos fallos críticos en PHP antes de tiempo, y hoy, 21 de octubre, el equipo de PHP ha sacado los parches de urgencia. Se llaman php2shell (CVE-2026-109618 y CVE-2026-109619) y el nombre lo dice todo: una sola petición, sin usuario ni contraseña, puede acabar ejecutando comandos en tu servidor antes de que se ejecute una línea de tu aplicación. Algunas administraciones europeas han apagado webs como precaución y Cloudflare y watchTowr ya ven intentos de explotación. Aquí tienes lo que pasa, cómo saber si te toca y qué hacer hoy, en orden.
Es una guía genérica escrita el 21/10/2026 a partir de los avisos de PHP, CERT-EU, INCIBE y los análisis de Huntress y Tenable. En casa no tengo nada con PHP expuesto a internet, así que no te cuento un parcheo concreto, sino cómo lo haría. Si lees esto días después, mira antes el aviso oficial por si hay cambios.
1. Qué está pasando
Son dos fallos de memoria en el propio PHP, no en WordPress ni en ningún framework. Los dos tienen CVSS 9.2 y llegan a ejecución remota de código con los permisos del usuario de la web (www-data, apache…):
| CVE | Qué falla, en cristiano |
|---|---|
| CVE-2026-109618 | PHP se queda sin memoria (memory_limit) a mitad de leer la petición, por ejemplo una subida de ficheros. Al limpiar, usa datos que no había terminado de preparar. |
| CVE-2026-109619 | Tras un error fatal, PHP se guarda punteros a memoria ya liberada. La siguiente petición que atiende ese mismo proceso puede usarlos. |
Afecta a cualquier PHP que sirva páginas web: PHP-FPM (lo típico con Nginx), Apache con mod_php, php-cgi, FrankenPHP y LiteSpeed. El equipo de PHP considera mod_php el caso más peligroso, porque hay un exploit demostrado que funciona con la configuración por defecto.
Y PHP está en muchos más sitios de los que parece: WordPress, Drupal, Joomla, Moodle, Nextcloud, GLPI, paneles como cPanel o Plesk, y bastantes aplicaciones comerciales que lo llevan dentro sin decirlo.
2. ¿Me afecta?
Versiones corregidas:
| Rama | Versión con el arreglo |
|---|---|
| 8.5 | 8.5.12 |
| 8.4 | 8.4.27 |
| 8.3 | 8.3.36 |
| 8.2 | 8.2.35 |
| 7.4, 8.0, 8.1 | Sin versión oficial; solo parches de código para quien compila |
| 7.3 y anteriores | Sin arreglo. El fallo viene de PHP 5.4 |
Lo difícil no es la tabla, es encontrar todos los PHP que tienes. Yo empezaría por aquí en cada servidor Linux:
# Paquetes instalados (Debian/Ubuntu y RHEL/Rocky)
dpkg -l | grep -E '^ii +(php|libapache2-mod-php)'
rpm -qa | grep -i php
# Qué PHP está corriendo de verdad ahora mismo
ps -eo pid,lstart,cmd | grep -E 'php-fpm|php-cgi|apache2|httpd' | grep -v grep
# Versión del FPM y del módulo de Apache (no te fíes solo de `php -v`)
php-fpm8.3 -v 2>/dev/null; apache2ctl -M 2>/dev/null | grep -i php
php -v te dice la versión de la consola, que es un paquete aparte. Puedes tener php8.3-cli actualizado y php8.2-fpm sirviendo la web con la versión vieja.
Si usas contenedores, cada imagen lleva su propio PHP:
for c in $(docker ps -q); do
echo "== $(docker inspect -f '{{.Name}} ({{.Config.Image}})' $c)"
docker exec $c sh -c 'php -v 2>/dev/null | head -1'
done
Y no olvides lo que no es un servidor «de PHP»: aplicaciones de Windows que traen php-cgi.exe, appliances, el hosting compartido de la web de la asociación. El NCSC de Países Bajos mantiene una lista de software de terceros afectado en GitHub (NCSC-NL/php2shell). Para lo que esté publicado, el manual de ss -ltnp te ayuda a ver qué escucha en cada máquina.
3. Actualiza (y reinicia, que es donde se falla)
Las distribuciones recibieron el parche antes de hacerse público, así que lo normal es actualizar con el gestor de paquetes:
# Debian / Ubuntu
sudo apt update
sudo apt install --only-upgrade 'php*' 'libapache2-mod-php*'
# RHEL / Rocky / Alma
sudo dnf upgrade 'php*'
Ojo con el número de versión. Debian, Ubuntu y RHEL no suben a 8.2.35: meten el arreglo en su versión de siempre y cambian solo el sufijo del paquete. Lo que hay que comprobar es que el changelog nombre los CVE:
# Debian / Ubuntu
apt changelog php8.2-common 2>/dev/null | grep -E 'CVE-2026-10961[89]'
# RHEL y derivadas
rpm -q --changelog php-common | grep -E 'CVE-2026-10961[89]'
Si no salen, tu distribución todavía no lo ha publicado: pasa al punto 4 mientras tanto.
Después, reinicia todo lo que ejecuta PHP. Un paquete actualizado no sirve de nada si los procesos viejos siguen en memoria:
sudo systemctl restart php8.2-fpm # uno por cada versión de FPM que tengas
sudo systemctl restart apache2 # si usas mod_php (httpd en RHEL)
# Comprueba que los procesos son nuevos: la fecha de arranque debe ser de después del parche
ps -eo pid,lstart,cmd | grep -E 'php-fpm|apache2|httpd' | grep -v grep
Con Docker, las imágenes oficiales php:* ya se han reconstruido. Si tu imagen parte de una de ellas (FROM php:8.3-fpm), hay que reconstruirla forzando la descarga de la base, no basta con reiniciar el contenedor:
docker compose build --pull
docker compose up -d
Para imágenes de terceros (WordPress, Nextcloud…), docker compose pull && docker compose up -d y comprueba con el bucle del punto 2 que la versión ha cambiado. Si no sabes cómo se organiza eso, lo tienes en el manual de Docker Compose.
Si usas el repositorio de Remi (
rpms.remirepo.net), hoy no tendrá paquetes nuevos. El equipo de PHP recomienda pasar a los de la distribución o compilar.
4. Si no puedes actualizar hoy
El equipo de PHP lo dice claro: solo el parche lo arregla. Lo de abajo cierra los caminos de ataque conocidos y te da tiempo, pero no deja el servidor seguro.
En un pool de PHP-FPM (por ejemplo /etc/php/8.2/fpm/pool.d/www.conf):
; CVE-2026-109619: cierra el camino confirmado por session.upload_progress
php_admin_flag[session.upload_progress.enabled] = off
; CVE-2026-109618: quita el límite de memoria que dispara el fallo y recorta el tamaño de las peticiones
php_admin_value[memory_limit] = -1
php_admin_value[post_max_size] = 8M ; 32M si la aplicación necesita subir ficheros
php_admin_value[upload_max_filesize] = 6M ; siempre por debajo de post_max_size
; Solo en PHP 8.5
; php_admin_flag[fatal_error_backtraces] = off
Con Apache y mod_php, lo mismo en el VirtualHost o en un .conf global:
php_admin_flag session.upload_progress.enabled Off
php_admin_value memory_limit -1
php_admin_value post_max_size 8M
php_admin_value upload_max_filesize 6M
Uso php_admin_* a propósito: la aplicación no puede volver a cambiarlo. Revisa también que ningún otro pool, .htaccess o carpeta vuelva a poner un memory_limit finito.
Quitar el límite de memoria tiene un precio: un script descontrolado puede comerse la RAM del servidor. El propio aviso propone poner el tope fuera de PHP, con el cgroup del servicio. En systemd:
sudo systemctl edit php8.2-fpm
[Service]
MemoryMax=2G
OOMPolicy=continue
OOMPolicy=continue hace que, si se pasa, el kernel mate el proceso que más gasta y el servicio siga vivo. No uses ulimit -v ni RLIMIT_AS: provocan justo el fallo de memoria que se quiere evitar.
Después, sudo systemctl restart php8.2-fpm (o Apache) para que se aplique en todos los workers.
Dos capas más que ayudan:
- WAF delante. Cloudflare ha publicado reglas para los vectores conocidos, también en el plan gratuito. Solo protege lo que pasa por Cloudflare, y ellos mismos lo llaman «un parche temporal».
- Aislar lo que no tiene arreglo. Un PHP 7.x o anterior expuesto a internet hoy es una puerta abierta. Detrás de VPN o apagado hasta que se migre: es lo que recomienda CERT-EU y lo que han hecho algunas administraciones.
5. Vigila si alguien ha entrado
Si el servidor ha estado expuesto desde ayer, no basta con parchear: hay que mirar. Casi todas las intrusiones por PHP se delatan igual: el proceso de la web lanzando cosas que no debería (sh, bash, curl, wget, python, nc).
Con auditd puedes registrar cada programa que ejecuta el usuario de la web (en Debian www-data es el UID 33):
sudo auditctl -a always,exit -F arch=b64 -S execve -F uid=33 -k php_exec
sudo ausearch -k php_exec -i | grep -E 'comm=(sh|bash|dash|curl|wget|nc|python3?|perl)'
Si tienes Wazuh, esa regla de auditd ya llega a la consola y puedes alertar sobre ella. Lo conté con más calma en el artículo de Wazuh y GLPI.
Otras tres miradas rápidas:
# Ficheros PHP nuevos o modificados en la web desde el día de la filtración
sudo find /var/www -name '*.php' -newermt '2026-10-20' -ls
# Workers que mueren por fallo de memoria: los intentos de explotación suelen dejar este rastro
sudo grep -iE 'signal 11|SIGSEGV|Segmentation fault' /var/log/php*-fpm.log /var/log/apache2/error.log
# Tareas programadas y usuarios nuevos
sudo ls -la /etc/cron.* /var/spool/cron/crontabs; sudo tail -5 /etc/passwd
Un pico de signal 11 no prueba un ataque, pero en estos días merece una mirada. Si encuentras algo raro, aísla el servidor y guarda los logs antes de tocar nada.
Lo que no hay que hacer
- Fiarte de
php -v. Es el PHP de la consola, no necesariamente el que sirve la web. - Actualizar el paquete y no reiniciar. Los workers viejos siguen corriendo con el código vulnerable.
- Poner las mitigaciones con
ini_set()o.user.ini. El ataque ocurre antes de que se lean. - Buscar el número 8.2.35 en Debian o RHEL. Busca el CVE en el changelog.
- Descargar «exploits» de GitHub para probar. Ya circulan PoC falsos y alguno con malware dentro. El equipo de PHP no encontró ninguno funcional entre los que aparecieron tras la filtración.
- Pensar que el WAF basta. Es una capa más; el arreglo es el parche.
Resumen
- php2shell son dos fallos de PHP que permiten ejecutar código sin autenticarse; afectan a FPM,
mod_php, CGI, FrankenPHP y LiteSpeed. - Haz inventario de verdad: paquetes, procesos en marcha, contenedores y software que lleva PHP dentro.
- Actualiza a 8.5.12, 8.4.27, 8.3.36 u 8.2.35 (o al paquete de tu distribución que nombre los CVE) y reinicia todos los workers.
- Si no puedes hoy, desactiva
session.upload_progress, quita elmemory_limitcon un tope de cgroup y pon un WAF delante; lo que no tenga arreglo, fuera de internet. - Revisa qué ha lanzado el usuario de la web desde el 20 de octubre.
Lo peor de este tipo de fallos no es el parche, que se aplica en diez minutos: es el PHP que nadie recordaba tener. El inventario de hoy te servirá para el próximo.
¿Te has quedado con la idea?
Tres preguntas rápidas. Cada acierto suma 10 XP.