Author Writeup (Intended) - The Matrix Revenge SCC2026 Web Challenge
The Matrix Revenge (Intended) Writeup
Twitter: @L3G4CY5
I created a web challenge for the Serbian Cyber Security Challenge 2026. Due to time constraints and lack of thorough playtesting, both the original and revenge versions ended up containing unintended flaws that significantly reduced the difficulty. (lets ignore that :D ). But obviously because of an easier way, nobody solved this challenge with an intended solution.
In this write-up, I will walk through the intended solution for the challenge.
The Matrix Revenge Description:
1 | |
TLDR;
- Use Iframe Hijacking trick to iframe a page and yet have a window.opener value.
- Use grandparent trick to change subframe’s location to attacker-controled page and abuse the trusted
postMessagecommunication channel.

Short Overview
The first part of the challenge involved an authentication bypass. However, this was only part of the original version and not the revenge variant, and it is fairly trivial, so I will skip it.
TL;DR: using __proto__ as the username and [object Object] as the password in a user[username] == password comparison results in a truthy value, allowing authentication bypass.
After this step, the second flag is located in the bot’s cookie, which indicates that the challenge is centered around an XSS vulnerability.
Challenge
Looking at the routes we can find a few interesting ones:
This page seems like it acts as a health dashboard monitor for the page./api/report - Reports a url to the bot./dashboard

dashboard.html
1 | |
In short, the page receives postMessage “health” data from the iframe and inserts it into the DOM as output. We can see that it uses innerHTML to render this value — which immediately raises the question: can this lead to XSS? Yes, but a few additional steps are required.
We can also observe a check for window.opener, meaning the page must be opened via window.open in order for the content (or the iframe) to be initialized.
The iframe’s HTML simply sends some dummy/random data to the parent (in this case, the dashboard).
After that, there is an event listener handling incoming messages. It includes a validation check to ensure that the message originates from the iframe:
1 | |
At first glance, this appears to be secure code that only accepts messages from the intended iframe. And while that is technically true, if we can control what the iframe sends, we can achieve XSS via the innerHTML sink.
Fortunately, the Same-Origin Policy (SOP) allows a grandparent frame to navigate (change the location of) a nested subframe, even across origins. In this case, we can abuse this behavior to replace the iframe that is responsible for sending the postMessage.
We can do this with the following code:
1 | |
This will redirect the nested iframe to attacker.com. So… do we have XSS now?
Well… not so fast.
If you try to iframe the /dashboard page directly, it will not load due to the window.opener check. This means we need a way to both set a valid window.opener and still have the page loaded inside an iframe.
We can achieve this by abusing how window.open works together with named frames. For example, we can use:
1 | |
Along with an iframe in our exploit like:
1 | |
When this code executes, the browser will not open a new window. Instead, it will look for an existing window or iframe with the name "windowName". Since our iframe already has that name, the browser will reuse it and navigate it to https://attacker.com/dashboard.
Because of this behavior, the /dashboard page is now loaded inside an iframe, while still having a valid window.opener.
In this challenge, the bot uses a headless browser, so we can directly call window.open in our exploit. In a real-world scenario, this would require the victim to already have a page that calls something like:
1 | |
and we would need to prepare an iframe with that same name in advance.
Using this technique, we successfully bypass the window.opener check while still framing the /dashboard page.
From this point, achieving XSS becomes straightforward. We can simply send a malicious payload via postMessage, which will be inserted into the DOM using innerHTML.
The final exploit would look like following:
Exploit
1 | |
And the attacker.com/js :
1 | |
Thanks for reading this and I hope you learned something new :) .