dctf 2021 writeup
初心者用といいながら、ちゃんと難しいCTFでした。
Result
Writeup
Misc
Encrypted the flag 100pt
Decrypted flag is not in exact format.
ファイル: EncryptedTheFlagIHave.png
5文字目と最後の文字(記号?)が"{}" に対応してそうです。このフォントを調べていくとこれがヒットしました。「alphabet font」でググって、根気強く探して見つかりました。
Languages in Star Wars - Wikipedia
dctf{mastercodebreaker}
Dragon 100pt
Hiding in plain sight.
ファイル:dragon.png
背景の白色部分に白文字が隠されているのか…?と色々調べましたが、各色を透過させたら出てきました。
WEBブラウザ上で簡単に透過PNG画像を作成できるツール | 無料で画像を加工できるサイト PEKO STEP
dctf{N0w_Y0u_s3e_m3}
Don't let it run 100pt
PDF documents can contain unusual objects within.
ファイル:dragon.pdf
PDFを開いてプロパティを確認しますが特には分からず。困った時はstringsコマンド。
$ strings dragon.pdf (中略) /Type /Action /S /JavaScript /JS <766172205F3078346163393D5B2736363361435968594B272C273971776147474F272C276C6F67272C273150744366746D272C27313036387552596D7154272C27646374667B7064665F316E6A33637433647D272C273736383537376A6868736272272C2737313733343268417A4F4F51272C27373232353133504158436268272C2738333339383950514B697469272C27313434373836335256636E546F272C2731323533353356746B585547275D3B2866756E6374696F6E285F30783362316636622C5F3078316164386237297B766172205F30783536366565323D5F3078353334373B7768696C652821215B5D297B7472797B766172205F30783237353061353D7061727365496E74285F307835363665653228307831366529292B2D7061727365496E74285F307835363665653228307831366429292B7061727365496E74285F307835363665653228307831366329292B2D7061727365496E74285F307835363665653228307831373329292A2D7061727365496E74285F307835363665653228307831373129292B7061727365496E74285F307835363665653228307831373229292A2D7061727365496E74285F307835363665653228307831366129292B7061727365496E74285F307835363665653228307831366629292A7061727365496E74285F307835363665653228307831373529292B2D7061727365496E74285F307835363665653228307831373029293B6966285F30783237353061353D3D3D5F307831616438623729627265616B3B656C7365205F30783362316636625B2770757368275D285F30783362316636625B277368696674275D2829293B7D6361746368285F3078353736346134297B5F30783362316636625B2770757368275D285F30783362316636625B277368696674275D2829293B7D7D7D285F3078346163392C3078386439376629293B66756E6374696F6E205F30786128297B766172205F30783363366432303D5F3078353334373B636F6E736F6C655B5F3078336336643230283078313734295D285F307833633664323028307831366229293B7D76617220613D27626B706F646E746A636F7073796D6C78656977686F6E7374796B787372707A79272C623D2765787262737071717573746E7A717269756C697A70656565787771736F666D77273B5F30786228612C62293B66756E6374696F6E205F307835333437285F30783337646533352C5F3078313961633236297B5F30783337646533353D5F30783337646533352D30783136613B766172205F30783461633965613D5F3078346163395B5F30783337646533355D3B72657475726E205F30783461633965613B7D66756E6374696F6E205F307862285F30783339623365652C5F3078666165353433297B766172205F30783235393932333D5F30783339623365652B5F30786661653534333B5F30786128293B7D0A> endobj (中略)
変な16進数ありますね。でかい数字はとりあえずlong_to_bytes()
>>> x = 0x766172205F3078346163393D5B2736363361435968594B272C273971776147474F272C276C6F67272C273150744366746D272C27313036387552596D7154272C27646374667B7064665F316E6A33637433647D272C273736383537376A6868736272272C2737313733343268417A4F4F51272C27373232353133504158436268272C2738333339383950514B697469272C27313434373836335256636E546F272C2731323533353356746B585547275D3B2866756E6374696F6E285F30783362316636622C5F3078316164386237297B766172205F30783536366565323D5F3078353334373B7768696C652821215B5D297B7472797B766172205F30783237353061353D7061727365496E74285F307835363665653228307831366529292B2D7061727365496E74285F307835363665653228307831366429292B7061727365496E74285F307835363665653228307831366329292B2D7061727365496E74285F307835363665653228307831373329292A2D7061727365496E74285F307835363665653228307831373129292B7061727365496E74285F307835363665653228307831373229292A2D7061727365496E74285F307835363665653228307831366129292B7061727365496E74285F307835363665653228307831366629292A7061727365496E74285F307835363665653228307831373529292B2D7061727365496E74285F307835363665653228307831373029293B6966285F30783237353061353D3D3D5F307831616438623729627265616B3B656C7365205F30783362316636625B2770757368275D285F30783362316636625B277368696674275D2829293B7D6361746368285F3078353736346134297B5F30783362316636625B2770757368275D285F30783362316636625B277368696674275D2829293B7D7D7D285F3078346163392C3078386439376629293B66756E6374696F6E205F30786128297B766172205F30783363366432303D5F3078353334373B636F6E736F6C655B5F3078336336643230283078313734295D285F307833633664323028307831366229293B7D76617220613D27626B706F646E746A636F7073796D6C78656977686F6E7374796B787372707A79272C623D2765787262737071717573746E7A717269756C697A70656565787771736F666D77273B5F30786228612C62293B66756E6374696F6E205F307835333437285F30783337646533352C5F3078313961633236297B5F30783337646533353D5F30783337646533352D30783136613B766172205F30783461633965613D5F3078346163395B5F30783337646533355D3B72657475726E205F30783461633965613B7D66756E6374696F6E205F307862285F30783339623365652C5F3078666165353433297B766172205F30783235393932333D5F30783339623365652B5F30786661653534333B5F30786128293B7D0A >>> long_to_bytes(x) b"var _0x4ac9=['663aCYhYK','9qwaGGO','log','1PtCftm','1068uRYmqT','dctf{pdf_1nj3ct3d}','768577jhhsbr','717342hAzOOQ','722513PAXCbh','833989PQKiti','1447863RVcnTo','125353VtkXUG'];(function(_0x3b1f6b,_0x1ad8b7){var _0x566ee2=_0x5347;while(!![]){try{var _0x2750a5=parseInt(_0x566ee2(0x16e))+-parseInt(_0x566ee2(0x16d))+parseInt(_0x566ee2(0x16c))+-parseInt(_0x566ee2(0x173))*-parseInt(_0x566ee2(0x171))+parseInt(_0x566ee2(0x172))*-parseInt(_0x566ee2(0x16a))+parseInt(_0x566ee2(0x16f))*parseInt(_0x566ee2(0x175))+-parseInt(_0x566ee2(0x170));if(_0x2750a5===_0x1ad8b7)break;else _0x3b1f6b['push'](_0x3b1f6b['shift']());}catch(_0x5764a4){_0x3b1f6b['push'](_0x3b1f6b['shift']());}}}(_0x4ac9,0x8d97f));function _0xa(){var _0x3c6d20=_0x5347;console[_0x3c6d20(0x174)](_0x3c6d20(0x16b));}var a='bkpodntjcopsymlxeiwhonstykxsrpzy',b='exrbspqqustnzqriulizpeeexwqsofmw';_0xb(a,b);function _0x5347(_0x37de35,_0x19ac26){_0x37de35=_0x37de35-0x16a;var _0x4ac9ea=_0x4ac9[_0x37de35];return _0x4ac9ea;}function _0xb(_0x39b3ee,_0xfae543){var _0x259923=_0x39b3ee+_0xfae543;_0xa();}\n"
見つかりました
dctf{pdf_1nj3ct3d}
Powerpoint programming 200pt
A login page in powerpoint should be good enough, right?
Flag is not in format. DCTF{ALL_CAPS_LETTERS_OR_NUMBERS}
ファイル:chall.ppsx
開くとパワポのスライドショーが始まります。WaniCTFでも似たようなのがありました。
$ mv chall.ppsx chall.ppt
これで開くと、どうやらアニメーションでflagを出すようです。プレビューで見れなかったので、アニメーションウインドウから頑張って読みます。
これで一つ一つflagを確認していきました。
DCTF{PPT_1SNT_V3RY_S3CUR3_1S_1T}
Crypto
Julius' ancient 100pt
I found this Ancient Roman papyrus. Could you decypher it for me?
ファイル:flag.txt
rq7t{7vH_rFH_vI6_pHH1_qI67}
dctf -> rq7t になっているようです。t->7以外は12文字シフトさせているようです。"abc-xyz0123456789"とすれば、tを12文字シフトすると7になります。あとは大文字アルファベットですね。"A-Za-z0-9"でシフトすればOKです。
dctf{th3_d13_h4s_b33n_c4st}
Just Take Your Time 200pt
Let's go. In and out. 2 second adventure.
nc dctf-chall-just-take-your-time.westeurope.azurecontainer.io 9999
Hint : While this may not be pwn, its tools may still be quite handy.
ファイル:just-take-your-time.py
まずはかけ算をやれと言われるので普通に掛け算をして積を送ります。
次に、DES3で暗号化されたものを渡されるので復号したものを送ります。ここで3回までチャンスがあります。keyはその時のUNIXTIMEなので掛け算が終わった後すぐUNIXTIMEを取り復号していきます。うまくkeyが設定されると16進数の平文が手に入りそれを送るとflagが手に入ります。
HOST, PORT = "dctf-chall-just-take-your-time.westeurope.azurecontainer.io", 9999 s, f = sock(HOST, PORT) read_until(f) recv_m = read_until(f).split() x = int(recv_m[0])*int(recv_m[2]) s.send(str(x).encode()+b"\n") tim = int(time()) #UNIXTIMEの取得 print(read_until(f)) enc = read_until(f).strip() print(enc) print("time: ",tim) for _ in range(3): key = str(tim).zfill(16).encode("utf-8") #keyの設定 cipher = DES3.new(key, DES3.MODE_CFB, b"00000000") pt = cipher.decrypt(long_to_bytes(int(enc,16))) s.send(pt+b"\n") recv_m = read_until(f).strip() if "guess" in recv_m: #はずれ tim += 1 else: #あたり break while True: print(read_until(f))
dctf{1t_0n1y_t0Ok_2_d4y5...}
A Simple SP Box 300pt
It's just a simple SP-box, 150 tries should be enough for you.
nc dctf1-chall-sp-box.westeurope.azurecontainer.io 8888
ファイル:sp_box.py
sp_box.pyを読んでいきます。まずflagの暗号文を送り、任意の平文を暗号化してくれるようです。
では暗号化アルゴリズムを見ていきます。
まず、roundsを決定します。flagの長さによって決まります。
rounds = int(2 * ceil(log(len(message), 2)))
次に変換表を作ります。変数ALPHABETには、大文字小文字アルファベットと数字、いくつかの記号が入っており合計78文字です。これをシャッフルし元あった場所と比較して変換表を生成します。
そして平文から変換表により文字を置き換え、次に順番を変えます。暗号文を前半後半に分け、[後半の0文字目], [前半の0文字目], [後半の1文字目], [前半の1文字目],...という風にします。これをrounds回行いますが、順番変えは最終roundは行いません。
解法として、まずALPHABETの文字を一文字ずつ暗号化してもらいます。暗号化の制限は150回なので回数は大丈夫です。暗号化の際に平文長が奇数だと後ろに"_"(アンダースコア)が付きます。"a", "b"を送ると、2文字の暗号文が返ってきます。どちらも先頭文字は一致するので"_"の暗号文だと分かります。しかし、平文長が1(2)だとroundsは2になります。なので先頭文字は"_"を2回変換させたもの、もう一文字は送った文字を2回変換させたものとなります。ここで1回変換させた文字は知る必要がありません。roundsはかならず偶数になるからです。
あとはdecrypt関数を作り、全ての文字を2回変換させた文字から暗号文をflagに戻していきます。ここで、変換表は一つのループになるまでサーバに繋ぎ直します("a"->"b"->"a"とならない)。その方がやりやすいためです。その確率は1/78なので、結構待つ必要があります。
def decrypt(enc,table): message = list(enc) pt = list(enc) rounds = int(2 * ceil(log(len(message), 2))) for r in range(rounds-1,-1,-1): if r < rounds-1: ppt = ["a" for _ in range(len(pt))] for j in range(len(pt)): if j%2 == 0: ppt[j] = pt[len(pt)//2+j//2] else: ppt[j] = pt[j//2] pt = ppt ppt = [] for j in range(len(pt)): ppt.append(table[pt[j]]) pt = ppt return ''.join(pt) ALPHABET = ascii_letters + digits + "_!@#$%.'\"+:;<=}{" l = len(ALPHABET) #HOSTはIPアドレスでも可 HOST, PORT = "dctf1-chall-sp-box.westeurope.azurecontainer.io", 8888 while True: s, f = sock(HOST, PORT) read_until(f) enc = read_until(f).strip() b1 = [] bef = "a" #b1.append(bef) for i in tqdm(range(l//2)): read_until(f,"> ") s.send(bef.encode()+b"\n") read_until(f) recv_m = read_until(f).strip() b1.append(recv_m[-1]) bef = b1[-1] print("b1") print(b1) if len(list(set(b1))) != l//2: print("NG b1 :",len(list(set(b1)))) s.close() continue if b1[-1] != "a": print("NG b1 because the last is not a") s.close() continue b2 = [] for x in ALPHABET: if x in b1: continue bef = x print("b2",x) break for i in tqdm(range(l//2)): #bef = b2[-1] read_until(f,"> ") s.send(bef.encode()+b"\n") read_until(f) recv_m = read_until(f).strip() b2.append(recv_m[-1]) bef = b2[-1] print("b2") print(b2) if len(list(set(b2))) != l//2: print("NG b2 :",len(list(set(b2)))) s.close() continue rev_table = {} for j in range(len(b2)): rev_table[b1[j]] = b2[j] rev_table[b2[j]] = b1[(j+1)%len(b1)] table = {} for k,v in rev_table.items(): table[v] = k print(decrypt(enc,table)) s.close() break
たまに上手くいきました。
dctf{S0_y0u_f0und_th3_cycl3s_in_th3_s_b0x}
This one is really basic 300pt
The answer to life, the universe, and everything.
ファイル:cipher.txt
中を見るとbase64のようです。復号してみるとまた同じような文字構成に加えて語尾が"="です。無限にdecryptして、"dctf{"が出てきたら終了です。得点の割には簡単でした。
import base64 f = open("cipher.txt","rb") a = f.readline().strip() while True: a = base64.b64decode(a) if b"dctf{" in a: print(a) break
dctf{Th1s_l00ks_4_lot_sm4ll3r_th4n_1t_d1d}