SECCON Beginners CTF2021 Writeup [Reversing onlye_read, children, please_not_trace_me, be_angry]
はじめに
久しぶりにCTFに参加して、思うように解けなかったり、そもそも解き方を忘れてたりして時間かかってしまったので戒めとしてReversingのWriteupを書きます。ただしfirmwareは解けなかったので、そのWriteupはないです。。angrを思い出すのに10分かかってた。
Reversing
only_read
Ghidraで文字列を見てみると、Correctという文字列が見つかり、その近くのコードを見てみるとflagらしいものが見つかる。
ctf4b{c0n5t4nt_f0ld1ng}
children
Ghidraで文字列を見てみると、以下略。 それっぽ文字列が見つかるがhoge関数の中に見つかるが、ifの条件文なので、目に見えてるものは答えじゃないだろうなと推定。
vvv関数はデータを参照して4倍と進めては、xorしてるだけなので復号してみる。 Ghidra Scriptを使ってもよかったがそんなにたいそうなものでもないので、手動で復号
st = ['cud7f~v7\x7fl','rgw2[qt3{`','nf]w4572W8','5^wpacs6u\x00'] def decode(st): res = '' for i in range(10): res += chr(ord(st[i]) ^ i) return res res = '' for i in st: res += decode(i) print(res)
ctf4b{p0werfu1_tr4sing_t0015_15_usefu1}
please_not_trace_me
見る限りRC4で暗号化されてるので、復号してあげればよい。keyや暗号化された文字はリファレンスをたどることで簡単に見つかる。
暗号化データは適当にバイナリエディタでファイルとして作成し、次のコードを動かすことで答えが手に入る。
def KSA(key): S = list(range(256)) j = 0 k = 0 tmp = 0 for i in range(256): tmp = j + S[i] + ord(key[i % len(key)]) k = (j >> 0x1f) >> 0x18 j = (tmp + k & 0xff) - k S[i], S[j] = S[j], S[i] return S key = "nickelodeon" S = KSA(key) with open('encod_data','rb') as f: data = f.read() res = [0] * len(data) for i in range(len(data)): res[i] = data[i] ^ S[S[0] * 2] print("".join([chr(i) for i in res])) #ctf4b{d1d_y0u_d3crypt_rc4?}
ctf4b{d1d_y0u_d3crypt_rc4?}
be_angry
Ghidraで中身を見てみると、入力した文字列に対してSwitch文で分岐して、文字列をチェックしているように見える。
こういったケースではangrを使うと簡単にできるので、angrを使ってみる。
angrで読み込んだ後、Switchの部分にCorrectとなっているアドレスを目的のアドレス、Incorrectとなっている部分のアドレスをavoid_listとして、実行する。 IDAやGhidraで目的のアドレスをメモして起き、メイン関数からのアドレス差で指定してあげるとうまくいく。
(angr) kali@kali:~/angr$ python3 Python 3.9.2 (default, Feb 28 2021, 17:03:44) [GCC 10.2.1 20210110] on linux Type "help", "copyright", "credits" or "license" for more information. >>> import angr >>> p=angr.Project('/home/kali/Desktop/chall') WARNING | 2021-05-22 21:53:26,900 | cle.loader | The main binary is a position-independent executable. It is being loaded with a base address of 0x400000. >>> main_addr = p.loader.main_object.get_symbol('main').rebased_addr SyntaxError: invalid syntax >>> addr_success = main_addr + 0x139E >>> avoid_list = (main_addr + 477,main_addr + 666,main_addr + 1964,main_addr + 1992,main_addr + 3353,main_addr + 3549,main_addr + 3706,main_addr + 3734,main_addr + 4994,main_addr + 5106,main_addr + 5417) >>> sim = p.factory.simgr() >>> sim.explore(find=addr_success, avoid=avoid_list) WARNING | 2021-05-22 22:18:36,454 | angr.storage.memory_mixins.default_filler_mixin | The program is accessing memory or registers with an unspecified value. This could indicate unwanted behavior. WARNING | 2021-05-22 22:18:36,454 | angr.storage.memory_mixins.default_filler_mixin | angr will cope with this by generating an unconstrained symbolic variable and continuing. You can resolve this by: WARNING | 2021-05-22 22:18:36,454 | angr.storage.memory_mixins.default_filler_mixin | 1) setting a value to the initial state WARNING | 2021-05-22 22:18:36,454 | angr.storage.memory_mixins.default_filler_mixin | 2) adding the state option ZERO_FILL_UNCONSTRAINED_{MEMORY,REGISTERS}, to make unknown regions hold null WARNING | 2021-05-22 22:18:36,454 | angr.storage.memory_mixins.default_filler_mixin | 3) adding the state option SYMBOL_FILL_UNCONSTRAINED_{MEMORY,REGISTERS}, to suppress these messages. WARNING | 2021-05-22 22:18:36,454 | angr.storage.memory_mixins.default_filler_mixin | Filling register id with 8 unconstrained bytes referenced from 0x4028f9 (_1_main_flag_func_4+0x1f in chall (0x28f9)) WARNING | 2021-05-22 22:18:36,455 | angr.storage.memory_mixins.default_filler_mixin | Filling register ac with 8 unconstrained bytes referenced from 0x4028f9 (_1_main_flag_func_4+0x1f in chall (0x28f9)) <SimulationManager with 13 deadended, 1 found, 11 avoid> >>> s = sim.found[0] >>> s.posix.dumps(0) b'ctf4b{3nc0d3_4r1thm3t1c}'
ctf4b{3nc0d3_4r1thm3t1c}