
Descubriendo CVE-2025-22381: Inyección de Cabecera de Host en el Proyecto de Código Abierto Aggie
Análisis detallado y prueba de concepto para CVE-2025-22381, una vulnerabilidad de inyección de cabecera Host descubierta en el proyecto de código abierto Aggie.
Resumen de la vulnerabilidad+
CVE ID: CVE-2025-22381
Publicado: octubre de 2025 (asignación de MITRE)
Divulgado públicamente: febrero de 2026
Reportero: Anas Abderrahman Benbarek
Fecha de descubrimiento: 17 de septiembre de 2025
Proyecto afectado: TID-Lab/aggie
Versiones afectadas: todas las versiones (incluida la 2.6.1 y anteriores; sin corrección aplicada hasta febrero de 2026)
Severidad: media a alta (CVSS estimado ~7.1–7.5)
Impacto: permite ataques de phishing que conducen al robo del token de restablecimiento de contraseña y a una posible toma de control de la cuenta.
Contexto
Dedico bastante tiempo a revisar proyectos de node.js de código abierto en GitHub, especialmente aquellos que manejan flujos de autenticación. En septiembre de 2025, mientras examinaba el repositorio de Aggie, noté algo que destacó de inmediato en la lógica de restablecimiento de contraseña. Lo que comenzó como una lectura rutinaria de código terminó convirtiéndose en CVE-2025-22381: una vulnerabilidad clásica de inyección de cabecera Host que permite a un atacante controlar el dominio en los correos de restablecimiento de contraseña.
Cómo lo encontré
Cloné el repositorio y comencé a leer los archivos en lib/api/, centrándome en todo lo relacionado con la autenticación y la generación de correos electrónicos.
El archivo lib/api/reset-password.js contiene la lógica del endpoint para /reset-password. La parte crítica está dentro del helper sendEmail:
function sendEmail(user, req, callback) { var token = encodeToken(user);
mailer.sendFromTemplate({ template: 'forgotPassword', user: user, token: token, host: req.headers.host, // ← vulnerable protocol: req.protocol, acceptLanguage: req.headers['accept-language'] }, callback); }
La línea host: req.headers.host es el problema. En express, req.headers.host proviene directamente de la cabecera HTTP Host, que está completamente controlada por el atacante. No hay validación, ni lista blanca, ni respaldo a un dominio confiable desde la configuración.
Confirmación inicial
Rápidamente configuré una instancia local siguiendo las instrucciones del README (Ubuntu, nvm, npm install, secrets.json con SMTP de prueba), inicié el servidor y disparé un restablecimiento de contraseña. El enlace del correo generado usaba localhost:3000, como se esperaba.
Luego repetí la solicitud con una cabecera Host manipulada:
curl -X POST http://localhost:3000/reset-password
-H "Host: evil-phish.example"
-d "email=[email protected]"
El correo (capturado a través de MailHog) contenía: http://evil-phish.example/reset-password?token=...
Prueba concluyente. La aplicación confía en la cabecera Host proporcionada por el cliente al construir el enlace de restablecimiento.
Cómo funciona realmente el ataque
El atacante envía una solicitud de restablecimiento de contraseña para la dirección de correo de la víctima, pero configura la cabecera Host con un dominio que controla (p. ej., evil-phish.example).
Aggie genera un token de restablecimiento legítimo (del lado del servidor, con límite de tiempo, cifrado con el secreto de configuración).
El correo se envía con un enlace al dominio del atacante en lugar del real.
La víctima recibe el correo y hace clic en el enlace (condición de éxito del phishing).
La víctima aterriza en el servidor del atacante.
El servidor del atacante puede:
Simplemente mostrar una página falsa de «error al restablecer» y descartar silenciosamente el token, o
Capturar el token de la cadena de consulta (mediante registros del lado del servidor o JavaScript), o
Actuar como proxy de la solicitud hacia la instancia real de Aggie, capturar el token y reenviar al usuario a la página legítima de restablecimiento (para que la víctima no note nada raro de inmediato).
El punto clave: la inyección de cabecera Host por sí sola no permite al atacante usar el token directamente. El atacante aún necesita que la víctima visite el enlace malicioso para que el token llegue a su infraestructura. Por eso esta es una vulnerabilidad que habilita el phishing, y no una toma de control directa de la cuenta sin interacción del usuario.
Severidad técnica e impacto
Este es un problema de severidad media a alta, dependiendo del contexto:
AV:N: alcanzable por red
PR:N: no se requieren privilegios
AC:L: baja complejidad
UI:R: requiere interacción del usuario
S:C: el alcance puede cambiar (el impacto se extiende a la cuenta de la víctima en el dominio legítimo)
C:L / I:H: impacto en la confidencialidad e integridad de la cuenta de la víctima
Muchas bases de datos lo clasifican en el rango de CVSS ~7.1–7.5. Personalmente lo considero grave en entornos de producción donde Aggie se usa para monitoreo sensible (elecciones, crisis), ya que un phishing exitoso aquí puede llevar a la toma de control total de la cuenta.
Prueba de concepto (detallada y reproducible)
Entorno
Ubuntu 18.04/20.04 (como se recomienda)
Node 12.16 (según .nvmrc)
MailHog: ejecutándose localmente para la captura de correos (docker run -d -p 8025:8025 -p 1025:1025 mailhog/mailhog)
Aggie configurado con email.transport apuntando a localhost:1025
Paso a paso
Clonar e iniciar Aggie:
git clone https://github.com/TID-Lab/aggie.git cd aggie nvm install npm install cp config/secrets.json.example config/secrets.json edit secrets.json → set adminPassword, add test SMTP if needed npm start
Crear un usuario de prueba a través de la interfaz web o directamente en MongoDB.
Disparar el restablecimiento malicioso:
curl -i -X POST http://localhost:3000/reset-password
-H "Host: evil-phish.example"
-H "Content-Type: application/x-www-form-urlencoded"
-d "email=[email protected]"
Abrir MailHog: inspeccione el correo enviado. El enlace de restablecimiento apuntará a http://evil-phish.example/reset-password?token=...
Cronología de divulgación
Sep 17, 2025: descubierto + PoC local
Sep 17, 2025: se envió un correo a [email protected] con todos los detalles y el PoC
Sep 17, 2025: se presentó a MITRE (solicitud de servicio 1926730 / MCID15453119)
Oct 9, 2025: MITRE asignó CVE-2025-22381
Oct–Dec 2025: no se observó ningún parche ni respuesta pública
Feb 2026: divulgación pública (este artículo)
Corrección recomendada
Cambio de código
Reemplace la línea vulnerable por un valor confiable en lib/api/reset-password.js:
// In lib/api/reset-password.js, inside sendEmail() const config = require('../../config/secrets').get();
// Option A: Hard trust config value (recommended for single-domain) const host = config.appHost || 'localhost:3000';
// Then use it: mailer.sendFromTemplate({ template: 'forgotPassword', user: user, token: token, host: host, // Use the trusted variable protocol: config.environment === 'production' ? 'https' : req.protocol, acceptLanguage: req.headers['accept-language'] }, callback);
Configuración
Agregue a secrets.json:
"appHost": "https://your-real-domain.com"
Reflexiones finales
La inyección de cabecera host sigue siendo sorprendentemente común en 2025–2026, especialmente en proyectos que comenzaron hace años y no han sido auditados en profundidad. Aggie es una herramienta valiosa para la tecnología cívica y el monitoreo de crisis; espero que los mantenedores apliquen una corrección pronto.
Si mantienes o usas Aggie, revisa tu despliegue y aplica el parche manualmente hasta que llegue una versión oficial. No dudes en contactarme si tienes preguntas o quieres hablar sobre problemas similares en otros proyectos.
Gracias por leer y cuídense ahí fuera.