> For the complete documentation index, see [llms.txt](https://simon-6.gitbook.io/simoncyber/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://simon-6.gitbook.io/simoncyber/ctf-writeups/picoctf-2025/binary-exploitation/pie-time-2-medium.md).

# PIE TIME 2 (Medium)

<figure><img src="/files/PGUHi6oHrePo1GEnBRZI" alt=""><figcaption></figcaption></figure>

```c
#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;§§§¨¨
}
```

***

<figure><img src="/files/nSHdPywUxWgWUS5RxaoY" alt=""><figcaption></figcaption></figure>

If we connect to the CTF, this time it asks for our name. (And doesn't leak the main address this time) This challenge mostly contains the same code as last time, except it contains one more function.

```c
void call_functions() {
  char buffer[64];
  printf("Enter your name:");
// outputs the string
  fgets(buffer, 64, stdin);
// fgets reads a line from a input you typed. buffer is where it is stored, and stdin is the keyboard
  printf(buffer);
// prints what's stored in the buffer (what you typed) 

  unsigned long val;
  printf(" enter the address to jump to, ex => 0x12345: ");
// asks for input 
  scanf("%lx", &val);
// unsigned long data type and stored in the variable val 

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

I'm guessing this challenge is similar to how eval() would work, in that the input we give would be treated as literal code instead of a string. However, since I don't really know C all that well, I don't really understand the vulnerability here.&#x20;

### The Vulnerability&#x20;

{% embed url="<https://deepwiki.com/ctf-wiki/ctf-wiki/2-binary-exploitation-(pwn)#format-string-vulnerabilities>" %}

This type of vulnerability is a Format-String vulnerability.

> Format string vulnerabilities occur when user input is used directly as the format string in functions like `printf()`:

Specifically, the "printf(buffer)" is the dangerous piece of code.&#x20;

```
SAFE: printf("anything you type directly here");
DANGEROUS: printf(variable_that_user_controls);
SAFE: printf("%s", variable_that_user_controls);
```

{% hint style="info" %}
`printf()` **always** treats its first argument as a **format string** that may contain format specifiers.

It scans through looking for `%` characters and interprets them as instructions.
{% endhint %}

Hence, a attacker can get away from stealing data using some format specifiers.&#x20;

1. Attacker provides format specifiers like `%x`, `%s`, or `%n`
2. Allows reading arbitrary memory with `%s`
3. Allows writing to arbitrary memory with `%n`

Knowing this, the goal of this challenge is a lot like last time, to find the main address and its offset.&#x20;

### Solving it

Using this cheat sheet.&#x20;

<figure><img src="/files/QlrYLNtDCAWzWAVSRkal" alt=""><figcaption></figcaption></figure>

Our challenge probably requires us to do use %p. So let's try that first.&#x20;

<figure><img src="/files/0UnqLIxM3JrftFr8OekU" alt=""><figcaption></figcaption></figure>

It works but it doesn't tell much specifically. Next we'll be using pwndbg.&#x20;

<figure><img src="/files/rY54klnnwik4Cho1MJZV" alt=""><figcaption></figcaption></figure>

<figure><img src="/files/Pr4dbGA7Gg4tCPsfZUXx" alt=""><figcaption></figcaption></figure>

Knowing this, we know the offset is the same (0x1418 - 0x1376 = 0xa2). However to know the main, we need to kind of brute force it. We know the main function ends right after the call function, so we should look for an address that ends with 145c. We need to use some number paired with a specifier to find this. EX: %(number)$p

<figure><img src="/files/3QBTrl6hRdNrXpq9CnHG" alt=""><figcaption></figcaption></figure>

I did it and turns out, none of it matched. I looked up in a writeup, and apparently my disassemble data is just wrong. This is the data of the correct solution.&#x20;

<figure><img src="/files/dNOBN8NWafuj2p50y267" alt=""><figcaption></figcaption></figure>

I'm not sure why my data is different, as there isn't even PIE in mine neither the digits at the end. But regardless, I tried to fix mine but it just didn't work so I'll continue on how the write up did it. They found out %19p worked, as the last 3 digits "441" matched.

{% embed url="<https://hackmd.io/@sal/HJtUdR5n1e>" %}

<figure><img src="/files/M0T03AQ5AG7BdyLMwYB3" alt=""><figcaption></figcaption></figure>

They then used to calculate the offset.&#x20;

<figure><img src="/files/y34VX81FbBjFMJ80fXMz" alt=""><figcaption></figcaption></figure>
