今回の問題
picoCTFより"IntroToBurp"を解いていきます。
実際に解いていく
1. 問題文にあるWebサイトに移動する
名前や電話番号などの入力フォームがあります。
ただそれだけのようです。
2. Webサイトを探索する
名前や電話番号などを適当に入力→二段階認証→「Invalid OTP」になってしまいました。
ソースコードも見ましたが、特に怪しいところは見つかりませんでした。
気になるのは、この問題のタイトル。つまり、IntroToBurp の Burp の部分。
Burp?→Burp Suite!
早速、BurpSuiteを使って、サーバーとの通信内容を確認します。
(Web上のデベロッパーツールでも見ることができるけどね)
3. BurpSuiteでHTTPリクエストとレスポンスを確認する
BurpSuiteでサーバーとの通信内容の履歴を見てみます。
すると、一番上のPOSTメソッドのリクエスト内容に注目です。
otp=otp(←自分が入力したものが入っている) が怪しいですね。
この部分を、何かすればFLAGが見つかりそうです。
4. HTTPリクエストの内容を変えて送信する
otpを数字にしたり、文字にしたり...
しかし、一向にFLAGは返ってきません。そもそも、返ってくる内容に何も変化がありません。
そこで、otp自体を削除してリクエストを送信してみます。
すると、FLAGが返ってきました!
これにてFLAG獲得です。お疲れ様でした。
もう少し考えてみる
otpの値を変えるのではなく、otp自体を削除すると、FLAGを獲得することができました。
それはなぜなのか?
原因はおそらく、サーバー側のロジックのエラーだと考えられます。数字でも文字でも返ってくる内容が同じなのも怪しいです。
サーバー側のロジックを推測していきます。
otpが不正解→Invalid OTPを返して終了。
otpが正解→何もしない。最後のreturnでFLAGを返して終了。
otpが存在しない→そもそもotpの検証を行わない。最後のreturnでFLAGを返して終了。
以上より、今回のサーバー側のロジックは以下のようになっているのではないでしょうか。
@app.route('/dashboard', methods=['POST'])
def dashboard():
otp = request.form.get('otp')
if otp: # 値が存在する場合のみチェック
if not is_valid_otp(otp):
return 'Invalid OTP'
# ここに来る = OTPが空なら無条件通過
return render_template('flag.html', flag=FLAG)
これなら、otpが存在しないときにFLAGが返ってくることに納得できますね。
これにて終了です。
まとめ
今回の問題は、フォーム(otp)を消して送信するだけで通ることができました。
おそらく、サーバー側のロジックに何らかのバグがあったのでしょう。
補足
・otpとは?
→One-Time Passwordの略称。似たようなものに、2fa(Two-Factor Authentication)とかもある