Manipulating WebSocket messages to exploit vulnerabilities
XSS persistente en el chat en vivo vía manipulación de mensajes WebSocket: el servidor no sanitiza el contenido y el agente de soporte renderiza el payload como HTML.
Target Overview & Vulnerability Summary
El laboratorio websockets/lab-manipulating-messages-to-exploit-vulnerabilities implementa un chat en vivo sobre WebSockets. Los mensajes enviados por el usuario son vistos por un agente de soporte en tiempo real. El servidor no sanitiza el contenido de los mensajes; al enviar un payload HTML/JS enmarcado en un mensaje WebSocket, el navegador del agente lo renderiza y ejecuta.
| Campo | Valor |
|---|---|
| Severidad | Alta (ejecución de JavaScript persistente en el navegador del agente) |
| Endpoint vulnerable | WebSocket del chat (/chat) |
| Root cause | Ausencia de saneamiento del contenido de mensajes WebSocket + renderizado como HTML en el cliente del agente |
Technical Breakdown & Exploitation Flow
1. Envío del payload a través del chat
El payload se envía como un mensaje de chat normal:
<img src=error onerror='alert(document.domain)'>
2. Observación del frame WebSocket
En Burp Repeater, el mensaje viaja como un frame de texto JSON. El servidor recibe y reenvía el contenido tal cual, con el payload escapado para JSON pero sin saneamiento HTML:
{"message":"<img src=error onerror='alert(1)'>"}
Ese escapar es solo de serialización JSON; el cliente del agente interpreta el contenido deserializado como HTML al insertarlo en el DOM del chat.
3. Reenvío directo con el frame manipulado
Con Repeater se puede emitir el frame WebSocket sin el escapado JSON de segundo nivel, demostrando que el servidor almacena/reenvía fielmente el contenido:
{"user":"You","content":"<img src=1 onerror='alert(1)'>"}
Cuando el agente de soporte recibe el mensaje, su cliente renderiza el <img> con onerror y dispara alert(1) en su navegador.
Root Cause Analysis
El defecto es la ausencia de validación server-side del contenido de los mensajes WebSocket. A diferencia de una request HTTP clásica donde el servidor puede escapar la salida, aquí el servidor actúa como relé: reenvía el texto crudo y delega el renderizado al cliente del agente. Si ese cliente inserta el contenido con innerHTML (o similar) sin escapar, el payload HTML/JS se ejecuta en el contexto del agente. El problema es la combinación de (a) sin saneamiento en el origen del mensaje y (b) renderizado inseguro en el destino.
Secure Remediation
- Sanitizar o escapar el contenido en el servidor antes de retransmitirlo a los demás clientes (defensa en el punto de confianza):
import html
sanitized = html.escape(user_message["content"]) # neutraliza <, >, &, comillas
server.broadcast({"user": ..., "content": sanitized})
- En el cliente del agente, insertar los mensajes como texto (
textContent) o escapar el HTML antes deinnerHTML. - Aplicar CSP estricto en el cliente del agente (
script-srcsinunsafe-inline) para que handlers inline comoonerrorno ejecuten. - Validar longitud y tipos del payload del frame WebSocket, y rechazar frames malformados.