Concept: Position Independent Executables
Modern architectures have good support for Program Counter-relative addressing, which has enabled the use of Position-Independent Executables . Information about underlying concepts is available online . This page will cover the concept as it relates to hacking.
Challenges of PIE
Since modern systems tend to map memory at random locations, The biggest challenge of PIE is that you don’t know where anything is in memory. This presents two problems: you don’t know where to write, and you don’t know what to write.
Problem: where to write
First, you must figure out where to write. Depending on the type of write primitive you can leverage, this might or might not be hard:
- If you have an overflow, the write itself doesn’t need any knowledge of where you’re writing: the data will get written onto the buffer you’re overflowing!
- If you have a relative write, then you can write into any adjacent data without having to know the actual location of that data.
- Otherwise, if you have an absolute write, you will need to first leak the address of the executable using another vulnerability.
Problem: what to write
If you solve the problem of where to write, you must still understand what to write.
In the context of PIE, there is a useful exploitation technique: a partial pointer overwrite .
Because memory pages are always aligned to
0x1000 , the three least-significant nibbles will always be the same, regardless of where the page is mapped.
Thus, you can overwrite the least-significant byte of a pointer with impunity to re-target that pointer to any other resource (such as a different instruction) that has the same other bytes as the pointer that you’re overwriting.
This will always work, since that byte is always the same.
You can also overwrite two bytes (four nibbles), giving you more options to re-target the pointer at.
This is more risky: because the most significant nibble you overwrite is not fixed, and you must overwrite it (as memory writtes typically happen on a byte-by-byte basis, not nibble-by-nibble or bit-by-bit), you have to take a guess for a value to overwrite it with and brute-force it.
Your guess will match the actual value in memory one out of 16 times (the number of values, 0x0 through 0xf, that a nibble can hold), and your exploit will work.