DEF CON CTF Qualifier 2017 Writeup
開催期間(JST)
04/29 AM9:00 ~ 05/01 AM9:00
結果
・チーム名:wabisabi
・得点:85pt
・順位:得点したチーム中,139/368
解いた問題
・crackme1(Baby’s First)
・magic(Crackme 2000)
・sorcery(Crackme 2000)
取り組んだが解けなかった問題
・smashme(Baby’s First)
・beatmeonthedl(Baby’s First)
・alchemy(Crackme 2000)
はじめに
参加しました.
去年は1問も解けず敗北したのでリベンジ.
最終日は夜しか参加できなくて,朝までやってたら英語とオートマトンの授業を寝過ごして死亡しました.
Writeup
crackme1(Baby’s First)
問題文
crackme1_f92e0ab22352440383d58be8f046bebe.quals.shallweplayaga.me:10001
配布されたバイナリ.
$ bzip2 -dc 8a97fb8c264a3b34dad0a707dbfc92832067a0fa0f2b5a576c73557960b11506.tar.bz2|tar xvf - $ file 4a2181aaf70b04ec984c233fbe50a1fe600f90062a58d6b69ea15b85531b9652 4a2181aaf70b04ec984c233fbe50a1fe600f90062a58d6b69ea15b85531b9652: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib/ld-musl-x86_64.so.1, stripped
ncで繋いでみる.
$ nc crackme1_f92e0ab22352440383d58be8f046bebe.quals.shallweplayaga.me 10001 send your solution as base64, followed by a newline 4a2181aaf70b04ec984c233fbe50a1fe600f90062a58d6b69ea15b85531b9652
base64で送ってくれと言われるので適当に送ってみると
aG9nZQo= didn't exit happy, sorry
とりあえず,objdumpでバイナリを逆アセンブルして,静的解析する.
objdump -d -M intel 4a2181aaf70b04ec984c233fbe50a1fe600f90062a58d6b69ea15b85531b9652
適当に見ていくと,入力値をチェックしているようなところを見つけた.
cmp rdi,0x79 je 94f <_init+0x257> sub rsp,0x8 mov edi,0x1 call 758 <_init+0x60> mov eax,0xa7 ret cmp rdi,0x65 je 969 <_init+0x271> sub rsp,0x8 mov edi,0x2 call 758 <_init+0x60> mov rax,0xffffffffffffff9b ret cmp rdi,0x73 je 985 <_init+0x28d> sub rsp,0x8 mov edi,0x3 call 758 <_init+0x60> mov eax,0xa0 ret cmp rdi,0x20 je 99f <_init+0x2a7> sub rsp,0x8 mov edi,0x4 call 758 <_init+0x60> mov eax,0x16 ret cmp rdi,0x61 je 9b9 <_init+0x2c1> sub rsp,0x8 mov edi,0x5 call 758 <_init+0x60> mov rax,0xfffffffffffffff0 ret cmp rdi,0x6e je 9d5 <_init+0x2dd> sub rsp,0x8 mov edi,0x6 call 758 <_init+0x60> mov eax,0x190 ret cmp rdi,0x64 je 9ef <_init+0x2f7> sub rsp,0x8 mov edi,0x7 call 758 <_init+0x60> mov eax,0x1d ret cmp rdi,0x20 je a09 <_init+0x311> sub rsp,0x8 mov edi,0x8 call 758 <_init+0x60> mov eax,0xc5 ret cmp rdi,0x68 je a23 <_init+0x32b> sub rsp,0x8 mov edi,0x9 call 758 <_init+0x60> mov eax,0x4 ret cmp rdi,0x69 je a3d <_init+0x345> sub rsp,0x8 mov edi,0xa call 758 <_init+0x60> mov eax,0xc1 ret cmp rdi,0x73 je a57 <_init+0x35f> sub rsp,0x8 mov edi,0xb call 758 <_init+0x60> mov eax,0x10e ret cmp rdi,0x20 je a71 <_init+0x379> sub rsp,0x8 mov edi,0xc call 758 <_init+0x60> mov rax,0xffffffffffffff4e ret cmp rdi,0x68 je a8d <_init+0x395> sub rsp,0x8 mov edi,0xd call 758 <_init+0x60> mov eax,0x23 ret cmp rdi,0x61 je aa7 <_init+0x3af> sub rsp,0x8 mov edi,0xe call 758 <_init+0x60> mov eax,0xae ret cmp rdi,0x6e je ac1 <_init+0x3c9> sub rsp,0x8 mov edi,0xf call 758 <_init+0x60> mov eax,0x58 ret cmp rdi,0x64 je adb <_init+0x3e3> sub rsp,0x8 mov edi,0x10 call 758 <_init+0x60> mov eax,0xb ret cmp rdi,0x73 je af5 <_init+0x3fd> sub rsp,0x8 mov edi,0x11 call 758 <_init+0x60> mov eax,0x2b ret cmp rdi,0x20 je b0f <_init+0x417> sub rsp,0x8 mov edi,0x12 call 758 <_init+0x60> mov rax,0xffffffffffffff6d ret cmp rdi,0x73 je b2b <_init+0x433> sub rsp,0x8 mov edi,0x13 call 758 <_init+0x60> mov eax,0x6b ret cmp rdi,0x68 je b45 <_init+0x44d> sub rsp,0x8 mov edi,0x14 call 758 <_init+0x60> mov eax,0x10 ret cmp rdi,0x6f je b5f <_init+0x467> sub rsp,0x8 mov edi,0x15 call 758 <_init+0x60> mov eax,0x18e ret cmp rdi,0x6f je b79 <_init+0x481> sub rsp,0x8 mov edi,0x16 call 758 <_init+0x60> mov eax,0x91 ret cmp rdi,0x6b je b93 <_init+0x49b> sub rsp,0x8 mov edi,0x17 call 758 <_init+0x60> mov eax,0xce ret cmp rdi,0x20 je bad <_init+0x4b5> sub rsp,0x8 mov edi,0x18 call 758 <_init+0x60> mov rax,0xffffffffffffff17 ret cmp rdi,0x77 je bc9 <_init+0x4d1> sub rsp,0x8 mov edi,0x19 call 758 <_init+0x60> mov eax,0xab ret cmp rdi,0x69 je be3 <_init+0x4eb> sub rsp,0x8 mov edi,0x1a call 758 <_init+0x60> mov eax,0x166 ret cmp rdi,0x74 je bfd <_init+0x505> sub rsp,0x8 mov edi,0x1b call 758 <_init+0x60> mov rax,0xffffffffffffffe8 ret cmp rdi,0x68 je c19 <_init+0x521> sub rsp,0x8 mov edi,0x1c call 758 <_init+0x60> mov eax,0x53 ret cmp rdi,0x20 je c33 <_init+0x53b> sub rsp,0x8 mov edi,0x1d call 758 <_init+0x60> mov rax,0xffffffffffffffb1 ret cmp rdi,0x65 je c4f <_init+0x557> sub rsp,0x8 mov edi,0x1e call 758 <_init+0x60> xor eax,eax ret cmp rdi,0x78 je c66 <_init+0x56e> sub rsp,0x8 mov edi,0x1f call 758 <_init+0x60> mov eax,0x9e ret
見やすいように,cmpで区切っている.
比較している対象を切り出してみる.
0x79 0x65 0x73 0x20 0x61 0x6e 0x64 0x20 0x68 0x69 0x73 0x20 0x68 0x61 0x6e 0x64 0x73 0x20 0x73 0x68 0x6f 0x6f 0x6b 0x20 0x77 0x69 0x74 0x68 0x20 0x65 0x78
これをlistとか適当なファイル名で保存して,pythonで文字列に変換してみる.
#!/usr/bin/env python import sys def main(): f = open('./list') for i in f: sys.stdout.write(chr(int(i.strip('\n'), 16))) if __name__ == '__main__': main()
すると,以下のような文字列が得られる.
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 eWVzIGFuZCBoaXMgaGFuZHMgc2hvb2sgd2l0aCBleAo= The flag is: important videos best playlist Wigeekuk8
いやいやこんな簡単なわけあるか・・?と思いながらも
important videos best playlist Wigeekuk8
という文字列をsubmitしてみると,通った.
magic(Crackme 2000)
問題文
cm2k-magic_b46299df0752c152a8e0c5f0a9e5b8f0.quals.shallweplayaga.me:12001
配布されるバイナリは,恐らく上のcrackme1で渡されたものの,比較している文字が変わったものを200個
自動化しろということだと思うので,上でやったことをコード一発でできるようにした.
とりあえず,書いたコードを貼る.
#!/usr/bin/env python import commands import sys import base64 import socket def main(): dic = dic_generator() s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect(('cm2k-magic_b46299df0752c152a8e0c5f0a9e5b8f0.quals.shallweplayaga.me', 12001)) while True: res_list = [] data = s.recv(256) if data == "": break if repr(data).find('send your solution') > -1: pass elif repr(data).find('flag') > -1: print repr(data) return else: index = repr(data)[1:-3] print dic[index] s.sendall(dic[index] + '\n') s.close() def dic_generator(): f = open('./list') dic = {} for i in f: result = commands.getoutput('objdump -d -M intel ' + i.strip('\n') + '|grep 48\ 83\ ff') res_list = result.split('\n') string = '' for j in res_list: string = string + chr(int(j[-4:], 16)) str_base64 = base64.b64encode(string) dic[i.strip('\n')] = str_base64 f.close() return dic if __name__ == '__main__': main()
listという名前のファイルは,200個のファイル名をコピペして作った,ファイル名が格納されているもの.
どうやったかというと,objdumpで逆アセンブルした結果に対して,機械語でgrepした.
比較している部分で使われている機械語はユニークなもので,
48 83 ff 比較対象
という組み合わせは,他の部分では存在しなかったので,簡単にできた.
その後dic_generatorという関数で,ファイル名が「キー」で,値が「比較対象をbase64でエンコードしたもの」である,辞書を作る.
あとは,ソケット通信を開始してから
send your solution as base64, followed by a newline 65cb596908789372c2d6fbeb0ac3a0e3a1089039138711a016ec3994ad5c7f10
この2行目のファイル名のみを取り出して,それをキーにして値を取り出し,送り返す.
10回連続で正解すると,flagが貰えるっぽい.
$ python script.py ZGVyLiAiVGhhbmtzLiI= aW5kIGFzIEkgc3RhZ2dlcmVkIGludG8gdGhlIGtpdGNoZW4sIHdoZXJlIExpbCA= IEhhdW50ZWQgTWFuc2lvbiwgaWYgeW91IGp1c3QgZ2l2ZSBt YWxlIG9yIG90aGVyIGxpbWl0YXRpb25zIG9uIHRoZSBleGNsdXNpdmUgcmlnaA== Y3Jld3Mgc2N1cnJ5aW5nIGk= b3VzZXMgaW4gdGhlaXIgaG9tZXRvd25zLiBUaGUgTWFuc2lvbidzIGJldHRlciA= eSBvZiwgb3IgdGhl YnVybmVkIGFsbCBoaXMgcmVwdXRhdGlvbiBjYXBpdGE= LCB0dXJuaW5nIG1lIGludA== IGltbWVkaWF0ZQ== 'The flag is: a color map of the sun sokemsUbif\n'
sorcery(Crackme 2000)
問題文
cm2k-sorcery_13de8e6bf26e435fc43efaf46b488eae.quals.shallweplayaga.me:12002
またmagicと同じように,解凍すると200個のバイナリ.
上で書いたスクリプトがそのまま使えるかなと思って試してみたらだめだったので,逆アセンブルして読んでみる.
magicと同じように機械語でgrepすると,余分なものまで取ってきてしまう.
↓これは,80 f9(cmp cl) でgrepしたもの.0x80以降のcmp全部がいらない.そう思った根拠は,ASCIIコードがないから.あるものもあるけど,明らか違うので排除.
cmp cl,0x6e cmp cl,0x69 cmp cl,0x63 cmp cl,0x20 cmp cl,0x46 cmp cl,0x72 cmp cl,0x6f cmp cl,0x6e cmp cl,0x74 cmp cl,0x80 cmp cl,0xf0 cmp cl,0xf4 cmp cl,0x80 cmp cl,0x80 cmp cl,0xe0 cmp cl,0xed cmp cl,0xee cmp cl,0x80 cmp cl,0x3 cmp cl,0xe0 cmp cl,0xf0 cmp cl,0x1 cmp cl,0x3 cmp cl,0x2 cmp cl,0x1 cmp cl,0x3 cmp cl,0x2 cmp cl,0x1 cmp cl,0x3 cmp cl,0x2 cmp cl,0x4
これでいけるかと思ったらまだダメ.
もう一度注意深く読んでみると,
. . . . cmp cl,0x74 jne 38b6 <pthread_mutex_lock@plt+0xd26> cmp rax,0x9 je 37e2 <pthread_mutex_lock@plt+0xc52> mov al,BYTE PTR [rdi+0x9] cmp al,0x69 jne 38c2 <pthread_mutex_lock@plt+0xd32> mov rax,QWORD PTR [rsp+0x8] test rax,rax je 377e <pthread_mutex_lock@plt+0xbee> . . . .
この,cmp al, 0x69のところ.
最後の1文字だけ,alレジスタを使って比較している.
よって,こいつも取り出してあげる必要がある.コマンドで頑張ったらいけた.
あとは,書いたコード.magicで書いたコードをちょっと変えただけ.
#!/usr/bin/env python import commands import sys import base64 import socket def main(): dic = dic_generator() s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect(('cm2k-sorcery_13de8e6bf26e435fc43efaf46b488eae.quals.shallweplayaga.me', 12002)) while True: res_list = [] data = s.recv(256) if data == "": break if repr(data).find('didn\'t exit happy, sorry') > -1: pass elif repr(data).find('send your solution as base64') > -1: pass elif repr(data).find('flag') > -1: print repr(data) return else: index = repr(data)[1:-3] print dic[index] s.sendall(dic[index] + '\n') s.close() def dic_generator(): f = open('./list') dic = {} for i in f: result = commands.getoutput('objdump -d -M intel ' + i.strip('\n') + '|grep 80\ f9 |head -n -22') result = result + '\n' + commands.getoutput('objdump -d -M intel ' + i.strip('\n') + '|grep cmp |grep al |head -2 |tail -1') res_list = result.split('\n') string = '' for j in res_list: string = string + chr(int(j[-4:], 16)) str_base64 = base64.b64encode(string) dic[i.strip('\n')] = str_base64 f.close() return dic if __name__ == '__main__': main()
実行結果.
$ python script.py IEkgc2FpZC4gIkp1c3QgZ29pbmcgc29tZXdoZXJlIGVsc2UsIHN0YXJ0aW4= b2QgcG9pbnQsIExpc2EuIFRoZSBvZmZlciB3ZSdyZSBtYWtpbmcgdG8g IHRoYXQgaGFwcGVucywgdGhlcmUncyBub3RoaW5n IGJyb3VnaHQgb3Zlcg== IG9mIGdldHRpbmcgaW50byBjaGFyYWN0ZXIuIFNv aG9tZSwgYXM= cyBhIHNpbmdsZSBjYXN0bWVtYg== ZyBhIGxpdHQ= bWluZyB0byB0aGUgcG9pbnQuICJXZSByZWFkIHlvdQ== aWNjZWQgbmVydm91c2x5IGFzIEkgd2F0Y2hlZCBteSBwcm9ncmVzcw== "The flag is: don't forget me when you're famous Klousovnec\n"
取り組んだが解けなかった問題
beatmeonthedl(Baby’s First)
問題文
I really like to be beaten but keep it on the dl. Connect to: beatmeonthedl_498e7cad3320af23962c78c7ebe47e16.quals.shallweplayaga.me 6969
配布されたバイナリ.
$ file beatmeonthedl beatmeonthedl: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.24, not stripped
実行してみるとこんな感じ.
__ __ .__ __ / \ / \ ____ | | ____ ____ _____ ____ _/ |_ ____ \ \/\/ // __ \| | _/ ___\/ _ \ / \_/ __ \ \ __\/ _ \ \ /\ ___/| |_\ \__( <_> ) Y Y \ ___/ | | ( <_> ) \__/\ / \___ >____/\___ >____/|__|_| /\___ > |__| \____/ \/ \/ \/ \/ \/ __ .__ .__ .__ _____ __ .__ _/ |_| |__ ____ | | _____ |__|______ _____/ ____\ _/ |_| |__ ____ \ __\ | \_/ __ \ | | \__ \ | \_ __ \ / _ \ __\ \ __\ | \_/ __ \ | | | Y \ ___/ | |__/ __ \| || | \/ ( <_> ) | | | | Y \ ___/ |__| |___| /\___ > |____(____ /__||__| \____/|__| |__| |___| /\___ > \/ \/ \/ \/ \/ _________.__ .___ __________ __ / _____/| |__ _____ __| _/______ _ __ \______ \_______ ____ | | __ ___________ ______ \_____ \ | | \\__ \ / __ |/ _ \ \/ \/ / | | _/\_ __ \/ _ \| |/ \// __ \_ __ \/ ___/ / \| Y \/ __ \_/ /_/ ( <_> ) / | | \ | | \( <_> ) <\ ___/| | \/\___ \ /_______ /|___| (____ /\____ |\____/ \/\_/ |______ / |__| \____/|__|_ \\___ >__| /____ > \/ \/ \/ \/ \/ \/ \/ \/ Enter username:
PCで見ないと表示が崩れてそう.
Enter username: hoge Invalid user: hoge Enter username:
適当なusernameだと入れない.
gdbで,正当なusernameを探す.
$ gdb beatmeonthedl
[-------------------------------------code-------------------------------------] 0x40125a <main+30>: call 0x400cf6 <header> 0x40125f <main+35>: nop 0x401260 <main+36>: mov eax,0x0 => 0x401265 <main+41>: call 0x400ec8 <login> 0x40126a <main+46>: test eax,eax 0x40126c <main+48>: je 0x401260 <main+36> 0x40126e <main+50>: mov eax,0x0 0x401273 <main+55>: call 0x400f52 <printmenu>
loginという関数があるので,ステップインして追いかける.
loginではさらに,checkuserとcheckpassという関数があり,usernameとpasswordをチェックしている.
checkuserにおいて,
0x400d9e <checkuser+86>: mov rax,QWORD PTR [rip+0x208c53] # 0x6099f8 <user>
ここが実行された後にRAXを見ると,usernameがわかった.
RAX: 0x4085d8 --> 0x776100796c66636d ('mcfly')
さらに今度はpasswordを探す.
checkpassにおいて,
0x400e77 <checkpass+135>: mov rax,QWORD PTR [rip+0x208b82] # 0x609a00 <pass>
ここが実行された後にRAXを見ると,passwordがわかった.
RAX: 0x4085de --> 0x70616e73657761 ('awesnap')
この2つでログインすると,
Enter username: mcfly Enter Pass: awesnap I) Request Exploit. II) Print Requests. III) Delete Request. IV) Change Request. V) Go Away. |
ここから,pwnのようなことをやってシェルを奪うのかなと思ったが,全くわからなかった.
alchemy(Crackme 2000)
magicやsorceryと同じように自動化する問題?
自動化できないようにさらに工夫がされてて厳しかった.
感想
意外に結構解けたので嬉しかった.
smashmeとbeatmeonthedlは解いている人が多かったけど自分はサッパリだったのでこういうのも解けるようになりたい・・・