From a3131641557802ecf157633660a18f85c22feef6 Mon Sep 17 00:00:00 2001 From: Yan Date: Tue, 26 Nov 2024 16:49:31 -0700 Subject: [PATCH] PIE description --- .../pie-overflow-w/DESCRIPTION.md | 26 ++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/binary-exploitation/pie-overflow-w/DESCRIPTION.md b/binary-exploitation/pie-overflow-w/DESCRIPTION.md index b2c2845..39312b1 100644 --- a/binary-exploitation/pie-overflow-w/DESCRIPTION.md +++ b/binary-exploitation/pie-overflow-w/DESCRIPTION.md @@ -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).