CVE-2026-105080 — RCE vía .recipe de calibre
Registro CVE · GHSA-m4hh-rqmf-c8hw
Resumen
ConvertX acepta archivos .recipe de calibre como entrada de conversión y los pasa a ebook-convert, que compila y ejecuta la receta como Python con los privilegios del proceso de conversión — root en el despliegue oficial de Docker Compose. Un atacante autenticado (o cualquiera que pueda alcanzar una instancia con registro abierto) sube un archivo .recipe malicioso, dispara una conversión a p.ej. EPUB, y obtiene ejecución remota de código completa. El compose.yaml por defecto también incluye JWT_SECRET hardcodeado en el entorno del proceso, así que la misma carga filtra el secreto usado para firmar tokens de sesión.
Impacto
- RCE como root dentro del contenedor — persistencia, lectura/escritura arbitraria de archivos.
- Fuga de secreto —
JWT_SECRETfiltrado vía el entorno del proceso permite forjar tokens de sesión arbitrarios (bypass total de autenticación). - Acceso a datos —
data/mydb.sqlite(hashes de contraseñas de usuarios) legible desde la shell. - Vector de ataque: red — subida de archivo + petición de conversión. No se requieren privilegios especiales más allá de una cuenta de ConvertX.
Causa Raíz
La configuración del conversor en src/converters/calibre.ts lista recipe entre los tipos de documento aceptados para el conversor calibre y ejecuta ebook-convert sin mitigación:
// src/converters/calibre.ts:29 — "recipe" aceptado en from.document
// src/converters/calibre.ts:70 — execFile("ebook-convert", ...) sin restricciones
Calibre trata los archivos .recipe como clases Python ejecutables (característica documentada para recetas estilo periódico). ConvertX nunca aislo este camino, y el compose.yaml oficial ejecuta el contenedor como root con JWT_SECRET hardcodeado.
Prueba de Concepto
Una receta mínima maliciosa que ejecuta un comando de shell al importar y aún satisface la interfaz de receta de calibre:
# evil.recipe — convertir receta → epub para disparar
import os
os.system("id >> /app/data/pwned.txt; env >> /app/data/pwned.txt")
from calibre.web.feeds.news import BasicNewsRecipe
class Poc(BasicNewsRecipe):
title = "poc"
oldest_article = 1
max_articles_per_feed = 1
feeds = []
Confirmado el 2026-09-21 contra v0.18.0 con el despliegue oficial Docker Compose: el archivo de salida contenía uid=0(root), el /etc/passwd completo del contenedor, y la variable de entorno JWT_SECRET. Una grabación de demo en vivo acompañó el aviso privado.
Demostración
Recorrido del exploit en vivo — registro, subida de .recipe malicioso, disparo de conversión, shell root y exfiltración de JWT_SECRET. También en xaferima.com/pocs.
Cronología de Divulgación
| Fecha | Evento |
|---|---|
| 2026-09-21 | Vulnerabilidad descubierta (análisis estático + revisión manual) |
| 2026-09-21 | PoC confirmado en Docker — shell root + fuga JWT_SECRET |
| 2026-09-21 | Aviso enviado vía GitHub Private Vulnerability Reporting |
| 2026-09-25 | Respuesta del mantenedor — corregido en v0.19.0 |
| 2026-09-29 | Solicitud de CVE enviada al mantenedor; solicitud MITRE CNA-LR CAN-2026-2038690 |
| 2026-10-03 | CVE-2026-105080 asignado — CVSS 3.1: 9.9 CRITICAL (4.0: 9.4) |
Corrección
- Eliminar
recipede los tipos de documento calibre aceptados (lista de permitidos estricta por conversor). - Ejecutar el contenedor como
USERno-root. - No exponer secretos (p.ej.
JWT_SECRET) al entorno del proceso de conversión. - Rotar el secreto por defecto hardcodeado en
compose.yaml.
Corregido en ConvertX v0.19.0. Asignación de CVE coordinada con el mantenedor y MITRE.