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.
La checklist que repaso en cada objetivo nuevo
47 comprobaciones ordenadas por coste: primero lo que te puede meter en un lío, luego lo barato, y al final lo caro — que es donde están los bounties grandes. Te la mando al correo ahora mismo.
Te das de baja en un clic, desde cualquier correo.
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.
