BBLABS v2BBLABSv2
>Home>Labs
>New labs

Latest 3 labs

Loading…

View all labs →
>Creators>Ranking
>Learn

Learn bug bounty

AcademyGuides, cheatsheets and glossaryVulnerabilitiesXSS, SQLi, IDOR, SSRF and moreHunter RoadmapYour step-by-step bug bounty pathBlogBug bounty guides and news
>Business>Pricing
ES
Log inLog in
>Home>Labs>New labs>Creators>Ranking>Learn>Business>Pricing
ES
Sign inCreate account
  1. Home
  2. Labs
  3. Mutation XSS en el editor Trix (bypass sanitizer↔serializer)
HardVDP1h 25min

Mutation XSS en el editor Trix (bypass sanitizer↔serializer)

By @gorka

Escondes un onerror en data-trix-serialized-attributes que sobrevive a DOMPurify; cuando el editor-jefe abre el borrador, Trix re-materializa el handler y exfiltras la publishing key en su sesión.

10 views0 completedUpdated Aug 2026
Log in to start

Learn to find this bug

A real HackerOne report, reproduced for you to practice.

Create your account and practice real bugs that got paid. Download the environment, find it and learn the exact technique — your path to your first bounty.

650

hunters training

50

labs from real reports

380

completions

$200,000

in bounties practiced

40 flags captured this week
Create account
I already have an account

Access to all labs · no commitment · cancel anytime

Hunters who solved it

Be the first to solve it

Your name could show up here, at the very top.

Objectives

1
Reconocer que el composer monta un editor Trix 2.1.16, versión con bug conocido en su serializador.
2
Refutar el decoy 'Trix + DOMPurify ya sanea': un <img onerror> plano se borra al guardar.
3
Descubrir que los atributos data-trix-* sobreviven intactos al sanitizador porque parecen benignos.
4
Usar el attachment text/html seedeado como molde de anidación para el payload.
5
Construir un onerror escondido en data-trix-serialized-attributes dentro de un data-trix-attachment.
6
Entregar el borrador a revisión como contributor y confirmar la ejecución vía POST /collect y GET /collect/log.
7
Lograr que el editor-jefe (bot admin) re-hidrate el borrador y materialice el handler en su sesión.
8
Exfiltrar la publishing key (rol editor, 403 al contributor) con un fetch same-origin pese a la cookie httpOnly.

Information

Platform
HackerOne
Difficulty
Hard
Duration
1h 25min
Bounty
VDP
Completed
0
Creator
gorka@gorka
Updated
Aug 2026

Download the environment

Reproduce it and find the bug yourself

Create account

Tools

NavegadorBurp SuiteDevTools

Prerequisites

  • XSS y DOMPurify
  • Editor Trix / DOM
  • HTTP básico

Tags

XSSBACInformation Disclosure

Achievement you'll earn

Solve this lab to unlock this shareable achievement

BBLABS.ESLab Solved
HardVDP
// achievement_unlocked

Mutation XSS en el editor Trix (bypass sanitizer↔serializer)

XSSBACInformation Disclosure
Aug 2026
gorka
solved_by@gorkaMember since Mar 2026
bblabs.es// real bug bounty practice

Community writeups

Contenido del lab

Escenario

Redactia es un CMS editorial SaaS para medios y newsletters. Los colaboradores (contributor) redactan artículos en Redactia Studio, un composer que monta un editor de texto enriquecido. Cuando un colaborador termina un borrador lo "envía a revisión", y un editor-jefe (editor, rol administrador) lo abre en una vista de revisión que re-hidrata el contenido en un editor real para anotarlo y aprobarlo antes de publicar.

Eres pentester con ámbito sobre el entorno de staging. Tu rol es el de un colaborador de bajo privilegio: lo que escribes acaba, tarde o temprano, delante de alguien con más permisos que tú. La organización guarda una clave de publicación que solo un editor puede consultar; tú, como colaborador, recibes 403 si la pides.

Tu misión: demostrar que el flujo de revisión permite ejecutar código en la sesión del editor-jefe y exfiltrar esa clave de publicación (la flag).

Qué tienes

  • Una cuenta de colaborador de demostración: attacker@redactia.local / password123 — es tu punto de partida, no es en sí la vulnerabilidad.
  • El composer de Redactia Studio para redactar y enviar borradores a revisión.
  • Una vista pública de artículos ya publicados (/read/:slug) y una bandeja de estados que refleja cuándo alguien abre tu borrador.

Guía paso a paso

  1. Recon del editor. Entra como colaborador y abre "Nuevo borrador". El composer no es un <textarea>: es un editor de texto enriquecido de una librería open-source conocida. Identifica cuál y qué versión exacta carga (inspecciona el asset servido y lo que la librería expone en el navegador, p.ej. su objeto de configuración). Reconocer una versión con un fallo publicado es el primer signpost, igual que reconocer un CVE.
  2. Refuta el decoy obvio. Prueba lo evidente: mete un <script> o un <img> con un manejador de evento plano en un borrador y guárdalo. Vuelve a leerlo (o pide GET /api/drafts/:id) y observa qué queda: un sanitizador borra lo evidente. Conclusión con una sola prueba: el filtro básico funciona, necesitas otro canal.
  3. Encuentra lo que sobrevive. Vuelve a mirar el contenido guardado con atención. No todo se borra: los atributos que empiezan por data-trix-* sobreviven intactos porque el editor los necesita para reconstruir el documento. Adjunta una imagen en el composer y estudia la forma exacta que genera el editor: ahí hay un canal que el sanitizador considera inofensivo.
  4. Localiza el molde de anidación. No todos los adjuntos del editor son imágenes. Algunos borradores ya sembrados llevan adjuntos cuyo content es HTML incrustado (text/html: tarjetas, "datos clave") que el editor renderiza al reconstruir el documento. Fíjate en esos borradores: ese content es un lienzo de HTML que sobrevive al sanitizador dentro del adjunto. Es tu plantilla de anidación.
  5. Construye el payload. El editor guarda ciertos atributos "en reserva" dentro de una estructura serializada (data-trix-serialized-attributes) y, al reconstruir el documento, los vuelve a aplicar uno por uno, a ciegas vía setAttribute. Si dentro de esa reserva colocas un manejador de evento (un onerror) en lugar de un atributo benigno, el sanitizador ve un data-attribute inocuo, pero al re-hidratar el editor lo materializa como handler vivo. Coloca esa reserva en el HTML de un adjunto text/html que el editor re-renderiza en la vista de revisión.
  6. Entrega y confirma la ejecución. Guarda el borrador (por el composer o POST /api/drafts) y envíalo a revisión (POST /api/drafts/:id/submit). Empieza con un beacon inofensivo: haz que tu payload avise a POST /collect cuando se ejecute, y léelo en GET /collect/log. Ese es tu canal para saber que el editor-jefe abrió el borrador y tu código corrió en su sesión.
  7. Cosecha el impacto. Ya tienes ejecución en la sesión del editor-jefe. Su cookie es httpOnly, así que document.cookie no te da el token: no robes la cookie, usa la sesión. Hay un recurso reservado al rol editor (una "clave de publicación") que a ti te devuelve 403. Haz que tu handler lo pida con un fetch same-origin —el navegador adjunta la cookie del editor automáticamente— y reenvíe la respuesta a tu sink en /collect. La flag vive dentro de esa clave.

Canales de feedback (no vuelas a ciegas)

  • El estado del borrador (borrador → enviado → revisado) te dice que alguien lo abre después de ti.
  • El sanitizador es observable: comparas lo que envías con lo que devuelve GET /api/drafts/:id y ves exactamente qué atributos se conservan y cuáles se borran.
  • POST /collect + GET /collect/log es tu sink out-of-band: confirma con un beacon inofensivo que tu contenido ejecuta código cuando el editor-jefe lo revisa, antes de armar el exfil real.

Decoys (descártalos por razonamiento acotado)

  • "Trix + DOMPurify ya sanea": es el decoy central y es honesto. El sanitizador elimina de verdad <script>, javascript: y los on* sueltos. Pero descartarlo requiere una sola prueba: los data-trix-* sobreviven. El defecto no es que el sanitizador falle, sino que el editor materializa lo que el sanitizador aprobó.
  • La vista pública /read/:slug: renderiza HTML endurecido sin editor real, sin re-hidratar Trix. Ahí tu payload nunca despierta — es un callejón sin salida verificable. El vector es únicamente el content rico que pasa por el editor en la vista de revisión.
  • El campo de notas/comentarios: es texto plano escapado; no ejecuta nada.
  • Robar la cookie: la cookie del editor-jefe es httpOnly. document.cookie no la lee. El camino no es exfiltrar la cookie, sino aprovechar la sesión con un fetch same-origin.

La falla del laboratorio

Clase: Mutation XSS por discrepancia sanitizer↔serializer (CWE-79, Cross-site Scripting) con codificación/escape de salida incorrectos (CWE-116), que encadena a una autorización incorrecta del recurso admin-only (CWE-863) y a la restricción impropia de una capa de UI renderizada (CWE-1021). OWASP A03:2021 — Injection (XSS).

El fallo no está en que el sanitizador sea débil: DOMPurify hace bien su trabajo con <script>, javascript: y los on* sueltos. El problema es que el sanitizador y el motor de render no coinciden en qué es "seguro":

  • El sanitizado server-side conserva a propósito los atributos data-trix-* (el documento del editor los necesita para re-hidratarse), así que a ojos de DOMPurify un data-trix-serialized-attributes es un data-attribute benigno y sobrevive al filtro.
  • El serializador del editor, al reconstruir el documento, lee ese data-trix-serialized-attributes y re-aplica sus pares a ciegas vía el.setAttribute(name, value), sin re-sanear. Un onerror que era texto inerte en un data-attribute nace vivo como manejador de evento.
  • El handler solo se materializa en el render-through-editor de la vista de revisión (no en un innerHTML directo, ni en la vista pública endurecida): por eso el payload es inerte hasta que el editor-jefe abre el borrador en su editor real, y entonces el onerror se ejecuta same-origin en su sesión autenticada.
  • Como la cookie del editor-jefe es httpOnly, el XSS no roba la cookie: usa la sesión. Un fetch same-origin al recurso editor-only (que al colaborador le da 403) lee la clave de publicación con la sesión de la víctima y la exfiltra out-of-band.

La librería de edición implicada es Trix, un editor de texto enriquecido open-source real (Basecamp); el lab reproduce de forma anonimizada —empresa ficticia Redactia— un fallo conocido de su serializador presente en la versión 2.1.16 y corregido en 2.1.17 (que sanea recursivamente el serializado antes de re-aplicarlo). Nombrar Trix está permitido: es una librería pública y load-bearing para la fidelidad de la técnica; ningún objetivo, ID de reporte ni investigador real aparece en el lab.

Lección: un XSS sigue siendo crítico aunque la cookie de sesión sea httpOnly —el navegador la adjunta en cada fetch same-origin— y el impacto real se cosecha pivotando a un recurso que la víctima privilegiada sí puede leer y el atacante no. Remediación: actualizar el editor a la versión con el fix, no preservar data-trix-* a ciegas (validar/allowlist-ar el contenido serializado), aplicar una CSP estricta sin handlers inline, y renderizar el contenido en la revisión con el mismo perfil endurecido que la vista pública en lugar de re-hidratar un editor vivo solo para mostrar.

Laboratorio basado en la reproducción anonimizada (empresa ficticia Redactia) de una técnica real de mutation XSS sobre el serializador del editor open-source Trix.

Contact

Practice, learn and hack

Bug bounty practice platform with labs based on real reports. Learn ethical hacking in safe environments.

contact→

Follow us

YouTube
@0xGorka
X
@gorkaelbochi
LinkedIn
gorka-el-bochi-morillo
Instagram
@_.gorkaaa.b
Email
team@bblabs.es

Access every lab from €7.99/mo

New labs every week. Cancel anytime.

Create account

BBLabs is the bug bounty labs platform where you learn bug bounty with real vulnerabilities extracted from paid reports on HackerOne, Bugcrowd and Intigriti. Here you practice web hacking —XSS, SQLi, IDOR, SSRF, CSRF and more— in downloadable environments, capture the flag, read the writeup and apply the technique on active bug bounty programs.

BBLabs is the alternative to HackTheBox, TryHackMe and PentesterLab for those who want to practice bug bounty with real reports instead of artificial CTFs. From €7.99/mo, no commitment.

→ Learn bug bounty from scratch→ How to do bug bounty step by step→ Real bug bounty reports→ BBLabs for companies and academiesLabsAcademyVulnerabilitiesToolsHunter rankingXSS labsIDOR labsSSRF labsCSRF labsHackTheBox alternativeHack4u alternativeTryHackMe alternativePortSwigger alternativePentesterLab alternativeBug Bounty Labs comparisonHackerOne to practiceOffSec / OSCP alternativeINE / eWPT alternativeHTB Academy alternativeDVWA alternativeJuice Shop alternativeVulnHub alternativePentesterAcademy alternativeRoot-Me alternativeHackTheBox vs TryHackMeBest bug bounty platforms 2026BlogSpoilersWhat is bug bounty?How much do you earn in bug bounty?OWASP Top 10 explainedBest sites to practice web hackingHow to become an ethical hacker from scratchBurp Suite tutorial (Spanish)OSCP guide and prepGoogle Dorks for bug bountyHow much an ethical hacker earns in SpainBug bounty tools 2026Best cybersecurity certifications 2026Burp Suite tutorialsqlmap tutorialffuf web fuzzingnuclei tutorialHTTP Request SmugglingWAF bypassPrompt injection (LLM)Google Dorks
Made withand code
TermsPrivacyComparisonES

© 2026 BBLABS v2 — All rights reserved