blog feature image

Ghidra Tutorial: CTF Challenge

Zaid Khaishagi

Nov 20, 2023

This is the final article in the Ghidra Tutorial series. In this tutorial, we will make use of Ghidra to solve an actual CTF challenge. We will analyse the binary provided for the CTF challenge in Ghidra and then obtain the flag from it which we need to submit to get the points for it. The challenge we will solve in this tutorial is from the CSAW CTF Qualification Round 2023 which ran from 15 Sept., 12:00 EDT to 17 Sept. 2023, 12:00 EDT. You can find the competition details here: https://ctftime.org/event/2087.

Capture The Flag (CTF) competitions

CTF competitions are cybersecurity competitions where people compete to solve challenges of different categories and get points as they solve more of the challenges. The challenges focus on some type of offensive security skills. These competitions are held with a time limit. They are usually organised around the weekends so that more people can participate in them. The organisers prepare various challenges of different categories such as web exploitation, reverse engineering, binary exploitation, forensics, cryptography and so on. Recently, competitions have also started introducing AI-based challenges.

Upcoming, past and currently running competitions can be viewed on the CTFtime website here: https://ctftime.org/. CTF competitions are a great way to practise your cybersecurity skills, compete with and against other people, get exposed to new types of skills and challenges and learn from other people. In order to get the most learning experience, you should discuss with other people in your team and, if and as the competition rules permit, discuss with other teams on Discord servers—most competitions maintain a Discord server for the competition. Once the competition ends, people publish their solutions as challenge writeups and share them online. Reading these and then trying to solve the challenges again is a great way to learn. These competitions usually keep the infrastructure for the challenges available for a while after the challenge ends, before they take it down, which is useful particularly for web challenges.

The challenges presented in these competitions are normally more focused and tailored to test a particular skill or group of skills as compared to penetration testing which is more open ended and where you don’t even know if there is an exploitable vulnerability. The challenges provide some information about where to find the challenge, such as a web address, an executable binary, an IP address, etc. and then they ask you to submit a flag string to show that you successfully completed the challenge. Flag strings usually include the name of the competition and then a string enclosed in braces, e.g., something like this: csawctf{some_string_here}.

If you want to get started in CTFs, picoCTF is a good place to start. It is aimed at beginners and they also have many practice challenges on their website as part of the picoGym.

CSAW Qualification Round 2023

CSAW is a competition that is held annually and is organised by NYUSEC. CSAW is held in two stages: a qualification round and then a final round. The qualification round is held online and is open to all. The finalists are selected based on their performance in the qualification round. These finalists are then invited to participate in the final round. It can be held both online and in-person across different time zones. The details for the 2023 competition are published here: https://www.csaw.io/ctf.

Rebug 1

The challenge we are looking at in this tutorial is called “Rebug 1” and is part of the “rev” category (reverse-engineering category) from the CSAW Qualification Round competition. This challenge can be solved using only Ghidra which makes it a good example to use for this tutorial. The other challenges are better solved using a combination of tools. You can download the challenge binary from here: test.out

The challenge summary as displayed on the official website is also given here:

Solving the challenge

Let’s start by downloading the challenge binary and then executing it. You might have to change the permissions for it to allow execution. To allow execution, use the below command. It will add the execution permission for the user.

$ chmod u+x test.out

When we run the program, we are presented with a prompt asking us to enter a string. If we input a random string, we are told that it is incorrect.

$ ./test.out
Enter the String: abcdef
that isn't correct, im sorry!

So, the program wants us to enter a specific string.

Let’s open it in Ghidra.

We see that there is a main function and one of the first things it does is print a string asking the user to enter a string. This is exactly what we saw while running the program. It then receives the user input using a scanf function, into a variable called local_408. So, let’s rename this variable to better reflect its use. I personally like to append to the name already assigned to it by Ghidra. This is because it doesn’t shuffle the variable around in the symbol tree (which you can see if you expand the main function in the symbol tree); and also because sometimes the Ghidra-assigned name can actually be useful and give you some insight, for example when you are debugging the binary and inspecting its memory during runtime. I’ve renamed the variable to local_408_input_str.

After that, we see that there is a for-loop. This loop uses a variable called local_c as an indexing variable for the loop. Let’s rename this. The end condition for the loop is local_408_input_str[local_c_index] != '\0'. Looking at the for-loop, it’s going to iterate over the input string and end the loop when it encounters a null-character which is when the string ends.

We can say that the for-loop is a manual implementation to get the string length. Let’s rename the variable again to reflect that to local_c_inp_len. The next lines show a condition check for whether the input length is equal to 0xc which is 12 in decimal notation. If the condition is true, the program prints out a string "that\'s correct!".

So, it seems as though the program wants an input string of length 12. Glancing through the next lines, we see some functions like (EVP_MD_CTX *)EVP_MD_CTX_new(), EVP_md5(), EVP_DigestInit_ex, EVP_DigestUpdate, EVP_DigestFinal_ex and EVP_MD_CTX_free. These functions indicate that there is some functionality relating to calculating an md5 hash. However, if we look through the variable used in this section of the code, we see that the input string or the input length are not used in the arguments to the functions. At the end of the if-block, we see that the program prints out a string using printf which fits the ctf-flag format. What this indicates is that the program may be calculating the md5 hash using some hard-coded data or something which is not influenced by the input string. This means that not matter what the input string is, as long as it passes the input-length condition, the program should perform the same processing and then present the same output.

One thing that we can try is to run the program again and then give it another random string which is 12-characters long and see what happens.

$ ./test.out
Enter the String: 123456789abc
that's correct!
csawctf{c20ad4d76fe97759aa27a0c99bff6710}

We receive a string that could possibly be the correct ctf-flag for this challenge. Let’s try to enter this into the challenge and see if it’s the correct flag.

We have solved the challenge! The ctf-flag was csawctf{c20ad4d76fe97759aa27a0c99bff6710} and we were able to get it by simply entering a 12-character long input.

Conclusion

We were able to use Ghidra to analyse an executable binary provided as part of a CTF challenge. We saw that in the decompiled code view of Ghidra, the program asked the user for an input string and then received it using a scanf function into a local variable. It then used a for-loop to calculate the string length. After this, it checked if the length was equal to 12. If not, it would print out that it was incorrect. If it was of the correct length, it printed a message saying that it is correct. It then calculated an md5 hash using various functions. However, we saw that it was not making use of the input string nor its length for these functions. Based on this, we made an educated guess that the processing is not affected by what our input is and that it would print out the same message regardless, as long as the condition check succeeds. We tried to run the program again and give it a 12-character long input. When we did this, it gave us the ctf-flag which we submitted and solved the challenge.

Remember that Ghidra is only one tool among many that can be used for reverse engineering. Ghidra won’t be the best fit for every problem, but it is a good tool to know and make use of. While participating in CTF competitions, the challenges will require you to use a variety of tools and may even push you to use a new tool that you’ve never touched before. You don’t need to gain mastery over every tool that you pick up, instead you should be able to make use of the tool to the level that your current problem requires. In the end, the tools are only a means to accomplish a goal. Cybersecurity requires you to be adaptable and learn new things as you go.