DEV Community

Cover image for picoCTF "GDB Test Drive" Walkthrough
SJ W
SJ W

Posted on • Edited on

picoCTF "GDB Test Drive" Walkthrough

Introduction

In this post, we are going to delve into something entirely different from what you usually stumble upon in my blog. A few years ago, I was sort of getting into what is known as binary exploitation, a form of hacking that finds vulnerabilities in various binaries, which essentially are executables whose extension starts with .exe in Windows OS for example. My first step into the world of binary exploitation started with going over the book called Hacking: The Art of Exploitation by Jon Erickson, which basically revolves around the topic of the hacking technique called buffer overflow. For the next post right after this, I will go over some of the examples of buffer overflow by going over the CTF challenge that involves the ROP chain, but it basically initiated my fascination with the world of binary exploitation. However, due to some circumstances in life, my interest in it kind of waned eventually, and recently, after reading and watching various contents associated with it kind of rejuvenated my interest in the topic of binary exploitation again. For that exact reason, I decided to get back into it, by actually learning how to write a program in NASM (x86 assembly in Intel syntax), decompiling binaries using tools, such as Ghidra and Radare2, and completing various CTF challenges that have to do with reverse engineering in general.

In this post, we are going to try the CTF challenge called GDB Test Drive on the website called picoCTF, a website that hosts a multitude of CTF challenges for anyone to try for free. I love feeling challenged in general, and feel like there is no better way to learn something other than actually doing and struggling with it.

GDB Test Drive

Challenge Description

Okay, so here is how this challenge works: download a binary that must be examined with the tools of your choice, find and submit the flag that is hidden within the binary in order to complete the challenge. In picoCTF, every challenge works differently from another, but the one thing that remains the same is that you have to submit a flag for completing a challenge. Anyway, let's download the binary to see what's going on inside.

1. Examine the binary

Let's use the tool called Ghidra to decompile the binary we downloaded for the challenge.

Importing the binary

  • The author of this challenge clearly instructs one to run the binary with the GNU Debugger (GDB), but I just decided not to do so, since I felt lazy. I thought that using Ghidra would be enough to complete the challenge.

Ghidra asking whether it should analyze the binary

Showing options prior to decompiling the binary

  • Ghidra is showing various options for decompiling a binary, but I decided to go with the default choices, since it seems to be the safest basically.

Image description

Here we are! Now, let's get to the task of analyzing the binary and finding the flag hidden within it. To understand how the binary works, I decided to go through a list of functions that exist within the binary, to see if there is anything interesting.

Image description

Image description

So in C, every program starts with a function called main, and in the list of functions above, right alongside the function main, I have discovered a function called rotate_encrypt. The first thing that came to my mind when I saw it was that it might have something to do with encrypting a string into something illegible to our eyes, basically. Let's check out both functions of main and rotate_encrypt

Inside the function "main"

main function decompiled

In the screenshot above, we are basically seeing a decompiled version of the function main, and it is unclear what each variable is for, since every variable is replaced with the name that is prefixed by the word local. However, as you can see, about midway through the function, that it invokes the function rotate_encrypt with two arguments, to encrypt something that is passed to it, and one of the arguments is of a pointer, which I assume is an address to the first letter of the string. Let's see what gets passed to the function!

a string that get passed to the function rotate_encrypt

positions of variables within the stack

  • There are total four variables that get assigned with the 8 bytes worth of data each, and we know that they are located right next to one another, for the fact that local_38 is located 8 bytes before local_30, which is also placed next to local_28, and local_20. local_38 is located at the address of EBP plus the offset of -0x38 bytes (when the offset is lower, it gets placed higher), and since it occupies 8 bytes starting from the address, it stores the data from the address of -0x38 to -0x31. And below local_38 is local_30 whose location is the address of EBP plus the offset of -0x30 bytes. For local_30, it occupies the address of -0x30 to -0x29. You can assume the same for the last two variables: local_28 and local_20.
  • local_38 = 0x4c75257240343a41: The binary assigns the hexadecimal value of 0x4c75257240343a41 to a local variable of local_38 of undefined8 (I assume you can store the data of the size of 8 bytes). When a computer stores a string in memory (RAM), it usually stores a string in a series of numbers in succession, and each character is mapped with a certain number. Each cell in memory can hold up to a byte worth of data, and the data may represent anything from an integer of 0 to 255 (0x00-0xff). Since the number (0x4c75257240343a41) we are seeing above represents eight bytes worth of data, we can split the hexadecimal value into a byte worth of data (0x4c75257240343a41 --> 0x4c, 0x75, 0x25, 0x72, 0x40, 0x34, 0x3a, 0x41) and convert each to a character according to ASCII encoding, which results in the following string: Lu%r@4:A. In ASCII, 0x4c is mapped with a character L, 0x75 is mapped with a character u, and so on.
  • local_30 = 0x4362383846336235: Let's apply the same logic to this as the example above. Converting the hexadecimal numbers of 0x4362383846336235 to ASCII string results in the following string: Cb88F3b5.
  • local_28 = 0x6630624760433530: Converting the hexadecimal numbers of 0x6630624760433530 to ASCII string results in the following string: f0bG`C50.
  • local_20 = 0x4e64646267353361: Converting the hexadecimal numbers of 0x4e64646267353361 to ASCII string results in the following string: Nddbg53a.

When you concatenate each one of them, it becomes the following string: Lu%r@4:ACb88F3b5f0bG`C50Nddbg53a. So at this point, I would like to assume that the string gets converted to the flag we have to submit for the challenge. Let's see what the function rotate_encrypt does with the string we have discovered.

Inside the function "rotate_encrypt"

decompiled function of rotate_encrypt

Carefully going through the contents of the function, I have noticed the followings:

  • It copies the content of the string (Lu%r@4:ACb88F3b5f0bG`C50Nddbg53a) to a new pointer, with the help of the function called strdup
  • In line 11, it stores the length of the copied string.
  • In line 12, it iterates over the string using a for-loop.
  • In line 13, there is a conditional logic that checks the followings: (' ' < __s[local_20]) && (__s[local_20] != '\x7f'). First, it evaluates whether a character in the string is bigger than ' ', which in ASCII evaluates to an integer of 32. Second, it checks whether a character in the string is not equal to \x7f, which in ASCII evaluates to an integer of 127. If a character evaluates to "true" in both cases, it enters the line 14, else move on to a next character of the string.
  • In line 14, it adds an integer of 47 to a character, and assigns it to the variable cVar1. For example, if I add ')' - in ASCII, it is 41 - with 47, it becomes 88 ('X').
  • In line 15, it checks if the variable cVar1 is smaller than an integer of 127. If it evaluates to "true", then a character is replaced with the content of the variable cVar1. If not then, it subtracts an integer of 94 from cVar1, and replaces a character with it.

Since we pretty much figure out how the program works, we can try to replicate the code with the help of Python!

2. Exploit

Since we know how the program works, we should be able to figure out the flag needed to complete the challenge!

Failure to submit the right flag

failed code

Here is my first attempt at writing the python code that replicates the function rotate_encrypt in the binary we analyzed. What it does is that it iterates over the string, converts each character in the string to a number according to ASCII encoding, adds 47 to it, and checks whether it evaluates to being bigger than an integer of 127. If it does, then it subtracts 94 from a number, and lastly, converts the number back to a character, which gets appended to a new string.

What is this? it is broken

Running the script results in the following string: {FTCocipr3ggub3d7_3v1rd_}5538db2. Hmm, looks a bit broken, since the flag is supposed in the format of picoCTF{flagssss}. Anyway, let's try to submit it and see if it is an answer. Surely, it is not an answer! Still though, I find some solace in the fact that reversing the first 8 characters of the string above results in the following: picoCTF{. After learning that very important fact, I come up with a new script to rectify the situation.

Finding the right flag

Since reversing the first 8 characters results in the right result of picoCTF{, which is how a flag should start, we can reverse each data stored in each variable in the following manner:

  • Lu%r@4:A --> A:4@r%uL
  • Cb88F3b5 --> 5b3F88bC
  • f0bG`C50 --> 05C`Gb0f
  • Nddbg53a --> a35gbddN

When you concatenate each reversed substring, it results in the string A:4@r%uL5b3F88bC05C`Gb0fa35gbddN. Let's run the script to see what it outputs.

Image description

Image description

It is still weird. Maybe, I should reverse it? Let's fix the code and output it in reverse.

Image description

Image description

Seems like the right flag! Let's submit it and see how it works! It is right, which means that I have successfully finished the challenge!

It was a fun challenge overall to ultimately test my knowledge in reverse engineering. From time to time, I will post walkthroughs of different challenges that I come across in picoCTF from time to time. Thank you for reading this post!

Top comments (0)