El viernes 25 de septiembre CISA metió CVE-2026-87902 en el catálogo de vulnerabilidades explotadas. Es un 9.2, es el núcleo de WordPress y hay prueba de concepto pública desde el día 22. Todo eso es verdad y no contesta la única pregunta que te importa esta mañana: si tu web está expuesta o no. Esa respuesta no está en WordPress. Está en un fichero de configuración de PHP que seguramente nadie ha mirado desde que se montó el servidor.
Operamos infraestructura de terceros desde hace bastantes años y hemos visto el patrón lo suficiente como para reconocerlo de lejos: la aplicación la eligió alguien, la plataforma la montó otro, y cuando sale un aviso como este nadie es dueño de la respuesta. Así que vamos a separar las dos cosas, porque esta vez la distancia entre «el fallo existe» y «a ti te afecta» es más grande de lo normal.
Qué hace el fallo, en una frase
La ficha de CISA lo resume así: WordPress contiene una inclusión remota de ficheros que permite a un atacante no autenticado conseguir que la resolución de plantillas de página incluya un fichero .php local legible elegido por él, fuera de los directorios del tema activo, y que eso acabe en ejecución remota de código. El aviso de WordPress señala la función concreta: get_page_template(). La clasificación es CWE-98: control indebido del nombre de fichero en un include o require de PHP.
El parche salió el 22 de septiembre. Lo que conviene mirar no es la versión nueva, es la lista: WordPress publicó arreglo para veinticinco ramas, desde la 7.1.2 hasta la 4.7.37. La 4.7 salió el 6 de diciembre de 2016. El aviso explica el gesto como una cortesía, «as a courtesy to users on older branches»; nosotros lo leemos también como un reconocimiento de lo que sigue encendido ahí fuera, porque a una rama de hace casi diez años no se le hace un backport por deporte.
El 9.2 mide el daño, no tu exposición
El vector publicado es CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N. La mitad de la derecha explica por qué sale 9.2: confidencialidad, integridad y disponibilidad, todas altas. Sin credenciales, sin interacción, por red. Pero hay tres caracteres en medio que casi nadie lee y que son los que hablan de ti: AT:P. En CVSS 4.0, Attack Requirements vale N cuando no hace falta nada especial y P cuando sí hacen falta condiciones en el objetivo. Aquí vale P, y eso es una advertencia impresa en el propio número.
El investigador que lo reportó, Robert Ressl, publicó las condiciones una por una. Son cinco y tienen que darse todas a la vez:
- 1Una página publicada y accesible de forma anónima, seleccionable por
page_id. - 2Que esa página no tenga ya una plantilla personalizada válida asignada.
- 3Un directorio de primer nivel cuyo nombre empiece por
page-en una raíz de temas que WordPress recorra. Ojo al matiz, porque aquí se equivoca todo el mundo: es un directorio, no los ficherospage-loquesea.phpque lleva cualquier tema y que son perfectamente normales. El aviso de WordPress es el que nombra los temas: «This affects the legacy Twenty Twelve and Twenty Fourteen themes, as well as some popular third party themes such as Neve, Hestia, and Sydney». Ressl, por su parte, dice que las versiones de Twenty Twenty-Three, Twenty Twenty-Four y Twenty Twenty-Five que revisó no tenían ese directorio. - 4Un fichero
.phplocal legible que sirva de destino. - 5Que la política del sistema de ficheros permita esa inclusión.
Y ahora la frase del propio investigador que más nos gusta de todo el aviso, porque es la que no aparece en ningún titular: «I have not measured their prevalence across live sites». No ha medido cuántas webs cumplen esas condiciones. Nosotros tampoco, así que no vamos a decirte que medio internet está comprometido. Vamos a decirte cómo saber si lo estás tú.
La condición que decide no es de WordPress
Fíjate en la cuarta condición. Para que una inclusión local acabe en ejecución de código hace falta un .php que el atacante pueda influir. La demostración pública usó el clásico: pearcmd.php, el ejecutable de línea de comandos de PEAR, con la directiva register_argc_argv activada en PHP. Ese fichero no lo instala WordPress. Esa directiva no la configura WordPress. Las dos vienen de cómo alguien montó la plataforma.
register_argc_argv nació para la línea de comandos: rellena $argv y $argc con los argumentos del script. Lo que casi nadie tiene presente es lo que hace fuera de la línea de comandos, y lo dice el manual de PHP sin rodeos al deprecarlo: en SAPIs que no son CLI, $_SERVER['argv'] se deriva de la cadena de consulta de la petición. Es decir: con esa directiva activa, lo que un desconocido escribe después del interrogante en la URL entra en un script que esperaba argumentos de consola. Ahí está el canal.
Y aquí viene lo incómodo. El valor por defecto de register_argc_argv en PHP es 1, es decir, activada. El fichero php.ini-production que distribuye el propio PHP la pone en Off y lleva el comentario explícito «Production Value: Off». Pero ese fichero no se aplica solo: hay que copiarlo, y la documentación de las imágenes oficiales de PHP en Docker dice que la imagen «ships with the default php.ini-development and php.ini-production configuration files» y recomienda encarecidamente usar la de producción. Si nadie la copia, PHP arranca con sus valores compilados. Con la directiva activada. El aviso de WordPress no deja esto a la interpretación de nadie y da dos nombres propios: «The official php image for Docker is affected, and the default cPanel configuration is affected when PHP prior to 8.5 is in use». La imagen oficial y la configuración por defecto del panel sobre el que corre buena parte del hosting compartido de este país.
Dos WordPress idénticos, misma versión, mismo tema, misma página publicada: uno acaba en ejecución de código y el otro en un error. La diferencia no está en la aplicación. Está en si alguien copió un fichero de configuración el día que construyó la imagen. Eso es, literalmente, la forma de la frase que escribimos cuando lo del PNG que por dentro era PostScript: la opinión que cuenta es la del último programa de la cadena, y ese programa casi nunca es el que crees que estás auditando.
Ressl es honesto con el alcance de esto y conviene repetirlo tal cual, porque es la parte que se pierde en cuanto alguien resume: desactivar la directiva o quitar PEAR «breaks this demonstrated route. It does not repair WordPress's underlying file-inclusion flaw». Rompe esa ruta. El agujero sigue donde estaba.
La comprobación que te va a mentir
Si has llegado hasta aquí, lo siguiente que vas a hacer es entrar por SSH al servidor y escribir php -i | grep register_argc_argv. Es lo que haríamos todos. Y te va a devolver una respuesta falsa.
Lo acabamos de medir en una máquina con PHP 8.3.6 mientras escribíamos esto, y sale así de claro:
$ grep -n '^register_argc_argv' /etc/php/8.3/cli/php.ini 690:register_argc_argv = Off $ php -i | grep register_argc_argv register_argc_argv => On => On
El fichero de configuración dice Off, en la línea 690, y PHP contesta On. No es un error de nadie: el propio php.ini-production lleva la explicación escrita cinco líneas más arriba de la directiva — «Note: This directive is hardcoded to On for the CLI SAPI». La documentación de PHP lo confirma por el otro lado: la lista de directivas que el SAPI de consola impone no se puede cambiar desde el php.ini, y register_argc_argv está en ella.
La consecuencia práctica es más grande que esta CVE, y por eso la escribimos aparte: una auditoría de configuración hecha desde la shell no describe lo que hace tu web. El SAPI de consola y el de FPM cargan ficheros distintos —en el empaquetado de Debian y Ubuntu cada SAPI tiene su propio directorio: /etc/php/8.3/cli/, /etc/php/8.3/fpm/— y además hay directivas que la consola impone por encima de lo que diga el fichero. La única comprobación válida es la que pasa por el servidor web: un fichero de una línea servido por el mismo intérprete que sirve tu WordPress.
Las cuatro comprobaciones que sí contestan
No van por gravedad, van por el orden en que se pueden hacer. Ninguna necesita herramientas nuestras y las cuatro se hacen en una mañana:
- 1La versión, y si se actualiza sola. WordPress trae las actualizaciones automáticas de las versiones menores activadas por defecto —y desde la 5.6, en instalaciones nuevas, también las mayores—, así que muchos sitios ya están parcheados sin que nadie haya hecho nada. El problema son los que tienen esa función desactivada «porque una vez se rompió algo». Si es tu caso, la lista de ramas con arreglo llega hasta la 4.7.37: hay parche para tu versión, sea la que sea.
- 2El tema activo, buscando directorios y no ficheros. Un
ls -d wp-content/themes/*/page-*/contesta la tercera condición. Si no devuelve nada, esa condición no se cumple hoy, con el tema que tienes puesto hoy. Anótalo con esas dos cursivas. - 3La directiva, leída por el servidor web. Un fichero temporal con
<?php var_dump(ini_get('register_argc_argv'));, servido por la web, borrado inmediatamente después. Eso sí es tu configuración real. La salida dephp -idesde la consola, no. - 4Qué más hay en ese sistema de ficheros. La cuarta condición pide un
.phplegible fuera de WordPress. Busca PEAR, sí, pero la pregunta buena es más amplia y no se contesta con unfind: ¿qué otra cosa vive en el mismo servidor que tu web? La copia de la tienda de 2019, el panel que se instaló para una migración, el segundo WordPress «de pruebas». Cada uno de ellos está en el inventario de otro, no en el tuyo.
Hay una quinta pregunta que no es una comprobación y que es la que de verdad decide el tamaño del día malo: ¿qué alcanza ese host? Una inclusión de ficheros te da ejecución con el usuario del servidor web, y ese usuario ya puede leer wp-config.php, donde vive la contraseña de la base de datos. Y con la base de datos vienen las credenciales que guarden los plugins, las de SMTP incluidas. La pregunta no es si el atacante entra en la web. Es a qué llega desde la web. Si la respuesta incluye la red de la oficina, el problema de hoy no es WordPress.
Por qué se parchea igual aunque no cumplas las condiciones
Podríamos habernos ahorrado este apartado y quedaríamos igual de bien, pero sería deshonesto. Todo lo anterior sirve para decidir con qué prisa, no para decidir si. Y la razón es que ninguna de las cinco condiciones es estable. La tercera depende del tema, y un tema se cambia un jueves por la tarde sin avisar a sistemas. La cuarta depende de qué ficheros hay en el disco, y eso cambia cada vez que alguien instala un paquete. Puedes cumplir cero condiciones hoy y tres el mes que viene sin haber tocado una línea de WordPress. El fallo, mientras tanto, sigue en get_page_template().
Dicho lo cual, la prisa también es un dato, y este caso lo trae: Ressl publica la cronología completa. Reportó el fallo por HackerOne el 20 de julio, le confirmaron recepción al día siguiente, le avisaron del arreglo el 15 de septiembre y el aviso y la prueba de concepto salieron el 22. Sesenta y cuatro días de silencio ordenado. Y luego, tres: del 22 al 25 de septiembre, de «hay prueba de concepto» a «CISA lo mete en el catálogo por explotación activa». Ese es el reloj real. No lo marcas tú.
Y si sospechas que ya ha pasado: no reinstales primero
La ficha del catálogo lleva un campo que casi nadie mira y que en esta entrada vale Yes: forensicTriage. CISA pide recoger evidencia antes de remediar. Y el reflejo del sector ante una web tocada es exactamente el contrario: borrar el directorio, restaurar la copia de anoche y respirar. Eso hace dos cosas malas a la vez. Destruye lo único que contestaba «¿qué se llevaron y desde cuándo?», y restaura un WordPress con el mismo agujero y, si la copia es de antes de que te dieras cuenta, posiblemente con el mismo intruso dentro.
Es la misma trampa que contamos con el parche del kernel que es un reinicio, solo que aquí es todavía más fácil caer, porque restaurar una web parece gratis. El orden que defendemos es aburrido y funciona: copia en frío del sistema de ficheros y de la base de datos antes de tocar nada, los registros de acceso del servidor web a un sitio donde nadie los rote, y solo entonces parchear. Lo que no estuvieras enviando fuera ya, no lo vas a poder mirar después.
La web de la empresa es un servidor en producción
Lo que hace interesante a esta vulnerabilidad no es el 9.2. Es que pone por escrito algo que decimos en cada reunión y que suena a sermón hasta que aparece un caso: la web corporativa es un servidor en producción, no una pieza de marketing. Tiene versión, tiene dependencias, tiene una configuración que alguien eligió hace años y tiene una ruta hacia dentro. La diferencia con el ERP es que del ERP alguien es dueño.
Si hoy tuvieras que contestar en diez minutos cuántos WordPress tiene publicados tu empresa, en qué versión, con qué tema y sobre qué PHP, y la respuesta honesta es «habría que preguntar a la agencia», el problema no lo ha creado CVE-2026-87902. Lo ha enseñado. El fallo era inevitable; que se convierta en avería es una decisión de diseño, y esa decisión se toma mucho antes del día del aviso.
Fuentes (verificadas el 26-09-2026): ficha de CVE-2026-87902 —descripción, dateAdded 2026-09-25, dueDate 2026-09-28, forensicTriage «Yes», knownRansomwareCampaignUse «Unknown» y CWE-98— leída del fichero JSON primario del catálogo KEV de CISA, versión 2026.09.25, 1.726 entradas. Versiones afectadas (4.7.0–7.1.1), las 25 ramas con parche, CVSS 4.0 de 9.2, vector completo, descripción de get_page_template(), fecha de publicación (22-09-2026), la lista de temas afectados, la afectación de la imagen oficial de Docker y de la configuración por defecto de cPanel con PHP anterior a 8.5, y el backport a las ramas antiguas «as a courtesy to users on older branches» — GHSA-7hp8-65ch-5whp. Las cinco precondiciones, la observación de que los Twenty Twenty-Three, Twenty Twenty-Four y Twenty Twenty-Five revisados no traían directorio page- de primer nivel, el papel de pearcmd.php, la cronología de divulgación y las dos citas entrecomilladas del investigador — análisis de Robert Ressl. Valor por defecto («1»), condición de INI_PERDIR, deprecación en PHP 8.5 y derivación de $_SERVER['argv'] desde la cadena de consulta en SAPIs no CLI — manual de PHP; el SAPI de consola impone la directiva y no se puede cambiar desde el fichero — diferencias del CLI; «Production Value: Off» y la nota del hardcoded to On for the CLI SAPI, leídas en php.ini-production de la rama PHP-8.3 del repositorio de PHP y en el fichero distribuido con el paquete de PHP 8.3 de Ubuntu. Imágenes oficiales de PHP: documentación en Docker Hub. Actualizaciones automáticas por defecto — documentación de WordPress. La salida de consola que reproducimos es de una máquina propia con PHP 8.3.6 y paquetes de Ubuntu; en otra distribución o con otro paquete la línea 690 no tiene por qué ser la misma, pero el comportamiento del SAPI de consola sí lo es. No hemos medido cuántos sitios cumplen las cinco condiciones y no afirmamos nada al respecto: el propio investigador dice explícitamente que tampoco lo ha hecho. Foto de portada: WordPress Photo Directory (CC0).
¿Cuántos WordPress tiene publicados tu empresa? Nosotros lo contamos
En everyWAN hacemos el inventario de lo que tienes publicado a internet y de la plataforma que hay debajo: versión, tema, PHP, qué más vive en ese disco y —la pregunta importante— qué alcanza ese host desde donde está. Es parte de cómo entendemos la ciberseguridad y de cómo operamos datos y aplicaciones ajenas. Si la respuesta es que estás bien, te lo diremos igual y no te venderemos nada.
Hablar con everyWAN