Skip to content

Commit

Permalink
PIE description
Browse files Browse the repository at this point in the history
  • Loading branch information
zardus committed Nov 26, 2024
1 parent c00e0f3 commit a313164
Showing 1 changed file with 25 additions and 1 deletion.
26 changes: 25 additions & 1 deletion binary-exploitation/pie-overflow-w/DESCRIPTION.md
Original file line number Diff line number Diff line change
@@ -1 +1,25 @@
Overflow a buffer and smash the stack to obtain the flag, but this time in a position independent (PIE) binary!
In prior levels, you knew the address of `win()` because the binary was always loaded into the memory at the same place.
In this level, we'll explore challenges when the executable that you are overflowing is _Position Independent_!
A Position Independent Executable is loaded into a random location in memory.
Because of this, you cannot know exactly where the `win()` function is located.

So how can you solve this?
On x86 (and most other modern architectures), memory is mapped into a process' memory space _page by page_.
A memory page is a contiguous block of 0x1000 (4096) bytes starting at a page address aligned to 0x1000 for performance and memory management reasons (more on this [much later](https://pwn.college/system-security/kernel-security) in the pwn.college curriculum!).
For example, the following are all examples of potential page addresses:

- `0x5f7be1ec2000`
- `0x7ee1382c9000`
- `0x6513a3b67000`

Do you see how the last three digits (e.g., 12 bits, or 1.5 bytes, or affectionately known as 3 "nibbles") are all `0`?
We can use this to _partially_ predict addresses in the binary.
For an example, let's assume that our `win()` function is located `0x1337` bytes past the start of the binary (so, if the binary were not position independent, it would likely be located at `0x401337`).
This means that, for example, if our PIE binary were loaded at page address `0x6513a3b67000`, it would have its `win` function at `0x6513a3b68337`.
If it were loaded at `0x5f7be1ec2000`, its `win` function would be at `0x5f7be1ec3337`, and so on.

So, realistically, know the last three nibbles of any address in the binary as these nibbles never change due to the page-alignment (to `0x1000` bytes).
This gives us a workaround: we can overwrite the least significant byte of the saved return address, which we can know from debugging the binary, to retarget the return to main to any instruction that shares the other 7 bytes.
Since that last byte will be constant between executions (due to page alignment), this will always work.
If the address we want to redirect execution to is a bit farther away from the saved return address, and we need to write two bytes, then one of those nibbles (the fourth least-significant one) will be a guess, and it will be incorrect 15 of 16 times.
This is okay: we can just run our exploit a few times until it works (statistically, ~50% chance after 11 times and ~90% chance after 36 times).

0 comments on commit a313164

Please sign in to comment.