chunk extend and overlapping

本来打算先写off-by-one和unsorted bin atack的…但是鉴于off-by-one和overlapping的联系以及,,,unsorted bin attack..看起来好像真的用处颇少…于是就偷懒先写这个了(逃

chunk extend

chunk的拓展?? 是叫这么魔性的东西吗….大概是对下一个chunk有什么操作吧QAQ,打扰了…

功能

  • 泄露地址
    • Libc地址
    • heap地址
  • 泄露数据
    • 泄露已经释放的堆中的数据
    • 泄露chunk内的内容
  • 覆盖指针

利用条件

  • 有堆的漏洞
  • 可以改变下一个chunk的header域

一般就用在off-by-one了吧..绝配

利用原理

我们知道我们释放的堆块是先不还给操作系统而是由ptmalloc来接管的

而ptmalloc在获取chunk大小时的代码如下:

1
2
3
4
5
/* Get size, ignoring use bits */
#define chunksize(p) (chunksize_nomask(p) & ~(SIZE_BITS))

/* Like chunksize, but do not mask SIZE_BITS. */
#define chunksize_nomask(p) ((p)->mchunk_size)

  1. 全部获取
  2. 忽略源码

而获取下一块chunk地址代码如下:

1
2
/* Ptr to next physical malloc_chunk. */
#define next_chunk(p) ((mchunkptr)(((char *) (p)) + chunksize(p)))

也就是当前块地址+当前块大小

上一块chunk地址:

1
2
3
4
5
6
/* Size of the chunk below P.  Only valid if prev_inuse (P).  */
#define prev_size(p) ((p)->mchunk_prev_size)

/* Ptr to previous physical malloc_chunk. Only valid if prev_inuse (P). */
#define prev_chunk(p) ((mchunkptr)(((char *) (p)) - prev_size(p)))
当前块-上一块大小

判断是否inuse

1
2
#define inuse(p)
((((mchunkptr)(((char *) (p)) + chunksize(p)))->mchunk_size) & PREV_INUSE)

查看prev_inuse域,此域为1即为inuse

小总结

也就是说,对于如何判断本chunk相邻chunk的地址完全是通过size来确定的,因而给了我们机会,前辈们真的tql…

利用姿势

这里引用了ctf-wiki的代码,并且我做了一点点小修改:

inuse状态下

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

    ptr=malloc(0x10);
    malloc(0x10);
    printf("[*]ptr:%p\n",ptr);
    *(long long *)((long long)ptr-0x8)=0x41;
    free(ptr);
    ptr1=malloc(0x30);
    printf("[*]ptr1:%p\n",ptr1);
    return 0;
    }

    }

我在第一次malloc处下了断点,然后我们来看看现在堆的情况:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
pwndbg> heap
0x602000 FASTBIN {
prev_size = 0,
size = 33,
fd = 0x0,
bk = 0x0,
fd_nextsize = 0x0,
bk_nextsize = 0x20fe1
}
0x602020 PREV_INUSE {
prev_size = 0,
size = 135137,
fd = 0x0,
bk = 0x0,
fd_nextsize = 0x0,
bk_nextsize = 0x0
}

发现此时的size是0x21…这里还是想吐槽一些pwndbg把size部分改成了十进制..真的苟..这个docker我忘记改代码了…
然后程序继续

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
0x602000 FASTBIN {
prev_size = 0,
size = 33,
fd = 0x0,
bk = 0x0,
fd_nextsize = 0x0,
bk_nextsize = 0x21
}
0x602020 FASTBIN {
prev_size = 0,
size = 33,
fd = 0x0,
bk = 0x0,
fd_nextsize = 0x0,
bk_nextsize = 0x20fc1
}
0x602040 PREV_INUSE {
prev_size = 0,
size = 135105,
fd = 0x0,
bk = 0x0,
fd_nextsize = 0x0,
bk_nextsize = 0x0
}

可以看到这里已经分配完了,此时的堆

1
2
3
4
5
6
pwndbg> x/10gx 0x602000
0x602000: 0x0000000000000000 0x0000000000000021
0x602010: 0x0000000000000000 0x0000000000000000
0x602020: 0x0000000000000000 0x0000000000000021
0x602030: 0x0000000000000000 0x0000000000000000
0x602040: 0x0000000000000000 0x0000000000020fc1

没有问题,下一步进行size的改写,至于为什么是chunk-8呢,因为我们知道chunk给我们的地址其实是mem的地址,也就是0x602010处,因此减8可以到pre_size处

1
2
3
4
5
6
7
8
0x602000 FASTBIN {
prev_size = 0,
size = 65,
fd = 0x0,
bk = 0x0,
fd_nextsize = 0x0,
bk_nextsize = 0x21
}

这里已经改写成功了,此时的heap分析出来的是这样的:

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
pwndbg> heap
0x602000 FASTBIN {
prev_size = 0,
size = 65,
fd = 0x0,
bk = 0x0,
fd_nextsize = 0x0,
bk_nextsize = 0x21
}
0x602040 PREV_INUSE {
prev_size = 0,
size = 1041,
fd = 0x303a7274705d2a5b,
bk = 0x6666666666663778,
fd_nextsize = 0xa3836356566,
bk_nextsize = 0x0
}
0x602450 PREV_INUSE {
prev_size = 0,
size = 134065,
fd = 0x0,
bk = 0x0,
fd_nextsize = 0x0,
bk_nextsize = 0x0
}

成功通过size欺骗到了ptmalloc机制

1
2
3
4
5
6
7
8
pwndbg> x/14gx 0x602000
0x602000: 0x0000000000000000 0x0000000000000041
0x602010: 0x0000000000000000 0x0000000000000000
0x602020: 0x0000000000000000 0x0000000000000021
0x602030: 0x0000000000000000 0x0000000000000000
0x602040: 0x0000000000000000 0x0000000000000411
0x602050: 0x303a7274705d2a5b 0x6666666666663778
0x602060: 0x00000a3836356566 0x0000000000000000

然后我们让程序到下一个分配

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
pwndbg> heap
0x602000 FASTBIN {
prev_size = 0,
size = 65,
fd = 0x0,
bk = 0x0,
fd_nextsize = 0x0,
bk_nextsize = 0x21
}
0x602040 PREV_INUSE {
prev_size = 0,
size = 1041,
fd = 0x303a7274705d2a5b,
bk = 0x6666666666663778,
fd_nextsize = 0xa3836356566,
bk_nextsize = 0x0
}
0x602450 PREV_INUSE {
prev_size = 0,
size = 134065,
fd = 0x0,
bk = 0x0,
fd_nextsize = 0x0,
bk_nextsize = 0x0
}

此时这里虽然没变,但是

1
2
pwndbg> p/x ptr1
$4 = 0x602010

也就是说我们成功分配了free掉的chunk1

程序运行结果:

1
2
3
╰─# ./fast_in
[*]ptr:0x180e010
[*]ptr1:0x180e010

  1. smallbin

代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <stdlib.h>
#include <stdio.h>

int main()
{
void *ptr,*ptr1;

ptr=malloc(0x80);
malloc(0x10);
malloc(0x10);
*(long long *)((long long)ptr-0x8)=0xb1;
// printf("[*]%p\n",&ptr);
printf("[*]%p\n",ptr);
free(ptr);
ptr1=malloc(0xa0);
printf("[*]%p\n",ptr1);
}

程序运行结果:

1
2
3
╰─# ./pwn
[*]0x7a3010
[*]0x7a3010

这个程序就不做深入分析了,和fastbin的一样

free状态下

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

ptr=malloc(0x80);//分配第一个0x80的chunk1
malloc(0x10);//分配第二个0x10的chunk2
printf("[*]ptr:%p\n",ptr);
free(ptr);//首先进行释放,使得chunk1进入unsorted bin

*(long long *)((long long)ptr-0x8)=0xb1;
ptr1=malloc(0xa0);
printf("[*]ptr:%p\n",ptr1);
}

程序运行结果:

1
2
3
╰─# ./pwn
[*]ptr:0x149b010
[*]ptr:0x149b010

这个就是先释放,然后才进行大小的更改

通过extend后向overlapping

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <stdlib.h>
#include <stdio.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
printf("[*]ptr:%p\n",ptr);
*(long long *)((long long)ptr-0x8)=0x61;
free(ptr);
ptr1=malloc(0x50);
printf("[*]ptr:%p\n",ptr1);
}

我在第一次分配处下了断点,此时的堆:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
pwndbg> heap
0x602000 FASTBIN {
prev_size = 0,
size = 33,
fd = 0x0,
bk = 0x0,
fd_nextsize = 0x0,
bk_nextsize = 0x20fe1
}
0x602020 PREV_INUSE {
prev_size = 0,
size = 135137,
fd = 0x0,
bk = 0x0,
fd_nextsize = 0x0,
bk_nextsize = 0x0
}

随后分配完全结束时我们再看

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
pwndbg> heap
0x602000 FASTBIN {
prev_size = 0,
size = 33,
fd = 0x0,
bk = 0x0,
fd_nextsize = 0x0,
bk_nextsize = 0x21
}
0x602020 FASTBIN {
prev_size = 0,
size = 33,
fd = 0x0,
bk = 0x0,
fd_nextsize = 0x0,
bk_nextsize = 0x21
}
0x602040 FASTBIN {
prev_size = 0,
size = 33,
fd = 0x0,
bk = 0x0,
fd_nextsize = 0x0,
bk_nextsize = 0x21
}
0x602060 FASTBIN {
prev_size = 0,
size = 33,
fd = 0x0,
bk = 0x0,
fd_nextsize = 0x0,
bk_nextsize = 0x20f81
}
0x602080 PREV_INUSE {
prev_size = 0,
size = 135041,
fd = 0x0,
bk = 0x0,
fd_nextsize = 0x0,
bk_nextsize = 0x0
}

这时:

1
2
3
4
5
6
7
8
9
10
11
pwndbg> x/20gx 0x602000
0x602000: 0x0000000000000000 0x0000000000000021
0x602010: 0x0000000000000000 0x0000000000000000
0x602020: 0x0000000000000000 0x0000000000000021
0x602030: 0x0000000000000000 0x0000000000000000
0x602040: 0x0000000000000000 0x0000000000000021
0x602050: 0x0000000000000000 0x0000000000000000
0x602060: 0x0000000000000000 0x0000000000000021
0x602070: 0x0000000000000000 0x0000000000000000
0x602080: 0x0000000000000000 0x0000000000020f81
0x602090: 0x0000000000000000 0x0000000000000000

随后我们更改了size的大小

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
pwndbg> heap
0x602000 FASTBIN {
prev_size = 0,
size = 97,
fd = 0x0,
bk = 0x0,
fd_nextsize = 0x0,
bk_nextsize = 0x21
}
0x602060 FASTBIN {
prev_size = 0,
size = 33,
fd = 0x0,
bk = 0x0,
fd_nextsize = 0x0,
bk_nextsize = 0x411
}
0x602080 PREV_INUSE {
prev_size = 0,
size = 1041,
fd = 0x303a7274705d2a5b,
bk = 0xa30313032303678,
fd_nextsize = 0x0,
bk_nextsize = 0x0
}
0x602490 PREV_INUSE {
prev_size = 0,
size = 134001,
fd = 0x0,
bk = 0x0,
fd_nextsize = 0x0,
bk_nextsize = 0x0
}

free之后我们可以看到:

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
pwndbg> heap
0x602000 FASTBIN {
prev_size = 0,
size = 97,
fd = 0x0,
bk = 0x0,
fd_nextsize = 0x0,
bk_nextsize = 0x21
}
0x602060 FASTBIN {
prev_size = 0,
size = 33,
fd = 0x0,
bk = 0x0,
fd_nextsize = 0x0,
bk_nextsize = 0x411
}
0x602080 PREV_INUSE {
prev_size = 0,
size = 1041,
fd = 0x303a7274705d2a5b,
bk = 0xa30313032303678,
fd_nextsize = 0x0,
bk_nextsize = 0x0
}
0x602490 PREV_INUSE {
prev_size = 0,
size = 134001,
fd = 0x0,
bk = 0x0,
fd_nextsize = 0x0,
bk_nextsize = 0x0
}

再分配一次

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
pwndbg> heap
0x602000 FASTBIN {
prev_size = 0,
size = 97,
fd = 0x0,
bk = 0x0,
fd_nextsize = 0x0,
bk_nextsize = 0x21
}
0x602060 FASTBIN {
prev_size = 0,
size = 33,
fd = 0x0,
bk = 0x0,
fd_nextsize = 0x0,
bk_nextsize = 0x411
}
0x602080 PREV_INUSE {
prev_size = 0,
size = 1041,
fd = 0x303a7274705d2a5b,
bk = 0xa30313032303678,
fd_nextsize = 0x0,
bk_nextsize = 0x0
}
0x602490 PREV_INUSE {
prev_size = 0,
size = 134001,
fd = 0x0,
bk = 0x0,
fd_nextsize = 0x0,
bk_nextsize = 0x0
}

此时

1
2
3
4
5
6
7
8
9
10
11
pwndbg> x/20gx 0x602000
0x602000: 0x0000000000000000 0x0000000000000061
0x602010: 0x0000000000000000 0x0000000000000000
0x602020: 0x0000000000000000 0x0000000000000021
0x602030: 0x0000000000000000 0x0000000000000000
0x602040: 0x0000000000000000 0x0000000000000021
0x602050: 0x0000000000000000 0x0000000000000000
0x602060: 0x0000000000000000 0x0000000000000021
0x602070: 0x0000000000000000 0x0000000000000000
0x602080: 0x0000000000000000 0x0000000000000411
0x602090: 0x303a7274705d2a5b 0x0a30313032303678

这时就可以进行fastbin attack了

通过extend进行前向合并

之前的后向合并都是通过更改size实现的,但extend的前向合并则不一样,但原理是类似的
后向合并是通过更改size实现的,而前向合并顾名思义需要我们更改pre_size才行

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

先看程序的运行结果:

1
2
3
4
5
6
╰─# ./pwn
[*]ptr1:0x10c2010
[*]ptr2:0x10c20a0
[*]ptr3:0x10c20c0
[*]ptr4:0x10c20e0
[*]ptr5:0x10c2010

然后再调试一下,这次我断点下在了第一free的地方

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
pwndbg> heap
0x602000 PREV_INUSE {
prev_size = 0,
size = 145,
fd = 0x0,
bk = 0x0,
fd_nextsize = 0x0,
bk_nextsize = 0x0
}
0x602090 FASTBIN {
prev_size = 0,
size = 33,
fd = 0x0,
bk = 0x0,
fd_nextsize = 0x0,
bk_nextsize = 0x21
}
0x6020b0 FASTBIN {
prev_size = 0,
size = 33,
fd = 0x0,
bk = 0x0,
fd_nextsize = 0x0,
bk_nextsize = 0x91
}
0x6020d0 PREV_INUSE {
prev_size = 0,
size = 145,
fd = 0x0,
bk = 0x0,
fd_nextsize = 0x0,
bk_nextsize = 0x0
}
0x602160 FASTBIN {
prev_size = 0,
size = 33,
fd = 0x0,
bk = 0x0,
fd_nextsize = 0x0,
bk_nextsize = 0x411
}

可以看到我们的符合smallbin和fastbin的四个chunk已经分配完成了,然后先free掉ptr1,修改ptr4的pre_inuse和pre_size

1
2
3
4
5
6
7
8
9
10
11
pwndbg> x/20gx ptr4-0x10
0x6020d0: 0x00000000000000d0 0x0000000000000090
0x6020e0: 0x0000000000000000 0x0000000000000000
0x6020f0: 0x0000000000000000 0x0000000000000000
0x602100: 0x0000000000000000 0x0000000000000000
0x602110: 0x0000000000000000 0x0000000000000000
0x602120: 0x0000000000000000 0x0000000000000000
0x602130: 0x0000000000000000 0x0000000000000000
0x602140: 0x0000000000000000 0x0000000000000000
0x602150: 0x0000000000000000 0x0000000000000000
0x602160: 0x0000000000000000 0x0000000000000021

这里已经改好了,然后进行free(ptr4),此时的heap

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
pwndbg> heap
0x602000 PREV_INUSE {
prev_size = 0,
size = 353,
fd = 0x7ffff7dd1b78 <main_arena+88>,
bk = 0x7ffff7dd1b78 <main_arena+88>,
fd_nextsize = 0x0,
bk_nextsize = 0x0
}
0x602160 {
prev_size = 352,
size = 32,
fd = 0x0,
bk = 0x0,
fd_nextsize = 0x0,
bk_nextsize = 0x411
}
0x602180 PREV_INUSE {
prev_size = 0,
size = 1041,
fd = 0x3a347274705d2a5b,
bk = 0x3065303230367830,
fd_nextsize = 0xa,
bk_nextsize = 0x0
}
0x602590 PREV_INUSE {
prev_size = 0,
size = 133745,
fd = 0x0,
bk = 0x0,
fd_nextsize = 0x0,
bk_nextsize = 0x0
}

此时的bins:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
pwndbg> bins
fastbins
0x20
: 0x0
0x30: 0x0
0x40: 0x0
0x50: 0x0
0x60: 0x0
0x70: 0x0
0x80: 0x0
unsortedbin
all
: 0x602000 —▸ 0x7ffff7dd1b78 (main_arena+88) ◂— 0x602000
smallbins
empty
largebins
empty

然后再分配

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
pwndbg> heap
0x602000 PREV_INUSE {
prev_size = 0,
size = 353,
fd = 0x7ffff7dd1b78 <main_arena+88>,
bk = 0x7ffff7dd1b78 <main_arena+88>,
fd_nextsize = 0x0,
bk_nextsize = 0x0
}
0x602160 FASTBIN {
prev_size = 352,
size = 33,
fd = 0x0,
bk = 0x0,
fd_nextsize = 0x0,
bk_nextsize = 0x411
}
0x602180 PREV_INUSE {
prev_size = 0,
size = 1041,
fd = 0x3a347274705d2a5b,
bk = 0x3065303230367830,
fd_nextsize = 0xa,
bk_nextsize = 0x0
}
0x602590 PREV_INUSE {
prev_size = 0,
size = 133745,
fd = 0x0,
bk = 0x0,
fd_nextsize = 0x0,
bk_nextsize = 0x0
}

成功跨多域进行分配

1
2
3
4
5
6
pwndbg> x/10gx 0x602010-0x10
0x602000: 0x0000000000000000 0x0000000000000161
0x602010: 0x00007ffff7dd1b78 0x00007ffff7dd1b78
0x602020: 0x0000000000000000 0x0000000000000000
0x602030: 0x0000000000000000 0x0000000000000000
0x602040: 0x0000000000000000 0x0000000000000000

over~ 得,乖乖回去写off-by-one去了


本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!