解けた問題
はじめに
タイトルの通りSECCON Beginners CTF 2020に参加しました。結果は惨敗だったわけですが、一応解けたのを共有します。
Welcome
チュートリアルですので割愛します。
R&B
まずfileコマンドでテキストファイルだとわかりました。
で、プログラム見ると、
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)
まず**rot13,base64って何?**という初歩的なことが分かってなかったので検索すると、pythonでデコードできるとのこと。
そこで以下のプログラムを書きました:
def rot13Reverse(arg):
import codecs
return codecs.decode(arg, 'rot13')
def base64Reverse(arg):
import base64
return str(base64.b64decode(bytes(arg, 'utf-8')))
s = "___" #テキストファイルの中身
while(True):
print(s)
if(s[0] == 'R'):
s = rot13Reverse(s[1:])
elif(s[0] == 'B'):
s = base64Reverse(s[1:])
else:
break
print(s)
お察しの通りこれ上手く動きませんでした。 base64Reverseはbyte配列を返すんですね。
ですからコンソールに出てきたものをひたすらコピペして何とか答えにたどり着きました。
emoemoencode
かわいい
F09F???? という並びが4回ずつ繰り返していることに気付いた僕は、デコードしたらctf4bから始まるだろうと仮定して以下の様なプログラムを書きました。
import java.util.List;
public class Main{
public static void main(String[] args){
List<Integer> list = List.of(0xF09F8DA3,0xF09F8DB4,0xF09F8DA6,0xF09F8CB4,
0xF09F8DA2,0xF09F8DBB,0xF09F8DB3,0xF09F8DB4,0xF09F8DA5,0xF09F8DA7,
0xF09F8DA1,0xF09F8DAE,0xF09F8CB0,0xF09F8DA7,0xF09F8DB2,0xF09F8DA1,
0xF09F8DB0,0xF09F8DA8,0xF09F8DB9,0xF09F8D9F,0xF09F8DA2,0xF09F8DB9,
0xF09F8D9F,0xF09F8DA5,0xF09F8DAD,0xF09F8CB0,0xF09F8CB0,0xF09F8CB0,
0xF09F8CB0,0xF09F8CB0,0xF09F8CB0,0xF09F8DAA,0xF09F8DA9,0xF09F8DBD);
list.stream().map(
i -> {
if(i > 0xF09F8D60){
return i-0xF09F8D60;
}else{
return i-0xF09F8C80;
}
}).forEach(i->System.out.print((char)i.shortValue()));
}
}
頭が悪い
最初ifで分岐しなかったら文字化けがひどかったのでctf4bを参考にして最初がそうなるように分岐を作りました。
結果は
CTF4B[STEGAN0GRAPHY?BY?EM000000JI]
あとはエスパーでアルファベットを小文字にし、「[」を「{」にし、「?」を「_」にしたら通りました。
想定解じゃないでしょうけども。
Spy
Beginner問題をあと一個でも頑張って通そうと思って取り組んだのがこれです。
サーバー側プログラムの重要な部分:
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))
最初はSQLインジェクションとかして強制的にログインするのかなとか思っていろいろやってましたが、二つのrender_templateが何が違うかを穴が開くほど見つめてようやく気付きました。
hash処理に時間がかかるはずっ!
パスワードを意図的に長くして全てのユーザーに対し総当たりして、ロード時間が不自然に長いものをピックしていったら正解しました。
ログインしなくてよかったのか...
終わりに
まだまだ自分が勉強不足であることを改めて思い知らされたので、これからも精進していこうと思います。
運営の皆様ありがとうございました。