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しているのもあやしい。

f:id:TAKEmaru:20170902220558p:plain

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}

最初のバイナリを修正するのがちょっとエスパーなかんじがする。意図的に動作しないバイナリが与えられる問題は珍しい。