Attack All Around

今やCTFと競プロばかりだが、トビタテ生のアメリカ留学やTOEFL奮闘記

dctf 2021 writeup

初心者用といいながら、ちゃんと難しいCTFでした。

dCTF

Result

f:id:partender810:20210517104257p:plain
Ranking

f:id:partender810:20210517104401p:plain
solves


Writeup

Misc

Encrypted the flag 100pt

Decrypted flag is not in exact format.
ファイル: EncryptedTheFlagIHave.png

f:id:partender810:20210516140325p:plain
EncryptedTheFlagIHave.png

5文字目と最後の文字(記号?)が"{}" に対応してそうです。このフォントを調べていくとこれがヒットしました。「alphabet font」でググって、根気強く探して見つかりました。

Languages in Star Wars - Wikipedia

dctf{mastercodebreaker}


Dragon 100pt

Hiding in plain sight.
ファイル:dragon.png

f:id:partender810:20210516141027p:plain
dragon.png

背景の白色部分に白文字が隠されているのか…?と色々調べましたが、各色を透過させたら出てきました。

WEBブラウザ上で簡単に透過PNG画像を作成できるツール | 無料で画像を加工できるサイト PEKO STEP

f:id:partender810:20210516141233p:plain
こんな感じ

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を出すようです。プレビューで見れなかったので、アニメーションウインドウから頑張って読みます。

f:id:partender810:20210516142802p:plain
Rectangle xx をクリックすると該当ボタンの左の稲妻マークが光る

これで一つ一つ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}

Problems & Solver

GitHub - ksbowler/dctf_2021