Hello and welcome back! We are going to be reversing on a Windows box today. This challenge we are going to look at today is called "The Art of Reversing" and it is from the website HackTheBox. If you prefer you can watch my corresponding YouTube video for this challenge here:
This challenge provides us with a product key, cathhtkeepaln-wymddd, and we have to figure out how the product key is generated. We are required to find the username/number of activation days that will yield the same product key. This challenge was pretty fun and I hope you enjoy.
Optional Materials to Follow Along
If you want to follow along feel free to download the VM I provide. You can find instructions on importing the VM here. If you don't want to use my VM that's fine, my feelings won't be shattered. But you will at least need the binary. You can download the binary here. The binary comes in a password protected zip file. The password is hackthebox.
Note: You will need a valid HackTheBox account to download the binary.
The initial triage is going to be a little different since we are on a Windows box. The idea is the same we want to gain some insight on the binary before we use open this up in a static analysis tool. There's a very nifty tool that will streamline the process for us, detectiteasy (die) I know the acronym is pretty dark but it's a great tool! If you're using my VM you can simply right click on the file and run the analysis as shown below:
Let's run die on our binary.
Die provides lots of information. We see that this is a 32-bit binary. It's a .NET executable. This is important because it will make our analysis a lot easier (assuming the author didn't add any anti-analysis measures. We also see there's a button for Imports. Let's take a look at what this shows us.
On Windows a Dynamic Link Library (DLL) is similar to a shared object on Linux. We see this binary is only importing the
mscoree.dll library. Below that we see the functions used from this library. It happens this binary only uses
_CorExeMain. Since these do not tell us anything about how this binary functions, let's look at the strings to see what else we can find out. Luckily, DIE can show us the strings as well. We just need to click on the String Tab in the .NET -> Metadata section.
There are actually quite a few strings that were interesting but, by far the most interesting is the
buttonCreateProductKey_Click. Intuitively, we can assume analyzing the function that is called when pushing this button will allow us to figure out how the product key is being created. Now, with all of our initial triage out of the way, let's open the binary up in a tool called
Static Analysis with dnSpy
It is important to note that this is a 32-bit binary. So we need to open this up in the 32-bit version of
dnSpy. On my VM the 32-bit version is
dnSpy-x86.exe. Once you open the correct version of
dnSpy, press File-> Open and navigate to the binary. If successful, it should look something like this:
If you press the arrow of the DotNetTwo tab, it'll open up the DotNetTwo.exe, open that tab and you'll see various tabs that we can inspect.
We are going to spend out time looking at the
DotNetTwo. This contains the main function and the other pertinent function we are interested in. If we expand it, we'll see two components:
Program. Program is only going to contain the main function which doesn't do much aside from call the
Form1 function. So, let's analyze
We see that this function just calls the
InitializeComponent() function. We do see the
buttonCreateProductKey_Click function. Let's go ahead and analyze the
We see the function first checks whether the Username or Number of Days textboxes are empty. If either are empty, it throws and error and returns. The function then assigns our Username to the variable
text. Since that's not a very useful variable name, let's rename it! You can rename variables by right clicking anywhere in the disassembly menu and pressing the "Edit IL Instructions" option.
Once you're in this menu, press the "Locals" tab and this will provide you the option to rename variables. What I hate about this is as you can see there are no variable names.
Fortunately, these variables are in order so the first variable at Index 0 corresponds to our
text variable. Similarly, the rest of the variables will correspond to the next variable we encounter and so on. With this in mind, let's rename the first variable in the list to username. Press ok and you will see the change reflects in the disassembled code.
After it grabs the username, it grabs the number of days. Let's rename
numDays. Then, we see some values get initialized. Let's just keep these values in mind for now. We see the username length gets checked. If we enter a username less than 3, it will yell at us and exit. Similarly, the number of days variable is also checked. If we enter a number less than or equal 15 or greater than 3650, it yells at us and exits. We don't need to worry about the
Application.DoEvents(); function. It simply checks whether there are any messages that need to be processed. We can also ignore the next line 196 as this does not do anything important as far as our analysis is concerned. After that we see another function is called
nPr. This has nothing to do with the radio station! The function is passed the length of our username twice as parameters. Keep this in mind. Let's click on this function to see what it's doing.
This function calls yet another function and returns. Note the parameters for this function. Recall that
r are both
username.Length. So, the function
FcDv has the parameters
username.Length - username.Length or 0. With this in mind, let's move on to see what this function is doing.
This function is simply calculating the factorial of
username.Length. And who said you'll never use Math in real life! Let's go back to our
createProductKey function and rename the variable to
username_length_factorial. Really rolls off the tongue right?
Line 198, sets a variable called
username_length_factorial / 2. On line 199, the variable word is set to
username but depicted as an array. For example, if we typed a username "Jaybailey216",
word would contain an array of characters:
["J", "a", "y", "b", "a", "i", "l", "e", "y", "2", "1", "6"]. Let's rename
username_as_array. We then see our username is passed to a function called
GetPer. Let's take a look at this function shall we?
GetPer calls another
GetPer function with different parameters. The
GetPer functions with three parameters is a recursive function.
GetPer calls a function called do on lines 58 and 60. Notice our username is passed by reference to these functions. Let's take a look at what this
Do function is doing to our username.
Fortunately, this function is simple. If
b are the same, then it returns. Otherwise, it swaps
b. Because this is a simple swap, we don't really need to worry about all the recursive craziness. We now know that
GetPer will return a jumbled up version of our username. To uncover the actual username we simply have to unscramble it. Before moving on, it's important to note that our scrambled username gets stored in a variable
ssOut on line 49 in the
GetPer function. Now let's go back to the
GetPer function finishes scrambling our username, a function
ToR (not to be confused with TOR) is called with our
numDays variable as input. This will likely lead us to the last piece of the puzzle.
We are hit with yet another recursive function. This function is pretty straightforward. All it is doing is turning our
numDays variable to roman numeral representation. With this in mind, let's go back to the
createProductKey function again and rename the
text variable to
numDays_Roman_Numerals. I know I'm mixing camel case with underscores, deal with it!
Now, the last step! Our
numDays_Roman_Numerals variable is passed to another function,
Luckily, this function is small and simple! It takes our Roman Numerals variable, turns it into an array, reverses it, and iterates over the array. It adds 1 to each element in the array. This is essentially getting the next letter in the alphabet. So, if our number was XIX, it would be changed to YJY. Then the array is changed to all lowercase letters so the string yjy would be returned in this hypothetical. Alright, we now have all we need to figure out the username/number of activation days that will give us the product key: cathhtkeepaln-wymddd. We know the username is simply scrambled. Unscrambling the username gives us the username "hacktheplanet". Now what about the number of activation days? All we have to do is reverse the string and subtract 1 from each letter to get the proper roman numberal. So wymddd, dddmyw (recall we reversed it in the DoR function. Then subtracting 1 gives us: ccclxv. We can get the value by adding up roman numerals values. C = 100, L = 50, X = 10, V = 5. So we have 100 + 100 + 100 + 50 + 10 + 5 which gives us 365! So the username/number of activation days is "hacktheplanet-365". Cute 😂.
Alright, that's all there is to it for this challenge. I do hope you enjoyed reading it and I hope you learned something as well! If you have any questions feel free to reach out to me on Twitter, Instagram, or Discord jaybailey216#6540. If you have a challenge you would like me to try, let me know and I'll give it a shot! I'll see you all next time!
Peace out! ✌🏾