Nuevos labs cada semana — Accede a todos desde 5€/mes

Nivel IntermedioGratis

Stored XSS vía SVG con href javascript: en chat — reclasificación de Self-XSS

Un payload SVG subido como adjunto. Filtro bypassed. Renderizado inline en el contexto principal. Cualquier participante del chat queda expuesto al click.

Gorka El Bochi9 de mayo de 202611 min

Respuesta rápida

Una plataforma de IA conversacional permitía adjuntar SVGs en el chat. Un parche previo bloqueaba javascript: en href de SVG, pero una variante del payload conseguía bypassarlo. Como el SVG se renderizaba inline en el DOM principal, cualquier participante del chat que hiciera click veía ejecutarse JavaScript en el contexto de la app, no solo el atacante. Reclasificación de Self-XSS → Stored XSS de impacto multi-usuario.


Contexto: clasificación original y por qué se reclasificó

El reporte se clasificó inicialmente como duplicado de un Self-XSS previo (variante que solo afecta al propio atacante y por tanto se considera de bajo impacto).

La reclasificación se basa en dos diferencias fundamentales:

  • Bypassa el filtro que neutralizaba el payload del reporte anterior.
  • El SVG se renderiza en el chat compartido, así que cualquier otro participante de la conversación queda expuesto al payload sin más acción que abrir el chat.

Eso lo convierte en Stored XSS con alcance a terceros, no en Self-XSS.


El payload

xml
<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
  <a href="javascript:alert(document.domain)">
    <text x="50" y="50" text-anchor="middle">click me</text>
  </a>
</svg>

El SVG se sube como adjunto en cualquier chat. La plataforma lo renderiza inline en la interfaz. El elemento <text> aparece como texto normal en pantalla. Al hacer click, se ejecuta alert(document.domain) en el contexto del dominio principal.

El payload usa el esquema javascript: en el atributo href de un elemento <a> dentro del SVG — el mismo vector que el reporte previo, pero con una codificación o estructura que supera el filtro que bloqueaba el original.


Por qué no es Self-XSS

La distinción clave entre Self-XSS y Stored XSS en este caso es el scope del almacenamiento:

Self-XSS (variante anterior)Este reporte
¿Dónde se ejecuta?Solo en la sesión del atacanteEn el chat compartido
¿Quién queda expuesto?Solo el propio atacanteCualquier participante del chat
¿Persiste para otros?NoSí — el SVG está almacenado en la conversación
¿Afecta a agentes de soporte?NoSí — si abren el chat

El payload está almacenado en el servidor dentro de la conversación. Cualquier usuario que tenga acceso a ese chat — invitado, participante o agente de soporte — ve el SVG renderizado y queda expuesto al payload.


CSP y renderizado inline

El reporte indica que el payload no se ejecuta desde el CDN y que bypassa el CSP. Esto apunta a que la plataforma renderiza el SVG directamente en el DOM del documento principal en lugar de cargarlo como recurso externo desde un CDN.

Cuando el SVG se renderiza inline en el DOM del documento principal, hereda el contexto de ejecución de la app. El javascript: en un href de un SVG inline no está bloqueado por la CSP si el evento de click lo desencadena el usuario, ya que no es un script externo sino una navegación a un URI javascript:.


Flujo de ataque

less
[Atacante] sube SVG con <a href="javascript:..."> en un chatServidor almacena el SVG en la conversación, filtro no lo bloqueaSVG renderizado inline en el chat UI[Atacante] comparte el link del chat (o un agente de soporte lo abre)
   ↓
[Víctima] abre el chatSVG visible con texto clickable[Víctima] hace click en el textojavascript: ejecutado en el contexto del dominio principal

Impacto observado

  • El SVG se almacena en el chat y persiste para todos los participantes.
  • La ejecución de JavaScript ocurre en el contexto del dominio principal, con acceso a document.domain.
  • El payload bypassa tanto el filtro de sanitización del SVG como la CSP vigente.
  • Los agentes de soporte que abran la conversación quedan igualmente expuestos.

Clasificación técnica

CampoValor
Tipo de vulnerabilidadStored XSS — SVG inline href javascript:
CWECWE-79 — Improper Neutralization of Input
VectorAdjunto SVG en chat, renderizado inline
Contexto de ejecuciónDOM principal de la app, no sandboxed
PersistenciaStored — el payload queda en la conversación
Interacción requeridaClick del usuario en el texto del SVG

Notas técnicas

  • Este reporte fue el origen de un parche que posteriormente fue bypassado en otro reporte. El parche sanitizaba el literal javascript: en href de SVGs, pero sin normalizar entidades HTML.
  • El SVG se renderiza inline en el DOM principal — eso es lo que permite que el javascript: tenga acceso al contexto de la página. Si se cargara como imagen externa o en un iframe sandboxed, el impacto sería diferente.
  • La diferencia técnica con el Self-XSS anterior consiste en una variante del payload que pasa donde el otro no, sugiriendo un filtro basado en strings en lugar de un parser real.

Labs relacionados

Practica Stored XSS en SVG y bypasses de filtros de sanitización: labs de XSS.

Practica esto en un lab

Xss

Resolver

Sigue aprendiendo · cuenta gratis

Guarda tu progreso, desbloquea payloads avanzados y rankea tus flags.

Crear cuenta

Artículos relacionados