Exploiting PIEs
In previous sections we discussed how ASLR affects exploitation and what requirements must be met by an attacker in order to break the randomization of a target's addresses in memory. The requirements and methods in order to effectively exploit PIEs are quite similar.
What's available to an attacker when the target is not a PIE?
When an executable file is not position independent, the sections can all be
statically-addressed. A non-PIE ELF binary will be loaded into the same base
address in virtual memory each time it is executed. This behavior can be seen
with any non-PIE binary by using the size
command:
size --format=sysv <non-PIE binary>
This command will display the sections of the non-PIE binary and the starting address for each section. Each section will have a static address. [1]
Why is this significant for an attacker? The attacker will always know where important resources are in the executable file, these resources can include:
- The
.got
and.got.plt
sections - useful for gaining control of the program counter if a write primitive is available. - The
.data
and.bss
sections - useful for reading and writing data / conducting stack pivots, etc.
With knowledge of these static addresses, an attacker can use the non-PIE
behavior of the ELF binary to construct ROP chains using the code available -
everything is statically-addressed so the gadgets will always be in the same
location. Contrast this with how attackers must utilize sensitive information
leaks in order to overcome ASLR to determine the locations of their ROP gadgets
within shared objects such as glibc
.
So do PIEs break ROP?
Again, the methods to overcome PIEs and ASLR are similar. When a binary ELF is
position independent, all of the sections will be dynamically-addressed. The
sections of the ELF will start with some offset - this can be seen by using
the same size
command specified above on a PIE ELF binary. [1]
A PIE ELF binary will be loaded into a different base address in virtual memory each time it is executed. This behavior increases the difficulty for an attacker to utilize the resources identified earlier, and it also removes the attacker's ability to use statically-addressed ROP gadgets.
Similiar to how an attacker must overcome ASLR in order to generate a ROP
chain, a sensitive information leak must be conducted in order to determine the
base address of the ELF in virtual memory. This can be done by leaking a return
address from the stack, a pointer to some location in .data
, and so on.
Are there primitives other than an information leak that can be used to defeat PIE? How might the related vulnerabilities manifest themselves?
Yes, the previous discussion on how to defeat PIE was mostly concerned with how an attacker could generate a ROP chain, but there are other solutions that aren't stack based.
Some conditions could exist in which the attacker could use a heap based vulnerability to gain code execution, these include:
A good example of gaining code execution without needing an information leak
to determine the base address of the binary is 0CTF's BabyHeap2017 challenge.
[5] For this challenge, the attacker conducts a fastbin attack
to leak a libc
address - in this case an address from the main_arena
structure in libc
. After using this libc
information leak to defeat ASLR,
the attacker uses the same fastbin attack to conduct a write to libc
's
__malloc_hook
function pointer, overwriting it with a one_gadget
. The next
time malloc
is called by the program, the attacker gains code execution.
Another example provided by reference [3] discusses how a Use After Free vulnerability could be used to overwrite function pointers that may be scattered in the heap. An attacker could overwrite the function pointer with an address to shellcode, achieving arbitrary code execution.