ctf4b 2020 writeup
ctf4b 2020 writeupとなります。
解けた問題はemo-emo-encode, R & B, Spyとなります。
emo-emo-encode
🍣🍴🍦🌴🍢🍻🍳🍴🍥🍧🍡🍮🌰🍧🍲🍡🍰🍨🍹🍟🍢🍹🍟🍥🍭🌰🌰🌰🌰🌰🌰🍪🍩🍽
上記の絵文字列が与えられるので、Unicode文字列に変換します。
\U0001F363\U0001F374\U0001F366\U0001F334\U0001F362\U0001F37B\U0001F373\U0001F374\U0001F365\U0001F367\U0001F361\U0001F36E\U0001F330\U0001F367\U0001F372\U0001F361\U0001F370\U0001F368\U0001F379\U0001F35F\U0001F362\U0001F379\U0001F35F\U0001F365\U0001F36D\U0001F330\U0001F330\U0001F330\U0001F330\U0001F330\U0001F330\U0001F36A\U0001F369\U0001F37D\U0000000D\U0000000A
各文字の共通部分を排除
63746634627B73746567616E306772617068795F62795F656D3030303030306A697D
16進数→Shift-JIS変換
ctf4b{stegan0graphy_by_em000000ji}
R&B
BQlVrOUllRGxXY2xGNVJuQjRkVFZ5U0VVMGNVZEpiRVpTZVZadmQwOWhTVEIxTkhKTFNWSkdWRUZIUlRGWFUwRklUVlpJTVhGc1NFaDFaVVY1Ukd0Rk1qbDFSM3BuVjFwNGVXVkdWWEZYU0RCTldFZ3dRVmR5VVZOTGNGSjFTMjR6VjBWSE1rMVRXak5KV1hCTGVYZEplR3BzY0VsamJFaGhlV0pGUjFOUFNEQk5Wa1pIVFZaYVVqRm9TbUZqWVhKU2NVaElNM0ZTY25kSU1VWlJUMkZJVWsxV1NESjFhVnBVY0d0R1NIVXhUVEJ4TmsweFYyeEdNVUUxUlRCNVIwa3djVmRNYlVGclJUQXhURVZIVGpWR1ZVOVpja2x4UVZwVVFURkZVblZYYmxOaWFrRktTVlJJWVhsTFJFbFhRVUY0UlZkSk1YRlRiMGcwTlE9PQ==
from os import getenv FLAG = getenv("FLAG") FORMAT = getenv("FORMAT") def rot13(s): # snipped def base64(s): # snipped for t in FORMAT: if t == "R": FLAG = "R" + rot13(FLAG) if t == "B": FLAG = "B" + base64(FLAG) print(FLAG)
ZIPファイルの中には上記文字列と暗号化スクリプト。
とりあえず文字列の先頭が"R"の場合はROT13で復号,"B"の場合はbase64でデコードすれば解けそうなので,手動でやってみたら解けました。
ctf4b{rot_base_rot_base_rot_base_base}
正解のフラグからrot→base→rot→base→rot→base→baseの順番で暗号化されてるっぽいですね。 (暇があればスクリプトかいてみます)
Spy
Arthur Barbara Christine David Elbert Franklin George Harris Ivan Jane Kevin Lazarus Marc Nathan Oliver Paul Quentin Randolph Scott Tony Ulysses Vincent Wat Ximena Yvonne Zalmon
import os import time from flask import Flask, render_template, request, session # Database and Authentication libraries (you can't see this :p). import db import auth # ==================== app = Flask(__name__) app.SALT = os.getenv("CTF4B_SALT") app.FLAG = os.getenv("CTF4B_FLAG") app.SECRET_KEY = os.getenv("CTF4B_SECRET_KEY") db.init() employees = db.get_all_employees() # ==================== @app.route("/", methods=["GET", "POST"]) def index(): t = time.perf_counter() if request.method == "GET": return render_template("index.html", message="Please login.", sec="{:.7f}".format(time.perf_counter()-t)) if request.method == "POST": name = request.form["name"] password = request.form["password"] exists, account = db.get_account(name) if not exists: return render_template("index.html", message="Login failed, try again.", sec="{:.7f}".format(time.perf_counter()-t)) # auth.calc_password_hash(salt, password) adds salt and performs stretching so many times. # You know, it's really secure... isn't it? :-) hashed_password = auth.calc_password_hash(app.SALT, password) if hashed_password != account.password: return render_template("index.html", message="Login failed, try again.", sec="{:.7f}".format(time.perf_counter()-t)) session["name"] = name return render_template("dashboard.html", sec="{:.7f}".format(time.perf_counter()-t)) # ==================== @app.route("/challenge", methods=["GET", "POST"]) def challenge(): t = time.perf_counter() if request.method == "GET": return render_template("challenge.html", employees=employees, sec="{:.7f}".format(time.perf_counter()-t)) if request.method == "POST": answer = request.form.getlist("answer") # If you can enumerate all accounts, I'll give you FLAG! if set(answer) == set(account.name for account in db.get_all_accounts()): message = app.FLAG else: message = "Wrong!!" return render_template("challenge.html", message=message, employees=employees, sec="{:.7f}".format(time.perf_counter()-t)) # ==================== if __name__ == '__main__': db.init() app.run(host=os.getenv("CTF4B_HOST"), port=os.getenv("CTF4B_PORT"))
問題文から上記ファイルをゲットして,URLを確認してみるが最初は何をすればいいかわからず..
悩んでいるうちにページに表示されている"It took 0.0000858 sec to load this page."が怪しいんじゃね?という発想になりソースを確認する。
すると,DB内に存在するユーザの場合はauth.calc_password_hash(app.SALT, password)の関数が呼び出され,負荷がかかりアクセス時間が伸びる仕組みだと判明。
なのでこれまた手作業で全ユーザのアクセス時間を調べ上げ、下記のリストを取得。
Arthur 0.0003552 Barbara 0.0002938 Christine 0.0003799 David 0.0002633 #Elbert 0.6839784 Franklin 0.0002328 #George 0.4655279 Harris 0.0003274 Ivan 0.0003797 Jane 0.0003581 Kevin 0.0003291 #Lazarus 0.4179981 #Marc 0.6877503 Nathan 0.0003937 Oliver 0.0003495 Paul 0.0003279 Quentin 0.0002408 Randolph 0.0003148 Scott 0.0002579 #Tony 0.4931895 Ulysses 0.0002482 Vincent 0.0002678 Wat 0.0004867 #Ximena 0.4124623 #Yvonne 0.3969579 Zalmon 0.0002404
コメントアウトされているユーザが時間かかっているユーザだとわかったので、このシステムを使っているユーザはこれで間違いないはず...
こちらをChallengeページに入力してFlagゲット。
ctf4b{4cc0un7_3num3r4710n_by_51d3_ch4nn3l_4774ck}
これはサイドチャンネルアタックっていうんですね。
まとめ
上記3問は2~3時間で解けたが、それ以外の問題は時間かけても解けず...
悔しい結果となったので精進したいと思います。
個人的に年々難易度が上がっている気が...