Las 14:00 UTC del 21 de octubre. Esa fue la hora que eligió el equipo de seguridad de PHP para su versión de emergencia fuera de calendario, un día después de que se filtraran los detalles de CVE-2026-109618 y CVE-2026-109619 antes de la divulgación coordinada. Los dos tienen una puntuación CVSS v4.0 de 9.2, y los feeds de amenazas ya han bautizado a la pareja como "php2shell". watchTowr reprodujo el primero en laboratorio. En el momento de la filtración, nadie había confirmado una explotación masiva en el mundo real. Salieron correcciones para 8.2, 8.3, 8.4 y 8.5. Si tu servidor responde a 7.4, 8.0 u 8.1, nadie va a venir desde upstream a ayudarte.
La mayoría de las guías de triaje que he leído esta semana, incluida la de Medium que me animó a escribir esto, se toman en serio a ese grupo sin soporte y le ofrecen un plan de endurecimiento muy ordenado: desactivar allow_url_fopen, configurar open_basedir, meter una lista larga en disable_functions, bloquear los .php dentro de la carpeta de subidas en Nginx y cerrar el origen detrás de los rangos CIDR del WAF. Todo eso lo querría en cualquier máquina. Mi problema es la tranquilidad que viene en el paquete, la idea de que disable_functions frena una shell antes de que llegue al kernel. Hablamos de un fallo en la propia gestión de peticiones del runtime. Se puede explotar sin autenticación y se dispara antes de que carguen tu router, tu middleware de autenticación o tus ACL. Una directiva de php.ini la aplica el mismo código C que el atacante está corrompiendo.
No es una preocupación teórica. disable_functions tiene un historial largo y bastante bochornoso de bypasses, y ese historial viene de atacantes que solo tenían PHP de espacio de usuario para trabajar. En cuanto alguien consigue corrupción de memoria dentro del intérprete, ya ha dejado atrás la capa que comprueba si un nombre de función está permitido. La lista de funciones hace fallar los payloads perezosos, los que alguien copia de un pastebin y que se limitan a llamar a system(). Contra quien se lee el diff con calma, sirve de muy poco. Los propios datos de la guía dicen que los escáneres convierten los diffs de upstream en sondas funcionales entre dos y seis horas después del commit. Eso es mucho antes de que la reunión de aprobación de cambios para tu clúster de 7.4 llegue siquiera a aparecer en un calendario.
Aquí va el mejor argumento en mi contra, y lo voy a plantear bien. Muchas empresas no pueden actualizar sin previo aviso. Algunas tienen un sistema de facturación de 2019 que usa each() en once sitios. Otras tienen un módulo de proveedor cifrado con ionCube que solo carga en 7.4. Otras están atadas a un contrato de hosting que te da la versión que traiga el panel de control. Para ellas, el endurecimiento por capas y vigilar los sockets salientes de www-data es, siendo honestos, lo mejor que pueden hacer esta noche, y decirles que no sirve de nada sería cruel. Estoy de acuerdo con todo eso. Esta noche es lo correcto. Lo que no quiero es que acabe convertido en el plan del próximo trimestre.
Lo útil de este incidente es que le pone cifra a una decisión que ya habías tomado. Mantener 8.1 más allá de su fin de vida siempre fue una apuesta a que no aparecería nada crítico en el núcleo antes de migrar. Casi siempre la apuesta sale bien y nadie se acuerda de haberla hecho. Este mes salió mal para un montón de flotas, y la factura llegó en forma de fin de semana haciendo grep en los web roots buscando base64_decode y gzinflate, revisando crontabs y comprobando si /dev/shm está montado con noexec. Mete esas horas en la estimación de la migración, porque son los intereses que pagas por quedarte en una versión antigua.
Hay una parte poco vistosa de la guía que merece más atención de la que va a recibir: el inventario. which -a php, un repaso de /opt/cpanel/ea-php* y /opt/plesk/php, y docker exec en cada contenedor, porque el host no los lista. Me juego un café a que la mitad de los equipos que corrieron esta semana encontraron un binario de PHP que no sabían que tenían. Yo diría que un worker de colas con una build distinta a la del pool de FPM, o una imagen sidecar que alguien fijó a php:8.0 en 2022 y olvidó. A la gente de Go le encanta decir que un binario estático no tiene runtime que parchear. Muy bien, hasta que les toca recompilar cuarenta servicios porque el toolchain tenía un CVE. El grafo de dependencias de todo el mundo es más grande de lo que cree. Un aviso del núcleo simplemente hace visible el nuestro de golpe.
Así que, una vez salido el parche y completada la checklist: lleva todos los runtimes que tengas a una rama con soporte, y trata el endurecimiento de php.ini como un cinturón de seguridad extra, no como la excusa para no actualizar. Ir una versión menor por detrás es una deuda manejable. Estar en una rama sin soporte significa apostar tu seguridad a que no se descubra nada grave en el núcleo, y esta semana esa apuesta se perdió.
Lo que de verdad quiero saber de quienes gestionáis flotas reales es cómo conseguís meter los runtimes sin soporte en el presupuesto antes de que un incidente os obligue. ¿Hay algún argumento, alguna métrica o algún informe mensual que de verdad haya conseguido que la dirección financiara una migración desde 7.4 en tu empresa? ¿O hizo falta un susto como php2shell cada vez?




Comentarios
Aún no hay comentarios — escribe el primero.
Inicia la conversación
Sin cuenta ni contraseña — introduce tu correo y te enviamos un enlace de acceso de un solo uso. ¿Primera vez? Todo se configura automáticamente.
Tu valoración se aplicará automáticamente al iniciar sesión.
Revisa tu bandeja de entrada
Hemos enviado un enlace de acceso a …. Ábrelo en este dispositivo — esta pestaña te conectará automáticamente.
¿No llega nada? Mira en la carpeta de spam — y marca el correo como «No es spam» para que la próxima vez llegue directo.