
Script de triaje de solo lectura para sitios WordPress tras PHP2Shell (CVE-2026-109618, CVE-2026-109619). Comprueba indicios de compromiso y escribe un informe por sitio.
Un script de triaje de solo lectura para sitios de WordPress tras PHP2Shell (CVE-2026-109618 y CVE-2026-109619), el par de errores críticos de ejecución remota de código en el motor de PHP divulgados el 21 de octubre de 2026.
Parchear PHP cierra la puerta, pero los honeypots de Amazon registraron un intento de explotación el 17 de octubre, cuatro días antes de la divulgación pública, cuando los errores aún estaban bajo embargo. Esa es la actividad más temprana que alguien ha publicado, no el día en que comenzaron los ataques, así que trátala como un límite inferior. Cualquier sitio que fuera accesible con un PHP sin parchear durante esa ventana puede que ya tenga una puerta trasera, y actualizar PHP no la eliminará. Este script comprueba uno o varios sitios de WordPress en busca de las cosas que un atacante con ejecución de código suele dejar atrás, y escribe un informe en Markdown para cada uno.
Un tercer error, CVE-2026-115560, se anunció el 23 de octubre. Todavía no tiene
una compilación corregida: las versiones puntuales de octubre (8.2.35, 8.3.36, 8.4.27, 8.5.12)
no lo cubren, y la corrección está prevista para la próxima versión regular en
noviembre. php.net pide a todos que establezcan session.upload_progress.enabled=0
en php.ini o en el pool de PHP-FPM hasta entonces. El script comprueba si eso
se ha hecho.
Nunca modifica ni elimina nada. Enumera, verifica e informa. Lo que hagas con los hallazgos es cosa tuya.
| Área | Comprobación |
|---|---|
| PHP | Versión de la CLI instalada frente a las versiones corregidas (8.2.35, 8.3.36, 8.4.27, 8.5.12). Señala las ramas al final de su vida útil, que no recibieron ninguna versión de php.net; consulta Ramas antiguas de PHP. |
| Progreso de subida | Si session.upload_progress.enabled=0, la mitigación de CVE-2026-115560, está establecida donde importa: la propia vista de PHP-FPM (php-fpm -i), cada php.ini legible, conf.d, el pool de FPM y la configuración de Apache. Los .user.ini y .htaccess a nivel de sitio se informan pero no se contabilizan, porque php.net dice que no funcionan para esta configuración. |
| Integridad del núcleo | wp core verify-checksums contra wordpress.org |
| Integridad de plugins | wp plugin verify-checksums --all, y enumera los plugins premium/personalizados que no puede verificar |
| Subidas | Cualquier archivo ejecutable por PHP bajo wp-content/uploads (ignora los stubs "Silence is golden") |
| Cambios recientes | Archivos PHP y de configuración modificados desde el inicio de la ventana (por defecto 2026-10-17). Los paquetes de traducción de WordPress (wp-content/languages/**/*.l10n.php) se cuentan por separado una vez que pasan una prueba estricta de forma; uno que la falle se señala. |
| Hooks de prepend | auto_prepend_file / auto_append_file en .user.ini, .htaccess, php.ini |
| Plugins must-use | Contenido de wp-content/mu-plugins (se ejecuta en cada petición, oculto de la lista normal de plugins) |
| Patrones de código | Firmas clásicas de web-shell (eval(base64_decode(...)), $_POST[...](), etc.) en wp-content |
| Usuarios | Todos los administradores, y cualquier cuenta registrada dentro de la ventana |
| Base de datos | siteurl/home, opciones y entradas que contienen scripts inyectados o código ofuscado |
| Cron | Eventos programados de WP-Cron |
| Registros | Registros de acceso: respuestas 5xx por día para la ventana frente a una línea base de hasta 7 días antes de ella, más las URL que devuelven 5xx en la ventana. Registros de errores y de PHP-FPM: caídas de workers (segfault/bus error). |
| Servidor | El crontab del usuario del sitio y las authorized_keys de SSH |
| Docker | Cada contenedor en ejecución con un binario php: la versión de PHP dentro de él, y si el contenedor sigue ejecutando una compilación más antigua que la imagen en disco (descargada pero no recreada) |
WP-CLI se ejecuta con --skip-plugins --skip-themes para que un plugin o
tema con puerta trasera no pueda ejecutarse mientras se realiza la comprobación.
En cada máquina que se compruebe:
bash 3.2+, find, grep y sort -V (estándar en hosts Linux; también funciona desde una estación de trabajo macOS estándar)php en el path, para la comprobación de versiónPara sitios remotos: acceso por clave SSH desde la máquina en la que ejecutas el script. El script se ejecuta a sí mismo por SSH; no es necesario instalar nada en el host remoto más allá de lo anterior.
curl -O https://raw.githubusercontent.com/brentmartinmiller/php2shell-check/main/php2shell-check.sh
chmod +x php2shell-check.sh
# One site
./php2shell-check.sh /var/www/example.com
less php2shell-reports/*.md
# Every site on the box
printf '%s\n' /var/www/*/public_html > sites.txt
./php2shell-check.sh sites.txt
cat php2shell-reports/SUMMARY.md
Ejecútalo como el usuario propietario de los archivos del sitio cuando sea posible. Funciona como root (WP-CLI recibe --allow-root), pero las comprobaciones de crontab y de claves SSH entonces inspeccionan las de root en lugar de las del usuario web.
git clone https://github.com/brentmartinmiller/php2shell-check.git
cd php2shell-check
cp sites.txt.example sites.txt # edit: one site per line
./php2shell-check.sh sites.txt
Para cada entrada user@host:path, el script se conecta por SSH, se ejecuta allí y trae el informe de vuelta a php2shell-reports/ en tu máquina. No se instala nada en el host remoto. SSH se ejecuta con BatchMode=yes, por lo que se requiere acceso basado en claves que funcione sin solicitar contraseña.
La mayoría de los hosts compartidos (Hostinger, SiteGround, Bluehost y similares) ofrecen SSH pero a menudo no tienen wp en el path. Compruébalo con which wp. Si no está, descarga WP-CLI una vez en tu directorio home y apunta el script hacia él:
curl -O https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar
WP="php $HOME/wp-cli.phar" ./php2shell-check.sh domains/example.com/public_html
Sin WP-CLI, las comprobaciones del sistema de archivos se siguen ejecutando y el informe indica qué se omitió. En cualquier caso, pregunta directamente al proveedor de alojamiento cuándo implementó el PHP parcheado, ya que eso es lo único que una comprobación a nivel de cuenta no puede ver.
sites.txt contiene un sitio por línea, ya sea una ruta local o user@host:path.
Las rutas remotas relativas son relativas al directorio home del usuario SSH. Las líneas
que comienzan con # se ignoran. Consulta sites.txt.example. El archivo está en
.gitignore para que los nombres de host de los clientes no terminen en un commit.
Los puertos SSH no estándar, las claves y los hosts de salto pertenecen a ~/.ssh/config:
Host client-a
HostName 203.0.113.10
Port 2222
User deploy
IdentityFile ~/.ssh/client-a
Luego enumera el sitio como client-a:/var/www/html.
php -v en un host Docker no te dice nada sobre los contenedores, y descargar
una imagen fija no parchea nada hasta que el contenedor se recrea a partir de ella. El sitio de un
equipo estuvo en 8.3.35 durante tres días después de la descarga exactamente por esa
razón.