← Reportes xaferima.com English →

XXE/SSRF/XSS almacenados mediante evasión del sanitizador SVG

XXE/SSRF/XSS CVSS 7.1 HIGH CWE-79: Cross-site Scripting snapotter-hq/SnapOtter ≤ 2.2.0

GHSA-9c7h-xjrp-8gh3 · Asignación de CVE pendiente (GitHub CNA)

Resumen

Ambas rutas de subida — el POST /api/v1/upload independiente y el de biblioteca POST /api/v1/files/upload — ejecutaban el sanitizador SVG solo después de que el validador de imagen hubiera decodificado el archivo:

// apps/api/src/routes/files.ts:117 (misma forma en user-files.ts:337)
const safeBuffer = isValidImage && isSvgBuffer(buffer)
    ? sanitizeSvg(buffer)
    : buffer;

Como las rutas aceptan cualquier tipo de archivo, un SVG que librsvg (vía Sharp) no puede analizar se almacena de todos modos: un DOCTYPE que declara una entidad externa hace fallar el analizador, isValidImage es false, y el cortocircuito && omite sanitizeSvg() completamente. El archivo crudo se sirve luego como image/svg+xml con su DOCTYPE, declaraciones de entidad y cualquier marcado ejecutable intacto.

Dos debilidades relacionadas dentro del propio sanitizador:

Impacto

Causa Raíz

La sanitización estaba acoplada a la validación de imagen exitosa en lugar de ejecutarse en toda subida con SVG. La corrección invierte la responsabilidad: ambas rutas de subida ahora sanitizan el contenido SVG independientemente de si el decodificador de imagen acepta el archivo, el sanitizador elimina elementos peligrosos con prefijo de espacio de nombres, y la detección SVG más eliminación de DOCTYPE manejan un prólogo y valores entre comillas.

Prueba de Concepto

SVG creado cuyo DOCTYPE hace fallar el decodificador, evadiendo el sanitizador:



]>

  payload

La reproducción usó cuatro cargas — xss-event.svg (ejecución onload), xss-cookie.svg (exfiltración de cookies), xxe-file-read.svg y ssrf-metadata.svg (los dos vectores descartados posteriormente por el análisis de impacto del aviso) — subidas con una sesión autenticada, luego recuperadas vía la URL de descarga sin autenticación y abiertas en un navegador para confirmar la ejecución de script.

Demostraciones de Prueba de Concepto

Recorrido completo del exploit (1:41)

Explotación de extremo a extremo: crear carga maliciosa URL, disparar vía API, demostrar ejecución de JavaScript en contexto de víctima, exfiltrar token de sesión, lograr toma de cuenta.

Ejecución mínima de carga XSS (7 s)

Clip corto que muestra la carga XSS básica javascript:alert(1) ejecutándose en el contexto de respuesta — confirma el punto de reflexión sin la cadena completa de explotación.

Videos: evasión del sanitizador SVG de SnapOtter (GHSA-9c7h-xjrp-8gh3). Demos completas en la página de PoC Demos.

Cronología de Divulgación

FechaEvento
2026-09-11Vulnerabilidad descubierta durante auditoría autorizada
2026-09-12PoC y videos demo creados; informe enviado a contact@snapotter.com
2026-09-14Sin acuse de recibo tras 48h — seguimiento enviado
2026-09-19Mantenedor confirmó la vulnerabilidad, la clasificó High, crédito a xaferima
2026-09-20Degradación a HIGH aceptada (librsvg bloquea XXE); CVE a solicitar vía GitHub CNA
2026-11-03Aviso publicado — GHSA-9c7h-xjrp-8gh3 — y corregido en SnapOtter v2.3.0
PendienteAsignación de CVE vía GitHub CNA

Corrección

Sanitizar subidas SVG incondicionalmente — independiente del validador de imagen — eliminar elementos peligrosos con prefijo de espacio de nombres, y reforzar la detección SVG y eliminación de DOCTYPE:

// Ambas rutas de subida, tras la corrección:
const safeBuffer = isSvgBuffer(buffer) ? sanitizeSvg(buffer) : buffer;

Corregido en SnapOtter v2.3.0 (2026-11-03, también el lanzamiento de agradecimiento del millón de pulls del proyecto, que acredita este informe en las notas de versión). Aviso publicado 2026-11-03; asignación de CVE pendiente.

← Volver a todos los reportes