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.