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. Stored XSS en el HTML exportado de transcripciones de un helpdesk
MediumVDP45 min

Stored XSS en el HTML exportado de transcripciones de un helpdesk

By @gorka

La app viva escapa el chat, pero el HTML exportado por el agente inserta el mensaje sin escapar ni CSP: un bot agente lo abre, ejecuta el XSS y exfiltra una nota interna que porta la flag.

9 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
Abrir el LiveChat como visitante anónimo y observar cómo el widget envía y guarda tu mensaje.
2
Comprobar que el chat en vivo escapa el HTML: una sola prueba refuta que el XSS viva ahí.
3
Descubrir el sink real: exportar tu propia transcripción a HTML y abrir el fichero servido.
4
Verificar que el export inserta tu mensaje sin escapar y sin CSP, y que ejecuta el payload.
5
Plantar una etiqueta img con onerror que lea el DOM y lo mande a un buzón público que controlas.
6
Solicitar revisión de un agente para encolar la conversación en la consola de staff.
7
Conseguir que el bot agente exporte con notas internas y abra el HTML en su sesión.
8
Leer el visor del buzón y recuperar la nota interna exfiltrada que porta la flag.
9
Descartar los decoys: chat vivo, email-transcript y export en modo texto.

Information

Platform
HackerOne
Difficulty
Medium
Duration
45 min
Bounty
VDP
Completed
0
Creator
gorka@gorka
Updated
Aug 2026

Download the environment

Reproduce it and find the bug yourself

Create account

Tools

Navegador webBurp SuitecurlPython 3

Prerequisites

  • XSS almacenado
  • HTML y JS básico
  • Contexto de un helpdesk

Tags

XSSInformation Disclosure

Achievement you'll earn

Solve this lab to unlock this shareable achievement

BBLABS.ESLab Solved
MediumVDP
// achievement_unlocked

Stored XSS en el HTML exportado de transcripciones de un helpdesk

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

Community writeups

Contenido del lab

Escenario

Deskoria es un helpdesk SaaS: su producto, Deskoria Desk, ofrece un widget de LiveChat embebible donde cualquiera puede abrir una conversación y escribir sin cuenta, y una consola interna donde los agentes de soporte gestionan esas conversaciones y exportan las transcripciones a HTML para adjuntarlas a tickets o pasarlas al siguiente turno.

Tienes ámbito staging como pentester. No dispones de credenciales de staff: tu única superficie es la entrada pública del chat. La misión es hacer que un dato que solo ve el equipo de soporte acabe en tus manos.

Qué tienes

  • El widget de LiveChat en la landing (botón "Probar el LiveChat (sin cuenta)"), tu único punto de entrada.
  • La capacidad de descargar tu propia transcripción en varios formatos.
  • Un buzón público y un visor legible donde puedes recibir y leer lo que un payload te mande.
  • Nada del lado de staff: las contraseñas de los agentes son aleatorias por arranque y no forman parte de la solución. La cadena no se resuelve con credenciales, sino con un input.

Guía paso a paso

  1. Recon de la entrada anónima. Abre el LiveChat sin registrarte y, con la pestaña Network abierta, mira qué peticiones dispara el widget al cargar y al enviar un mensaje. Los nombres de campo que necesitas (quién eres, en qué conversación estás, qué escribes) te los da la propia app, sin adivinar nada. Tu mensaje viaja a la conversación y se guarda tal cual.

  2. Sonda el chat en vivo (primer decoy). Escribe algo que "signifique" HTML, no solo texto plano, y observa cómo lo trata la app cuando te lo devuelve. La vista en vivo lo muestra escapado: aparece como texto literal. Con esa sola prueba puedes concluir que ese render es seguro. La pregunta deja de ser "¿es vulnerable el chat?" y pasa a ser "¿en qué otro sitio termina apareciendo lo que escribí?".

  3. Busca el otro sitio. La app no solo pinta tu conversación en pantalla: también te deja llevártela. Hay más de una forma de obtener una copia de tu propio chat. Genera esas copias y compáralas — no todas tratan tu mensaje igual. Una de ellas es un candidato mucho mejor para que tu input "signifique" HTML de verdad.

  4. Confirma el sink con tu propia copia. Abre esa copia y míralas de cerca: contenido y cabeceras. Si tu payload aparece intacto, sin neutralizar, y nada en la respuesta le impone una política que frene la ejecución (no hay Content-Security-Policy), tienes tu sink: abrir ese fichero en un navegador dispara lo que plantaste. Verifícalo con tu propia copia antes de involucrar a nadie.

  5. Entiende el gating. Tu copia dispara el payload, pero el dato que buscas no está en ella: es material del equipo de soporte y tu versión no lo incluye (lo dice ella misma). Ese material solo se materializa cuando un agente genera su versión de la copia. La deducción clave: no necesitas leer el dato tú — necesitas que sea un agente quien abra un artefacto que tú controlas.

  6. Arma el payload. Planta en un mensaje una etiqueta que ejecute JavaScript al renderizarse (por ejemplo, una imagen rota con un manejador onerror) que lea el contenido de la página y lo mande al buzón público que controlas. Ese mensaje queda persistido crudo, listo para el sink.

  7. Encola la conversación (paso ciego con feedback). En tu conversación hay una forma de pedir que el equipo la revise; úsala para que entre en la cola de los agentes. Un agente acabará procesándola: verás cambiar el estado de la conversación, tu señal de que el bot la tomó. Cuando abra su copia (que sí lleva las notas internas), tu payload correrá en su sesión con el dato de staff presente en la página.

  8. Recoge la exfiltración. Revisa el visor del buzón. Entre lo que llega, uno de los mensajes traerá la nota interna del equipo, y dentro de ella está la cadena FLAG{...}. Distingue ese beacon (el que trae la nota) de tu propia copia de prueba.

Canales de feedback (no vuelas a ciegas)

  • El render del chat en vivo te confirma en una prueba que ese sink está escapado y no es el camino.
  • Comparar las distintas copias exportadas (contenido + cabeceras) te dice cuál trata tu mensaje como HTML y no impone CSP.
  • El estado de la conversación (nuevo → revisado) confirma que el bot agente la procesó, sin dejarte a ciegas en el paso que depende de él.
  • El visor del buzón te deja leer exactamente lo que el payload envió.

Decoys (descártalos por razonamiento acotado)

  • El chat en vivo escapa. El widget del visitante y la consola de agente pintan tu mensaje con neutralización de HTML. Una sola prueba refuta que el XSS viva en la app viva.
  • La transcripción por email escapa. Pedir la copia por correo aplica entity-encoding y muestra tu payload como texto. Mismo codebase, distinto renderizador: el defecto no es una política global rota.
  • El export en modo texto es inerte. Descargar la transcripción en formato de texto plano devuelve text/plain; el payload aparece como texto y no ejecuta. Refuerza que el problema es el contexto HTML del export, no el almacenamiento.

La falla del laboratorio

Clase: Cross-Site Scripting almacenado (CWE-79) cuyo sink no es la aplicación viva, sino un artefacto exportado offline. La app viva neutraliza correctamente el input, pero el generador del HTML de la transcripción interpola el cuerpo del mensaje y el nombre del visitante crudos, sin escape (CWE-116, Improper Encoding or Escaping of Output; CWE-838, Inappropriate Encoding for Output Context), y ese HTML se sirve sin Content-Security-Policy (CWE-1021), de modo que nada frena la ejecución de script al abrirlo. Mapea a OWASP A03:2021 — Injection.

El punto pedagógico es el contraste honesto dentro del mismo codebase: el widget del visitante, la consola de agente, la transcripción por email y el export en texto plano tratan bien el input; solo el export HTML descargable lo emite en contexto de marcado sin neutralizarlo. Por eso "el chat viejo escapa" no cierra el caso: hay que preguntarse en qué otros contextos (offline, exportados, adjuntos) se re-emite el mismo dato controlado por el usuario.

El impacto se materializa porque una víctima privilegiada —un agente de soporte— abre ese artefacto en su navegador, con su sesión y con notas internas que un visitante jamás debería ver ya materializadas en la página. El export del propio visitante no incluye esas notas, así que dominar el vector con la copia propia no basta: el único camino es plantar el XSS y conseguir que sea el agente quien exporte-con-notas y abra el HTML. Eso convierte un XSS aparentemente "muerto" en una exfiltración de datos de staff (Information Disclosure) que porta la flag.

Remediación: aplicar entity-encoding a todo dato controlado por el usuario en todos los renderizadores de salida (no solo en el email); servir el HTML exportado con una Content-Security-Policy restrictiva o como descarga (Content-Disposition: attachment / text/plain) para evitar el render inline con scripting; y no materializar datos staff-only en un artefacto que también contiene contenido del usuario sin neutralizarlo antes.

Laboratorio basado en la reproducción anonimizada (empresa ficticia Deskoria) de una técnica real de bug bounty sobre un artefacto de transcripción exportado por un producto de LiveChat/helpdesk.

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