はまやんはまやんはまやん

hamayanhamayan's blog

Beginner's Web [TSG CTF 2020]

CTFtime.org / TSG CTF 2020 / Beginner's web

なぜ人々は物事を難しく考えすぎるんでしょうね?
http://35.221.81.216:59101/
初心者向けヒント: とりあえず、上のリンクを開いてみてください。適当にいじってください。で、添付したソースコードを読んでください。⋯⋯flagとかいう怪しい変数があるのが見えますね? やることは簡単、このWebアプリケーションをハックしてflagを吐き出させるだけです。では、やってみましょう。 添付ファイル:CTF-Docs/Beginners-Web.md at master · TeamUnderdawgs/CTF-DocsのCode for Challenge部分

えー、さっぱりわからなかった。
FLAGのフィルタリングの突破方法をずっと探していた。

作問者解説

TSG CTF 2020 作問感想 - 博多電光
なるほど?

解説をさらに見る

なるほど!

Prototype Pollution Attack

  const result = await new Promise((resolve, reject) => {
    converters[request.body.converter](request.body.input, (error, result) => {
      if (error) {
        reject(error);
      } else {
        resolve(result);
      }
    });
  });

ここなんですが、bodyのconverterとinputがconverter配列に入れられているし、何やら特殊な感じになっている。
Prototype汚染方針で考えてみると、これに使えそうなものが出てくる。
Object.prototype.defineSetter() - JavaScript | MDN
2回リクエストを投げて情報を抜き取る。

converter=__defineSetter__&input=FLAG_oMKJNJGhxUdXBweIYrbZcDAbs1H1QBub
これをやると、converter部分は
converters['__defineSetter__']('FLAG_oMKJNJGhxUdXBweIYrbZcDAbs1H1QBub', (error, result) => {
となって、FLAG_oMKJNJGhxUdXBweIYrbZcDAbs1H1QBubに代入するときに、後ろの関数が呼ばれる形になる。
で、ここで大事なのが、これがPromiseの中で、awaitされているという点。
つまり、converter=__defineSetter__&input=FLAG_oMKJNJGhxUdXBweIYrbZcDAbs1H1QBubでアクセスすると、rejectかresolveが呼ばれるまでawaitで待ち合わせることになる。

この待ち合わせているときに、② converter=base64&input=xxxxxxxxxxを呼んだとしよう。
すると、色々あって

converters[`FLAG_${request.session.sessionId}`] = flagConverter;

が呼ばれるので、
これが呼ばれた段階で、先ほどawaitされている関数が呼ばれて、errorにflagConverterが入って、rejectでflagConverterが入るのだが、ここでto_stringされて関数が文字列に変換されてしまう。
それが①のレスポンスとして帰ってくるので、その中身を見るとフラグが書いてある。

これってrace conditionなの?

Discordでちょっとだけそういう話が出てたけど、race conditionではないとしている。
予期せぬ順序やタイミングで予期せぬ結果が出てくるもので、それに当てはまるかというと、攻撃タイミングを作って突いているからrace conditionではないのか。
そういわれればそうか。