DEF CON CTF Qualifier 2017 write-up

4/29 0:00-5/1 0:00(UTC) に開催されたDEF CON CTF Qualifier 2017で解けた問題のwrite-upです。
チーム名 CTF-infinitとして参加し、43pts獲得して173位でした。

crackme1 (Baby'sFirst)

crackme1_f92e0ab22352440383d58be8f046bebe.quals.shallweplayaga.me:10001
[https://2017.notmalware.ru/add6a6232ea1e9d42b925a7d2a2c5781bfccd6fd/8a97fb8c264a3b34dad0a707dbfc92832067a0fa0f2b5a576c73557960b11506.tar.bz2]

x86-64のELFが提供され、リモートに繋いでみると何か答えをBase64で送れと言われる。

$ file magic_dist
magic_dist: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib/ld-musl-x86_64.so.1, stripped

$ nc crackme1_f92e0ab22352440383d58be8f046bebe.quals.shallweplayaga.me 10001
send your solution as base64, followed by a newline

とりあえずELFファイルを逆アセンブルし、解析

$ objdump -D -Mintel magic_dist > magic_dist.asm

c6cから始まる関数を見てみると955, 971など細かく関数をcallしている。
引数(rdi)に渡しているアドレスは、1バイトずつずらしている。

 c6c:	55                   	push   rbp
 c6d:	53                   	push   rbx
 c6e:	48 89 fd             	mov    rbp,rdi
 c71:	48 83 ec 08          	sub    rsp,0x8
 c75:	48 0f be 3f          	movsx  rdi,BYTE PTR [rdi]
 c79:	e8 bd fc ff ff       	call   93b <_init+0x243>
 c7e:	48 0f be 7d 01       	movsx  rdi,BYTE PTR [rbp+0x1]
 c83:	48 c1 f8 03          	sar    rax,0x3
 c87:	48 89 c3             	mov    rbx,rax
 c8a:	e8 c6 fc ff ff       	call   955 <_init+0x25d>
 c8f:	48 0f be 7d 02       	movsx  rdi,BYTE PTR [rbp+0x2]
 c94:	48 01 c3             	add    rbx,rax
 c97:	48 c1 fb 03          	sar    rbx,0x3
 c9b:	e8 d1 fc ff ff       	call   971 <_init+0x279>
 ca0:	48 0f be 7d 03       	movsx  rdi,BYTE PTR [rbp+0x3]
 ca5:	48 01 c3             	add    rbx,rax
 ca8:	48 c1 fb 03          	sar    rbx,0x3
 cac:	e8 da fc ff ff       	call   98b <_init+0x293>
 cb1:	48 0f be 7d 04       	movsx  rdi,BYTE PTR [rbp+0x4]
 cb6:	48 01 c3             	add    rbx,rax
 cb9:	48 c1 fb 03          	sar    rbx,0x3
 cbd:	e8 e3 fc ff ff       	call   9a5 <_init+0x2ad>
 cc2:	48 0f be 7d 05       	movsx  rdi,BYTE PTR [rbp+0x5]
  :

呼び出されている関数の中身を見てみると、引数(rdi)と1バイト比較。
イコールならeaxに値を代入してret。notイコールのときに呼ばれる758は、<.plt.got>セクションにあるし、exit関数っぽい。とりあえず比較している1バイトを集める。

   :
 93b:	48 83 ff 79          	cmp    rdi,0x79 ; "y"
 93f:	74 0e                	je     94f <_init+0x257>
 941:	48 83 ec 08          	sub    rsp,0x8
 945:	bf 01 00 00 00       	mov    edi,0x1
 94a:	e8 09 fe ff ff       	call   758 <_init+0x60>
 94f:	b8 a7 00 00 00       	mov    eax,0xa7
 954:	c3                   	ret    

 955:	48 83 ff 65          	cmp    rdi,0x65 ; "e"
 959:	74 0e                	je     969 <_init+0x271>
 95b:	48 83 ec 08          	sub    rsp,0x8
 95f:	bf 02 00 00 00       	mov    edi,0x2
 964:	e8 ef fd ff ff       	call   758 <_init+0x60>
 969:	48 c7 c0 9b ff ff ff 	mov    rax,0xffffffffffffff9b
 970:	c3                   	ret    

 971:	48 83 ff 73          	cmp    rdi,0x73 ; "s"
 975:	74 0e                	je     985 <_init+0x28d>
 977:	48 83 ec 08          	sub    rsp,0x8
 97b:	bf 03 00 00 00       	mov    edi,0x3
 980:	e8 d3 fd ff ff       	call   758 <_init+0x60>
 985:	b8 a0 00 00 00       	mov    eax,0xa0
 98a:	c3                   	ret    
   :

集めた文字を連結すると、"yes and his hands shook with ex"という文字列になった。試しにBase64エンコードして送信してみたらフラグだった。

$ echo "yes and his hands shook with ex" | base64 | nc crackme1_f92e0ab22352440383d58be8f046bebe.quals.shallweplayaga.me 10001
send your solution as base64, followed by a newline
4a2181aaf70b04ec984c233fbe50a1fe600f90062a58d6b69ea15b85531b9652
The flag is: important videos best playlist Wigeekuk8

magic (CrackMe_2000)

cm2k-magic_b46299df0752c152a8e0c5f0a9e5b8f0.quals.shallweplayaga.me:12001

[https://2017.notmalware.ru/5e1d5bc4e73657be1b008fbb6b08a7604e81d7cb/91ae7f2ec76f00975849c44b3d8ec8ed897fab7335c156d949bd15ea156338b3.tar.bz2]

解凍するとx86-64のELFが200個ほどあった。とりあえず逆アセンブルして眺めてみたところ、crackme1と同じ形式なことがわかった。

       :
     93b:	48 83 ff 64          	cmp    rdi,0x64 ; d
     93f:	74 0e                	je     94f <_init+0x257>
     941:	48 83 ec 08          	sub    rsp,0x8
     945:	bf 01 00 00 00       	mov    edi,0x1
     94a:	e8 09 fe ff ff       	call   758 <_init+0x60>
     94f:	b8 6c 00 00 00       	mov    eax,0x6c
     954:	c3                   	ret    
     
     955:	48 83 ff 65          	cmp    rdi,0x65 ; e
     959:	74 0e                	je     969 <_init+0x271>
     
     95b:	48 83 ec 08          	sub    rsp,0x8
     95f:	bf 02 00 00 00       	mov    edi,0x2
     964:	e8 ef fd ff ff       	call   758 <_init+0x60>
     969:	b8 49 00 00 00       	mov    eax,0x49
     96e:	c3                   	ret    
     
     96f:	48 83 ff 2e          	cmp    rdi,0x2e ; .
     973:	74 0e                	je     983 <_init+0x28b>
     975:	48 83 ec 08          	sub    rsp,0x8
     979:	bf 03 00 00 00       	mov    edi,0x3
     97e:	e8 d5 fd ff ff       	call   758 <_init+0x60>
     983:	b8 72 00 00 00       	mov    eax,0x72
     988:	c3                   	ret    
      :
     df6:	55                   	push   rbp
     df7:	53                   	push   rbx
     df8:	48 89 fd             	mov    rbp,rdi
     dfb:	48 83 ec 08          	sub    rsp,0x8
     dff:	48 0f be 3f          	movsx  rdi,BYTE PTR [rdi]
     e03:	e8 33 fb ff ff       	call   93b <_init+0x243>
     
     e08:	48 0f be 7d 01       	movsx  rdi,BYTE PTR [rbp+0x1]
     e0d:	48 c1 f8 03          	sar    rax,0x3 ; 0x6c >> 0x3 = 0xd
     e11:	48 89 c3             	mov    rbx,rax
     e14:	e8 3c fb ff ff       	call   955 <_init+0x25d>
     
     e19:	48 0f be 7d 02       	movsx  rdi,BYTE PTR [rbp+0x2]
     e1e:	48 01 c3             	add    rbx,rax
     e21:	48 c1 fb 03          	sar    rbx,0x3
     e25:	e8 45 fb ff ff       	call   96f <_init+0x277>
     e2a:	48 0f be 7d 03       	movsx  rdi,BYTE PTR [rbp+0x3]
       :

200個を手作業でやるわけにはいかないので、pythonで自動収集するプログラムを作成。
問題の文字列収集は、比較部分のニーモニック(cmp rdi,0xXX)に該当する機械語が"48 83 ff XX"だったので、逆アセンブル結果から該当する機械語を含む行だけ抽出したら、うまいぐあいに文字の比較部分だけ抽出できた。最終的な回答コードが以下になった。

#!/usr/bin/python
import os
import re
import glob
import base64
import subprocess
import socket

def read_data(f, delim="\n"):
    data = ""
    while not data.endswith(delim):
        data += f.read(1)
    return data


def make_ans(fname, data):
    data = data.split("\n")
    cmp = ""
    for d in data:
        if  "\t48 83 ff " not in d:
            continue
        a = d.split(",")[1]
        a = chr(int(a, 16))
        print d, "; ", a
        
        cmp += a
    
    print fname
    print "[+]str: ", cmp
    print "[+]Base64: ", base64.b64encode(cmp)
    return base64.b64encode(cmp)


if __name__ == "__main__":
    # run objdump
    files = glob.glob("./magic_dist/*")
    ans_list = {}
    # collect ans
    for f in files:
        arg = ["objdump", "-d", "-Mintel", f]
        asm = subprocess.Popen(arg, stdout=subprocess.PIPE)
        data, err = asm.communicate()
        
        q = f.split("/")[2]
        ans_list[q] = make_ans(f, data)
    
    # connect to server
    host = "cm2k-magic_b46299df0752c152a8e0c5f0a9e5b8f0.quals.shallweplayaga.me"
    port = 12001
    
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.connect((host, port))
    sf = s.makefile("rw", bufsize=0)
    while True:
        data = read_data(sf)
        print data
        if "flag" in data:
            break
        elif 'send your solution as base64, followed by a newline' in data:
            continue
        data = data.split("\n")[0]
        ans = ans_list[data]
        sf.write(ans + "\n")

コードとしては、Objdumpで逆アセンブル、その結果から"\t48 83 ff " を含む行を抽出。比較されているバイトを連結し、Base64エンコードして辞書作成。サーバーへの回答時は、サーバーからの質問をキーとしてエンコードしている回答を送信しているだけ。
何度か回答に成功するとフラグがもらえた。

 $ ./solve.py
 93b:   48 83 ff 64             cmp    rdi,0x64 ;  d
 955:   48 83 ff 6f             cmp    rdi,0x6f ;  o
 96f:   48 83 ff 63             cmp    rdi,0x63 ;  c
 989:   48 83 ff 74             cmp    rdi,0x74 ;  t
 9a3:   48 83 ff 6f             cmp    rdi,0x6f ;  o
 9bd:   48 83 ff 72             cmp    rdi,0x72 ;  r
 9d7:   48 83 ff 20             cmp    rdi,0x20 ;   
 9f3:   48 83 ff 63             cmp    rdi,0x63 ;  c
 a0d:   48 83 ff 61             cmp    rdi,0x61 ;  a
 a27:   48 83 ff 6d             cmp    rdi,0x6d ;  m
 a41:   48 83 ff 65             cmp    rdi,0x65 ;  e
 a5b:   48 83 ff 20             cmp    rdi,0x20 ;   
 a75:   48 83 ff 69             cmp    rdi,0x69 ;  i
 a8f:   48 83 ff 6e             cmp    rdi,0x6e ;  n
 aa9:   48 83 ff 2c             cmp    rdi,0x2c ;  ,
 ac3:   48 83 ff 20             cmp    rdi,0x20 ;   
 adf:   48 83 ff 73             cmp    rdi,0x73 ;  s
 af9:   48 83 ff 68             cmp    rdi,0x68 ;  h
 b13:   48 83 ff 61             cmp    rdi,0x61 ;  a
 b2d:   48 83 ff 6b             cmp    rdi,0x6b ;  k
 b47:   48 83 ff 69             cmp    rdi,0x69 ;  i
 b61:   48 83 ff 6e             cmp    rdi,0x6e ;  n
 b7b:   48 83 ff 67             cmp    rdi,0x67 ;  g
 b95:   48 83 ff 20             cmp    rdi,0x20 ;   
 bb1:   48 83 ff 68             cmp    rdi,0x68 ;  h
 bcb:   48 83 ff 69             cmp    rdi,0x69 ;  i
 be5:   48 83 ff 73             cmp    rdi,0x73 ;  s
 c01:   48 83 ff 20             cmp    rdi,0x20 ;   
 c1b:   48 83 ff 68             cmp    rdi,0x68 ;  h
 c35:   48 83 ff 65             cmp    rdi,0x65 ;  e
 c4f:   48 83 ff 61             cmp    rdi,0x61 ;  a
 c69:   48 83 ff 64             cmp    rdi,0x64 ;  d
 c83:   48 83 ff 2e             cmp    rdi,0x2e ;  .
 c9f:   48 83 ff 20             cmp    rdi,0x20 ;   
 cbb:   48 83 ff 22             cmp    rdi,0x22 ;  "
 cd5:   48 83 ff 57             cmp    rdi,0x57 ;  W
 cef:   48 83 ff 65             cmp    rdi,0x65 ;  e
 d0b:   48 83 ff 6c             cmp    rdi,0x6c ;  l
./magic_dist/e005a1973167b4cf47100c81bca178b98646a128e05a6beb804226888be3f344
[+]str:  doctor came in, shaking his head. "Wel
[+]Base64:  ZG9jdG9yIGNhbWUgaW4sIHNoYWtpbmcgaGlzIGhlYWQuICJXZWw=
 :
send your solution as base64, followed by a newline

c0d5312126c2a52cb23f91694a22ba32284e59336fb5176ed92dc1cd23b62655
  :
省略
  :
be422c4d780fcc9f29d01283ad76088529af9b4dc7a69c3bb9979fb51a23824e

The flag is: a color map of the sun sokemsUbif

解けた問題は以上です。力不足感が半端ないので、もっと精進したい。