Pivoting Around Memory
When exploiting a program, there's four primary regions of memory that matter to us:
- The program itself
- The stack
- libc
- The heap
All of these may be at randomized addresses, but a complex exploit will often need to interact with each of them. So how can we figure out where all of these are?
It turns out that if you have an arbitrary memory read and a pointer to any one of the four regions (or a relative read inside a region and the address you're reading relative to) it's actually possible to pivot around and leak the addresses of all of the other regions.
To start with, lets cover some methods you may be familiar with:
libc from binary: reading GOT entries
In the case a binary is not position-independent or you have a leak of the program base, GOT entries are a super simple way to leak libc's address, opening up an entire realm of exploits available inside of libc itself.
libc <-> heap: reading main_arena
pointers
If you have an arbitrary heap read (or, in some cases, just a read after free), you can often get pointers into libc by reading memory in the heap that points into the main_arena
in libc. You'll typically see these as the last next
pointer in freelists.
Going the other way, if you have a read inside of libc, you can read out of main_arena
to get pointers into the heap.
Program base and/or libc from stack: reading return addresses
If you have the ability to read on the stack, you can leak function pointers by reading out the return addresses. This will yield either function pointers to the program itself (defeating PIE), to libc (giving you libc base), or to other libraries which could be useful as part of an exploit chain.