picoCTF "GDB Test Drive" Walkthrough
Tags: ctf, hacking
Published: 2024-05-02 15:20:06

# 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](https://www.amazon.com/Hacking-Art-Exploitation-Jon-Erickson/dp/1593271441)** 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](https://play.picoctf.org/practice/challenge/273?category=3&page=2)** 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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qv6siz70z9o2ru3p37d6.png)

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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zzzxsmip0qxhj2omv9wa.png)

- 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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5livrkljk86fwya82hpd.png)

![Showing options prior to decompiling the binary](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4uuwb4e7mf7li2mgzfac.png)

- 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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pnzytr1igmhtj2zegfzk.png)

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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nig4cz3vhm4s2uq403ba.png)

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/cotsbxkyd3f5vgit67gf.png)

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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/em98txfa4aniueb3566h.png)

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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fyr1eidiy8jzs00jkeh2.png)

![positions of variables within the stack](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jpzg7ne5qzf5fs8q29ij.png)
- 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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kexq4tw4opt9xxiw8l06.png)

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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/u8cpjixzo72dfctfp8jf.png)

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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zqqcq051pi1qn1b2ez4d.png)

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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/v8ap94hgr1e9osti0qzq.png)

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ub3zm2b1z9kma2mbs1m5.png)

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

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9dqc78see4okfv3uaolh.png)

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3cdtef0ullhx3aibvjbr.png)

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!