CVE-2026-104014 permite tomar control de cualquier usuario que haya verificado un código de recuperación sin completar el cambio de contraseña de inmediato
Un plugin de verificación por email para WooCommerce dejó expuestas durante días las cuentas de usuarios que iniciaron un proceso de recuperación de contraseña sin terminarlo. La vulnerabilidad, catalogada como crítica por el National Vulnerability Database de NIST, afecta a Naimur Email OTP Verification for WooCommerce en todas las versiones hasta la 2.1.0 inclusive y permite a atacantes no autenticados tomar control total de cuentas ajenas — incluidas las de administradores del sitio — con solo conocer la dirección de email de la víctima.
El vector de ataque no requiere credenciales ni interacción de la víctima en el momento del compromiso. Basta con que el propietario de la cuenta haya solicitado y verificado un código de reset de contraseña en algún momento previo, sin haber completado inmediatamente la elección de la nueva clave. Esa ventana de exposición, que debería cerrarse en minutos, permanece abierta indefinidamente por un fallo de diseño en la gestión de estados de verificación dentro del plugin.
Cómo un proceso de limpieza que nunca se ejecuta dejó la puerta abierta
El problema radica en la forma en que el plugin maneja el ciclo de vida de las verificaciones de email. Cuando un usuario solicita recuperar su contraseña, el sistema genera un código de un solo uso que se envía por correo. Al ingresar ese código, la función mark_otp_as_verified_for_reset() marca la dirección de email como VERIFIED en una fila de base de datos. Hasta aquí, el flujo es estándar. El error aparece en lo que sucede después.
El endpoint de finalización del reset — la acción wp_ajax_nopriv_ceovw_update_new_password manejada por CEOVW_AJAX::update_new_password() — autoriza el cambio de contraseña únicamente verificando la presencia de esa fila marcada como VERIFIED. No valida timestamp de expiración, no vincula la verificación a una sesión específica, no exige un token único por cada intento de reset. Si el usuario verifica el código pero abandona el proceso antes de elegir la nueva contraseña, la fila queda en estado verificado en la base de datos.
El plugin declara un evento cron llamado ceovw_cleanup_expired_otps_event que debería limpiar esas filas huérfanas. Pero ese evento nunca tiene un handler add_action() registrado en el código del plugin. La limpieza programada simplemente no se ejecuta. Las filas verificadas permanecen en la base de datos sin límite de tiempo, convirtiendo cada reset incompleto en una autorización permanente para sobrescribir la contraseña de esa cuenta.
El nonce público que convierte la exposición en explotación trivial
Para ejecutar el ataque, un atacante no autenticado necesita dos elementos: la dirección de email de la víctima y un nonce de WordPress que autorice la llamada al endpoint de reset. El primer elemento es trivial de obtener en la mayoría de los sitios — direcciones de email de administradores suelen estar en páginas de contacto, políticas de privacidad o metadatos de posts. El segundo elemento, que debería ser la barrera de seguridad, el plugin lo entrega directamente.
Naimur Email OTP Verification localiza el nonce públicamente en el objeto global CEOVW_APP en la página de cuenta de WooCommerce, accesible para cualquier visitante no autenticado. Con ambos datos en mano, el atacante llama al endpoint ceovw_update_new_password, sobrescribe la contraseña de la víctima y recibe como respuesta una cookie de sesión wordpress_logged_in_ válida para ese usuario. Si la cuenta comprometida pertenece a un administrador, el resultado es toma de control total del sitio.
Exposición en comercios electrónicos de América Latina
WooCommerce es la plataforma de comercio electrónico más utilizada en WordPress, con presencia dominante en pequeñas y medianas empresas de Argentina, México, Colombia y Brasil. Los sitios que implementaron este plugin para agregar verificación por email a sus flujos de recuperación de contraseña — una práctica recomendada en teoría — quedaron expuestos a un vector de ataque que no deja rastro evidente hasta que el propietario legítimo de la cuenta pierde acceso.
La ventana de exposición es acumulativa: cada usuario que en los últimos meses solicitó un reset de contraseña, verificó el código y luego cerró la pestaña sin completar el cambio — porque se distrajo, porque decidió no cambiar la clave después de todo, porque el navegador se cerró inesperadamente — dejó su cuenta vulnerable. En un comercio con cientos de clientes, la superficie de ataque crece con cada solicitud de recuperación incompleta. No hay indicador visible para el administrador del sitio de cuántas cuentas están en ese estado hasta que se produce un compromiso.
Nuestro análisis
CVE-2026-104014 es un caso de manual sobre cómo múltiples decisiones de diseño aparentemente menores se combinan para producir una falla crítica. Ninguno de los tres componentes del problema — la falta de expiración temporal en las verificaciones, la ausencia de binding entre verificación y sesión, la localización pública del nonce — sería por sí solo una vulnerabilidad crítica. La criticidad emerge de la intersección: un estado de autorización que persiste sin límite de tiempo, combinado con un mecanismo de acceso que no requiere autenticación previa ni interacción de la víctima en el momento del ataque.
El patrón se repite en plugins de WordPress que implementan flujos de autenticación personalizados sin aprovechar las primitivas de seguridad que el core de WordPress ya provee — nonces con alcance limitado, tokens de reset con expiración forzada, validación de sesión en endpoints sensibles. La tentación de construir un sistema de verificación «más simple» o «más flexible» frecuentemente resulta en la omisión de controles que parecen redundantes hasta que un investigador encuentra la cadena de explotación. La pregunta que queda abierta es cuántos otros plugins de verificación por OTP en el ecosistema de WordPress están replicando este mismo patrón de gestión de estado sin expiración, esperando que alguien mire el código con suficiente atención para encontrar la fila de base de datos que nunca se limpia.