HackIT CTF 2017 rev150 writeup
問題
Description: Looks like this packer can not unpack what has been packed :( There are 2 mistakes in unpacking procedure. It leads to the error. Try to fix unpacker and figure out what is inside.
packerとpackedという2つの64bitのELFが与えられる。
解く
packedを動かすと0x6b5489でSegmentation faultで落ちる。
$ ulimit -c unlimited $ strace ./packed execve("./packed", ["./packed"], [/* 74 vars */]) = 0 --- SIGSEGV {si_signo=SIGSEGV, si_code=SI_KERNEL, si_addr=0} --- +++ killed by SIGSEGV (core dumped) +++ Segmentation fault (コアダンプ) $ gdb ./packed core Reading symbols from ./packed...(no debugging symbols found)...done. [New LWP 10428] Core was generated by `./packed'. Program terminated with signal SIGSEGV, Segmentation fault. #0 0x00000000006b5489 in ?? () gdb-peda$
落ちた0x6b5489周辺を見てみると、0x6b548fでraxがindexのカウンタとして使われているのに0x6b5489ではrdxをindexにしていておかしいような気がしてくる。 0x6b548fでraxに+2しているのもあやしい。
0x6b548f、0x6b5489を以下のようにhopperで変更する。
00000000006b5489 xor byte [rax], dl ; CODE XREF=_6b5468+46 00000000006b548b ror rdx, 0x8 00000000006b548f add rax, 0x1 00000000006b5493 cmp rax, rcx
実行するとまたも Segmentation faultで落ちる。
$ ulimit -c unlimited $ strace ./modify_packed execve("./modify_packed", ["./modify_packed"], [/* 74 vars */]) = 0 uname({sys="Linux", node="ubuntu", ...}) = 0 brk(0) = 0x25ec000 brk(0x25ed1c0) = 0x25ed1c0 arch_prctl(ARCH_SET_FS, 0x25ec880) = 0 readlink("/proc/self/exe", "/mnt/hgfs/ctf2/hackit/rev150/Bro"..., 4096) = 55 brk(0x260e1c0) = 0x260e1c0 brk(0x260f000) = 0x260f000 access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory) fstat(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 9), ...}) = 0 write(1, "The hardest part is overcome.\n", 30The hardest part is overcome. ) = 30 --- SIGSEGV {si_signo=SIGSEGV, si_code=SEGV_MAPERR, si_addr=0} --- +++ killed by SIGSEGV (core dumped) +++ Segmentation fault (コアダンプ) $ gdb ./modify_packed core Reading symbols from ./modify_packed...(no debugging symbols found)...done. [New LWP 10774] Core was generated by `./modify_packed'. Program terminated with signal SIGSEGV, Segmentation fault. #0 0x000000000042194a in ?? () gdb-peda$ bt #0 0x000000000042194a in ?? () #1 0x0000000000400bde in ?? () #2 0x0000000000400e63 in ?? () #3 0x00000000004010ee in () #4 0x00000000004009ba in ()
The hardest part is overcomeと出力された。やったぜ。 バックトレース結果にある0x400bdbを見ていく。
gdb-peda$ x/19i 0x400bdb =>0x400bdb: idiv edi 0x400bdd: inc DWORD PTR [rbp+0x481375c0] 0x400be3: mov eax,DWORD PTR [rbp-0x10] 0x400be6: add rax,0x10 0x400bea: mov rax,QWORD PTR [rax] 0x400bed: mov rdi,rax 0x400bf0: call 0x400aae <"> 0x400bf5: mov rax,QWORD PTR [rbp-0x10] 0x400bf9: add rax,0x8 0x400bfd: mov rax,QWORD PTR [rax] 0x400c00: lea rsi,[rip+0x88fa9] # 0x489bb0 0x400c07: mov rdi,rax 0x400c0a: call 0x400370 0x400c0f: test eax,eax 0x400c11: jne 0x400c18 0x400c13: call 0x400b39 0x400c18: mov eax,0x0 0x400c1d: leave 0x400c1e: ret
0x400c00でrsiに0x489bb0のstringを入れている。
gdb-peda$ x/s 0x489bb0 0x489bb0: "decr"
直後でcallされている0x400b39があやしいので見る。
gdb-peda$ x/33i 0x400b39 0x400b39: push rbp 0x400b3a: mov rbp,rsp 0x400b3d: sub rsp,0x10 0x400b41: lea rdi,[rip+0x2b1558] # 0x6b20a0 0x400b48: call 0x417bb0 0x400b4d: mov DWORD PTR [rbp-0x8],eax 0x400b50: mov eax,DWORD PTR [rbp-0x8] 0x400b53: mov DWORD PTR [rbp-0x4],eax 0x400b56: cmp DWORD PTR [rbp-0x4],0x0 0x400b5a: js 0x400ba6 0x400b5c: mov eax,DWORD PTR [rbp-0x4] 0x400b5f: movsxd rdx,eax 0x400b62: lea rax,[rip+0x2b1537] # 0x6b20a0 0x400b69: movzx ecx,BYTE PTR [rdx+rax*1] 0x400b6d: mov eax,DWORD PTR [rbp-0x8] 0x400b70: sub eax,0x1 0x400b73: sub eax,DWORD PTR [rbp-0x4] 0x400b76: movsxd rdx,eax 0x400b79: lea rax,[rip+0x2b1520] # 0x6b20a0 0x400b80: movzx eax,BYTE PTR [rdx+rax*1] 0x400b84: xor eax,ecx 0x400b86: xor eax,0xffffff80 # 実質0x80 0x400b89: mov BYTE PTR [rbp-0x9],al 0x400b8c: mov eax,DWORD PTR [rbp-0x4] 0x400b8f: movsxd rdx,eax 0x400b92: lea rax,[rip+0x2b1507] # 0x6b20a0 0x400b99: movzx ecx,BYTE PTR [rbp-0x9] 0x400b9d: mov BYTE PTR [rdx+rax*1],cl 0x400ba0: sub DWORD PTR [rbp-0x4],0x1 0x400ba4: jmp 0x400b56 0x400ba6: nop 0x400ba7: leave 0x400ba8: ret
0x400b62、0x400b79、 0x400b92でraxに代入されているアドレス 0x6b20a0 はフラグの一部を含んでいた。この関数内でxorすることでフラグを生成しているようである。
gdb-peda$ x/s 0x6b20a0 0x6b20a0: "\225\300Ӆ\302\307ˉ\260\201\302ޚ\255\223\223\263\204\227\300\264\214گ_no_se1ler_sn0rom{t1kc4h"
フラグ生成処理を書き起こす。
# coding: UTF-8 flag_parts = bytearray(open("packed","rb").read()) offset = 0xb20a0 first_flag = "" latter_flag = str(flag_parts[offset+0x30/2: offset + 0x30]) for i in range(0x30/2): first_flag += chr(flag_parts[offset+i] ^flag_parts[offset+0x2f-i] ^ 0x80) print((first_flag + latter_flag)[::-1])
フラグが出た
$ python solver.py h4ck1t{mor0ns_rel1es_on_p4ck3r5_vari0rs_d03sn0t}
最初のバイナリを修正するのがちょっとエスパーなかんじがする。意図的に動作しないバイナリが与えられる問題は珍しい。