Respuesta rápida
Secondary context XSS surge cuando el input pasa por dos pipelines de parsing: el primero escapa lo que entiende como peligroso, el segundo re-interpreta lo que el primero ignoró. Markdown renderers, BBCode parsers, custom template engines, latex-to-HTML, SVG inline serialization — todos son secondary contexts donde los caracteres "seguros" del primary se convierten en payloads. El truco: encontrar el delta entre lo que el sanitizer mira y lo que el render finalmente emite.
El modelo de threat
Pipeline típico vulnerable:
User input
↓
[Primary parser] ← escapa <, >, " según context HTML
↓
[Secondary parser] ← markdown / BBCode / template engine → emite NUEVO HTML
↓
DOM render ← ejecuta el HTML resultante
El sanitizer suele aplicarse en el primary o en el output final. Si solo aplica en el primary, el secondary tiene libertad para emitir lo que quiera. Si aplica en el output final, el sanitizer está protegido — pero solo si conoce todos los outputs posibles del secondary.
Markdown — el secondary context #1
Markdown→HTML es el caso más explotado. CommonMark/markdown-it/marked/showdown todos tienen comportamientos diferentes en edge cases.
Vector básico — link sin sanitize
[click me](javascript:alert(1))
Algunos parsers emiten:
<a href="javascript:alert(1)">click me</a>
Solución típica: parser bloquea javascript:. Pero:
[click me](javascript:alert(1))
[click me](javascript:alert(1))
[click me](java	script:alert(1))
[click me](java%0ascript:alert(1))
CommonMark spec dice "preserve the URL as-is, decode HTML entities at render time". Algunos parsers decodan, otros no. La inconsistencia es el bypass.
Vector — HTML raw inline
Por defecto la mayoría de parsers permiten HTML raw mezclado con markdown:
Hola
<img src=x onerror=alert(1)>
mundo
Mitigación común: pasar el output por DOMPurify. Pero DOMPurify trabaja en el HTML final, no en markdown. Si el parser emite HTML válido que DOMPurify acepta + un side effect, el chain pasa.
Vector — referencias y autolinks
[click][autolink]
[autolink]: javascript:alert(1)
Reference-style links. El parser puede decodar el destino later, después del sanitize.
Vector — code blocks que escapan al render
Markdown permite lang hint en code blocks. Un payload típico es un bloque "html" con un payload dentro:
```html
<img src=x onerror=alert(1)>
```
Si el renderer pasa el code block through un syntax highlighter (Prism, highlight.js, Shiki) que emite HTML "structured", el sanitizer del output puede asumir que está dentro de <pre><code> y permitir tags... y algunos highlighters serializan mal.
Sigue leyendo el chain completo
La parte que falta incluye el PoC paso a paso, código de explotación y la cadena completa que llevó al impacto. Disponible para suscriptores.
Practica esto en un lab
Secondary Context
Sigue aprendiendo · cuenta gratis
Guarda tu progreso, desbloquea payloads avanzados y rankea tus flags.
Artículos relacionados
Stored XSS en nombres de plantilla — del campo más aburrido al domain takeover
Un campo de título en una plantilla, sin sanitizar, en una sesión con permisos sobre dominios. Bounty real de €1.200. Cómo encontrar XSS donde nadie mira.
DOM XSS — gadgets, postMessage handlers y CVE-2025-59840
DOM XSS no es solo innerHTML. Sources/sinks, gadget chains via toString(), postMessage handlers sin origin check, hash-based routing rotos.
postMessage — vulnerabilidades comunes: origin bypass, XSS sink, IDOR cross-window
Cómo identificar y explotar vulnerabilidades en window.postMessage(): listeners sin validación de origin, payloads JSON inseguros que llegan a DOM XSS, IDOR cross-origin.