Buffer Overflow 2 : picoCTF 2022

introduction

Musyoka Ian
10 min readMar 19, 2022

Hello guys back again with another walkthrough. Today we are going to be tackling a stack based buffer overflow challenge on a 32 bit system. It was a challenge on the pico mini ctf 2022. The challenge start off by you being given both a binary and the source code of the binary for analysis. By viewing the source code you discover that the program has a classic buffer overflow but this time you are not only supposed to control the return address but also the arguments on the stack. The program checks to see of the arguments provided much with what has been provided in the binary and if they are the same the program prints the flag. It’s a nice challenge that teaches buffer overflow and we are also going to learn a lot about debugging with GDB without much say let’s jump in

After downloading the two attachments provided we do a file command and see that one is an executable and the other probably the source code of the compiled executable

Having the source code makes our jobs easier since we won’t have to decompile the binary with ghidra to get the source code of the application. We are going to be doing two kinds of analysis namely:

  1. static code analysis (going through the source code manually)
  2. dynamic analysis (using GDB with gef extension and examining memory address while the program is in a running state)

I started by first looking at the source code before executing the program to get a feel of what the application is meant to do.

The program has 3 functions namely:

  1. main function
  2. vuln function
  3. win function

Anything that is in the main function or referenced on the main function gets called when the program is executed.

The win function basically does does two things.

  1. It opens the flag and reads it
  2. Then checks the two arguments if it’s what has been declared in the binary then the program prints the flag

But the problem is this function had not been declared or referenced in the main function meaning there’s no way it’s supposed to get executed. The main function just calls the vuln function

And looking at the vuln function we see it’s uses gets which is vulnerable to a buffer overflow

Gets is supposed to read 100 bytes but with gets it is impossible to tell without knowing the data in advance how many characters gets() will read and this causes gets() to continue to store characters past the end of the buffer which causes a buffer overflow. Meaning we can store in the stack as many characters as we want. Our main goal now is to overwrite the EIP register so that we can control the return address and cause the program to call the win function.

So our plan of attack is first to overflow the buffer and control the instruction pointer to call the win function and see if we can get the flag.

First i made the program executable using the chmod command

Then executed the program from the terminal

Looking at the screenshot above we see that the program just prints whatever we wrote back. I decided to open the program with GDB for debugging

Next i decided to check the compile protection in place since this will define how we will exploit the buffer overflow

Looking at the screenshot above we see that the only protection enabled is NX this will prevent the stack from being both writable and executable at the same time meaning we cannot just drop shellcode to the stack and execute it

Next i decided to set a couple of breakpoints the first one was in main function

and executed the program. Looking at the screenshot we did hit the breakpoint at the main. We know that the defined buffer size is 100 bytes

So i created a string of 200 A’s and sent it

Looking at the screenshot below we caused a segmentation fault and this cause the program to seg fault since the memory address in the instruction pointer was invalid

We want to control the EIP and for this to happen we need to know the exact amount of byte we can use to overflow the buffer until we reach the EIP address

Am going to be using the cyclic redundant pattern to figure out the exact amount of bytes we need. First i created a 200 byte partern using gdb then executed the program as before

This time when i supplied the input the program crashed when the memory address was 0x62616164. This is important because we can use it to determine exactly what our offset is.

By using the command

pattern offset 0x62616164

We see that we begin overflowing the EIP after 112 bytes. So i created a string of 112 A’s and four B’s to confirm this

I again executed the program and send the input and looking at the screenshot below EIP has only B’s

Sweet. we know that if we put a memory address here if it’s valid our code will jump to it. To demonstrate i did put the address of main. To get the address of main we just use the following command in GDB

info functions

And looking at the screenshot below we get that the address for main is 0x08049372

I created a python script that will do exactly what we’ve done but send the address for us

After executing the binary using gdb we can see that we hit the main function twice during the execution of the program

The first time was when we were performing the buffer overflow and the second time was when we overwrote the EIP to point at main. Now we have a POC that the buffer overflow works, next we want to jump to the win function. Looking at the address we had before we see it has the address of 0x08049296

Let’s replace the address of main with the address of win

Saved the exploit and executed it again

Looking at the output below we successfully jumped to the win function

It needs us to create a flag.txt for testing. That’s the next thing i did

The again executed the binary to see if we were going to get the flag

But looking at the screenshot below we just crashed without getting the flag.

The reason is because we did not provide the correct arguments causing the program to exit without printing the flag

There is a check to make sure argument 1 is 0xCAFEF00D and argument 2 is 0xF00DF00D

My first though was why don’t we just jump the checks and just use the address that prints the flag. The get the address we have to disassemble the win function using the command

disassemble win

Then i took an address near the printf call and tried to see if i could leak the flag by bypassing the argument check condition. But looking at the screenshot below it failed

Meaning we’ll have to satisfy those two conditions to get the flag. Back to debugging

Let’s examine what those memory addresses holds after we perform a buffer overflow

We are interested in the following two compare call

I did set a break point in both of them

Then executed the program again. Looking at the screenshot below we hit the first breakpoint which we had set

And the program is doing a comparison between 0xcafef00d and memory address [ebp+0x8]

Let’s examine it manually and see what that address contains. It contains the following information 0xffdd21e4

It doesn’t look like it has any of the input that we earlier provided. I decided to add more bytes to the buffer even after we had overwrote the instruction pointer and see if we may overwrite the values of the two arguments. To do this i edited the exploit to add more bytes

I saved the exploit after adding 30 more bytes which will be added to the stack and executed the program again and set the same breakpoints as i had done before. Comparing the address again we see that it now contain B’s which we had specified in our buffer

Meaning we successfully overwrote the value of that address. The fact that we overwrote means we can also put our very own value in that memory address and we want it to be 0xCAFEF00D and 0xF00DF00D.

To find out the exact point we start overflowing the compare addresses i used the same technique i did when trying to find the EIP overwrite.

I created a cyclic redundant pattern of 30 bytes

Added the string to my exploit code

Saved the script and executed again. Remember to set the two breakpoints in the win function

Examining the memory address again we see that it has the value 0x61616162

And searching for that pattern in gdb we get that after 4 bytes we begin overwriting the first argument on stack

Sweet now we know the exact position where the first argument starts on the stack so i edited the script to add the value 0xCAFEF00D to that address

Saved the exploit and executed it again. My main focus was to see if the first argument had been edited successfully. Looking at the screenshot below it work we successfully changed the value on the stack

Next was to edit the second argument on stack. I followed the same exact steps as before

Looking at the screenshot above we see that immediately after overwriting the the first argument we begin overwriting the second argument. I added the second value to the payload

I saved the script and ran it again and looking at the arguments on the stack all seems well

So i continued execution of the program and looking at the output we get the fake flag which we used for debugging

Next i spawned up a new instance of the challenge and attacked the live system using the exploit i had created and looking at the screenshot below we get the flag

The source code for the exploit can be found below

#!/usr/bin/env python3from pwn import *
import sys
if len(sys.argv) != 2:
print("[~] Usage: python3 exploit.py <HOST>")
sys.exit(1)
host = sys.argv[1]
port = 56164
context(terminal = ['tmux', 'new-window'])
#make sure you put the correct binary name and path below
binary = context.binary = ELF("./vuln")
#connect = gdb.debug("./vuln", "b main")
context(os = "linux", arch = "i386")
connect = remote(host, port)
log.info("[+] Starting buffer Overflow")
connect.recvuntil(b"Please enter your string:")
log.info("[+] Crafting payload")
payload = b"A" * 112
payload += p32(0x08049296)
payload += b"A" * 4
payload += p32(0xCAFEF00D)
payload += p32(0xF00DF00D)
log.info("[+] Sending Payload to the remote server")
connect.sendline(payload)
connect.recv()
connect.interactive()

Hope you enjoyed the walkthrough if so clap for me down below and follow me so that you don’t miss any upcoming walkthrough

--

--