Introduction
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.
You'll also need a disassembler. I recommend IDA or Ghidra. With all of that out of the way, let's get reversing!
Initial Triage
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 dnSpy
.
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: Form1
and 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 Form1
.
We see that this function just calls the InitializeComponent()
function. We do see the buttonCreateProductKey_Click
function. Let's go ahead and analyze the buttonCreateProductKey_Click
function.
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 num
to 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 n
and r
are both username.Length
. So, the function FcDv
has the parameters username.Length
and 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 nToStop
to 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 word
to 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 a
and b
are the same, then it returns. Otherwise, it swaps a
and 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 createProductKey
function.
After 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, DoR
.
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 😂.
Conclusion
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! ✌🏾