PIE TIME

Practical demonstration of bypassing PIE and ASLR protections in modern Linux binaries.

2 min read
CybersecurityBinary ExploitationPosition Independent Executable

The basic idea is to use the classic ret2win strategy. But, let's define it further. We can do something similar to return-to-libc. The C standard library (libc) includes functions like system() or execve(). The goal of the attacker is to execute these functions with specific arguments to achieve an exploit. For example calling system("/bin/sh") spawns in a shell.

In the source code of the vuln binary we see two functions main and win. win seems to execute our flag, so let's try and run it using our strategy. Let's run objdump to get some info about the binary.

objdump -d vuln | grep '<main\|<win>'

We should see:

   11c1:       48 8d 3d 75 01 00 00    lea    0x175(%rip),%rdi        # 133d <main>
00000000000012a7 <win>:
000000000000133d <main>:
    1387:       48 8d 35 af ff ff ff    lea    -0x51(%rip),%rsi        # 133d <main>
    1400:       74 05                   je     1407 <main+0xca>

Since the program seems to jump to a memory address of our choosing, we could find the offset between the main and win addresses. It's important to note that our executable is a Position Independent Executable (PIE), meaning that recording the absolute addresses won't work here. Instead, we'll have to use relative addresses.

Because PIE is enabled, the binary is loaded at a randomized base address each time it's run. This means function addresses like main and win will change every execution, but their offset relative to each other stays the same.

Given the addresses of 0x12a7 and 0x133d for win and main respectively, we can calculate the offset using a hexadecimal calculator. We do: 0x12a7 - 0x133d, and get -0x96 as our hexadecimal offset.

We're now ready to craft our attack. Launch up the instance, and connect to the server using the netcat (nc) command.

You'll see something like this:

Address of main: 0x5b75f60a133d
Enter the address to jump to, ex => 0x12345:

Take the leaked address of main (e.g. 0x5b75f60a133d) and subtract 0x96 to get the runtime address of win: 0x5b75f60a133d - 0x96 = 0x5b75f60a12a7. Type that address in and you should be able to jump to the address of win which will print out the flag!

Thanks for reading! Found this useful? Share it or reach out with thoughts.

© 2025 Emir Durakovic. All rights reserved.