HackTheBox – Exatlon Write-up

Dear readers,

Today’s post is on the Exatlon challenge which is a reverse engineering challenge. The challenge was created on 2nd May 2020. The challenge tests the knowledge of knowing the existence of packers and reverse engineering. Let’s dive right into the write-up.

Fig 1. Exatlon challenge on HackTheBox

Files provided

In this challenge, there is only one file being provided which is exatlon_v1 (download it here). It is a file that can only be run on a 64-bit Unix system. If you are using Windows as your primary operating system, do use a virtual machine or Windows Subsystem Linux (WSL). I used WSL which allows you to boot up the system and navigate around easily. Remember to use chmod 774 to change the permission of exatlon_v1 before it can be executed.

Software required

As this is a reverse engineering challenge, we will require a few tools to help us do static and dynamic analysis. Below are the tools required. I used Ghidra for the static analysis process.

Static analysis (Any one of them):

Dynamic analysis:

Analysis process (Packer discovered)

When we first launch the program on the terminal, we will notice that the big title is slowly being printed out. After printing the title, the password is being prompted.

Fig 2a. Running the program on the terminal

When I opened up the program on Ghidra, navigate to (Search > For Strings…), and search for the string “[+] Enter Exatlon Password“, it cannot be found. Even when I searched for “Password“, it cannot be found as well. However, scrolling through a list of strings in the program allows me to discover that the program has been packed by UPX (see Fig 2b). The UPX is a popular packer that packs programs so that it ruins the execution flow or the strings in the content during static analysis. As such, the program will only unpack during execution. Packer such as UPX helps to make analysis harder for new reverse engineers as well as anti-viruses that depend on static analysis.

Fig 2b. Exatlon_v1 is packed by UPX which is found in string search

Therefore, UPX is downloaded and unpacked exatlon_v1 using the following command:

upx.exe -d exatlon_v1

After unpacking, we will be able to search for the string “[+] Enter Exatlon Password” (see Fig 2c). Besides that, we will be able to see more functions being revealed as the control flow is no longer disrupted by any packer (see Fig 2d).

Fig 2c. Password prompt found showing unpacking is successful
Fig 2d. All functions are revealed after unpacking Exatlon_v1

Analysis process (Search for password/flag)

When we search for the location used the string to prompt us for the password, we will be directed to the main() and we can see that the prompt string is highlighted in yellow and the section where the big title is being printed slowly with 1 second of sleep delay as show in the red box in Fig 3a. Note that I have renamed some of the variable names and return types hence what you see on your fresh copy of unpacked Exatlon_v1 will be slightly different from mine.

Fig 3a. Password prompted and the title to be printed code section found

During static analysis, I noticed that the 2nd parameter was added to the exatlon() which is the user input as it can be seen in the assembly code but not in the decompiled code. Therefore, I added the 2nd parameter in the decompiled code (see Fig 3b). To understand which registers are used as parameters, see this source.

Fig 3b. Missing 2nd parameter which is the user’s input string is added into the decompiled code on the right

If we analyze the remaining main(), we will notice that the variable, local_38, used as 1st parameter for exatlon() is being compared to the static string of numbers (see red box in Fig 3c). The result is that placed in bVar1 variable. If the local_38 is different from the long static string “1152 1344 1056 1968 1728 816 1648 784 1584 816 1728 1520 1840 1664 784 1632 1856 1520 1728 816 1632 1856 1520 784 1760 1840 1824 816 1584 1856 784 1776 1760 528 528 2000 “, it will return FALSE can causing the big title and prompt to be printed again unless user input letter ‘q’ which causes the program to exit.This can be seen in the blue box of Fig 3c. Finally, we want to go to the green box which shows that the ELSE block will be reached if the comparison is TRUE. However, we noticed that no flag is printed if the password is correct. Therefore, the password to enter must be the flag.

Fig 3c. Remaining code of main()

Since the password to enter is the flag, we have to decode exatlon() which encodes the user’s input before comparing it with that encoding long string of numbers.

As we look at the encoding section in exatlon() in Fig 3d, we can see that the user’s input password is being assigned to local_78 in the red box. A WHILE loop then occurs in the orange box where each character of the user input is placed in pcVar2 in the blue box. Finally, some kind of encoding that uses 4 bits of bit shift before concatenating each encoded string/character in the new variable store_user_passwd_encoded. The address is that incremented by one to address the next character of local_78 which stores the user’s input password in the brown box.

Fig 3d. Encoding section in exatlon()

Since we know that the encoding is done on each character, we can get the encoding of each character. Firstly, we have to use GDB to get the encoded value of each character dynamically. Alternately, you can reverse the values of the encoded flag if you want. I used the 1st way.

If we analyze the assembly code of the comparison between the encoded user input variable, local_38, and the stored encoded flag, we can see that the address in local_38 is stored in register RAX. As a result, we can set our breakpoint with the relative address of 0xd2d.

Fig 3e. Location of encoded value stored in RAX register

Launch the program using GDB with the following command:

> gdb exatlon_v1

Once you launch it, run the program using “run” command, then press CTRL+C to terminate the program when we are prompted for the password. Use the “info file” command to see the entry point’s address. We can see that the start of the address is 0x404 (see Fig 3g).

(gdb) run
[+] Enter Exatlon Password  : ^C
(gdb) info file

Fig 3f. Commands/actions to be done in GDB

Fig 3g. Entry point’s address found that begins with 0x404

Since the address begins with 0x404, we can set our breakpoint to at 0x404d2d as the last 3 characters of the hexadecimal (last 12 bits) are fix since it is the relative address for both GDB’s address and the address shown in Ghidra. Besides that, GDB disables ASLR by default. Therefore, the last 12 bits are definitely the same. The only thing is the bits that are before the last 12 bits may be different hence we needed to see the entry point’s address to know the value which we now know is 0x404.

Once we set our breakpoint, we can use the “r” command to rerun the program again. I inputted “HTB{” as it is the starting value for flags in HackTheBox. As we know that the address to the stress is stored in the register RAX, we can use the x/8x command to print out the address that stores the encoded user’s password. This allows me to know that the address to the stress is 0x005c47b0. Using the “x/s” command on that address allows me to print out the content in that address in String format which gives me “1152 1344 1056 1968 “. Comparing to the static string ” 1152 1344 1056 1968 1728 816 1648 784 1584 816 1728 1520 1840 1664 784 1632 1856 1520 1728 816 1632 1856 1520 784 1760 1840 1824 816 1584 1856 784 1776 1760 528 528 2000 “, we can see that the 1st 4 numbers are the same. This shows that the user input password in the string is encoded character by character and the adjacent characters do not affect each other. See Fig 3h for the example.

Fig 3h. The encoded values of the string “HTB{

Flag obtained

If we repeat the steps and try to obtain all the ASCII characters that can be typed on a keyboard, we will obtain the list below. The format is in <encoded number>:<actual character>.

1968:{ 2000:} 1520:_ 2016:~ 1536:` 528:! 1024:@ 560:# 576:$ 592:% 1504:^ 608:& 672:* 640:( 656:) 720:- 688:+ 976:= 1456:[ 1488:] 1984:| 1472:\ 704:, 736:. 752:/ 1008:? 960:< 992:> 928:: 944:; 784:1 800:2 816:3 832:4 848:5 864:6 880:7 896:8 912:9 768:10 1552:a 1568:b 1584:c 1600:d 1616:e 1632:f 1648:g 1664:h 1680:i 1696:j 1712:k 1728:l 1744:m 1760:n 1776:o 1792:p 1808:q 1824:r 1840:s 1856:t 1872:u 1888:v 1904:w 1920:x 1936:y 1952:z 1040:A 1056:B 1072:C 1088:D 1104:E 1120:F 1136:G 1152:H 1168:I 1184:J 1200:K 1216:L 1232:M 1248:N 1264:O 1280:P 1296:Q 1312:R 1328:S 1344:T 1360:U 1376:V 1392:W 1408:X 1424:Y 1440:Z

If you match number by number of the encoded flag, you will be able to obtain the flag which is:

You can download the original file here and the unpacked file here.

I hope this post has been helpful to you. Feel free to leave any comments below. You may also send me some tips if you like my work and want to see more of such content. Funds will mostly be used for my milk tea addiction. The link is here. 🙂


Leave a Reply

Please log in using one of these methods to post your comment:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.