yyy

CTFつよくなりたい

DEF CON CTF Qualifier 2017 Writeup

f:id:ywkw1717:20170501053356p:plain

開催期間(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は解いている人が多かったけど自分はサッパリだったのでこういうのも解けるようになりたい・・・