Bypass | HackTheBox
Bypass
Challenge Introduction
This challenge requires us to input username, password, and secret key to get the flag. The image below is the challenge introduction.
Challenge Introduction
And the image below is the challenge.
Challenge Display
When we examine the executable,
Examine Executable.
Okay, this is windows executable, which most likely was built using .net. From the previous story, I told you basic reverse engineering using gdb in examining GNU compiled program. Now, we are facing a different problem.
I am about to tell you about my lesson learned. If you want to jump to the solution, you jump to the solution part. The first thing I learn about programs is programs do not have to have the main function. The main function indeed is required in the c program but not a universal standard. I spend a lot of time looking for the main function (haha).
The second thing I learn about debugging and reversing is GDB stands for GNU Debugging. It can only debug GNU compiled program like C programs. I also spend some time just to figure it out.
The third thing I learn about reversing is if we can execute the file locally (like .exe program), reversing is not about guessing the right answer only but also can probably be patching. What does it mean? In a simple sentence, patching a program is when you can modify the program binary so the program behaves differently, for instance, skipping authentication logic by bypassing the compare code, etc. I will tell you about it from this challenge writeup.
Preparation
The first thing across my mind when we can get the executable and we know that we do not need to solve the problem remotely is I do not have to find for the correct answer but instead, I will modify the program code hence bypassing all the authentication process.
After a long run, I finally found a tool that can help me, Ilspy. Ilspy is a tool for decompile and reverse engineering .net program. It is cool indeed. Ilspy need a plugin to enable the code modification, Reflexil. Now let’s start to solve the challenge.
How to Install
- Download Ilspy executable. DO NOT INSTALL FROM MICROSOFT STORE, YOU CAN NOT INSTALL THE PLUGIN. Extract the zip file into a folder.
- Download the reflexil plugin. Extract the zip file into a folder.
- Move all the reflexil data at its root to the root of ilspy and start ilspy.exe.
Solution
After I downloaded the challenge, I import the program to Ilspy. You can use the drag and drop feature from your explorer to Ilspy.
Import program to Ilspy
Now you can expand your program tree. If it is a correct .net program, it will extract all the information regarding the program. If your display is not as precise as mine, do not worry, it is not necessary for now.
Program Tree
After you open the tree, you can see there is a metadata-kind-of-folder that contains all header information regarding the program. We are not using them now. The next item is references & resources, we do not do much as well (I will explain it whenever we are needed to use them). I want us to focus on the function part or maybe the program part, the one with ‘{}’ symbol.
This is also important. Those numbers from 0–7 are a class. We can call it a module. So a module contains several attributes and methods. When we are about to modify, we need to go deep into the method part. Again I spend a lot of time on how this ‘modifier’ plugin works.
Code Segment in Ilspy
Back to the problem, now we have the decompiled program, now we need to know the flow. The first thing you need to find out when you want to examine the program flow is the entry point.
(Again, you can skip this part if you do not want to know how I know that module 0 is the entry point) … Ok, this is the plot twist. I am a beginner in reverse engineering, so I admit I do not have a solid reason why am I doing this but this is how I think. Like I said above, I think every programming language is mandatory to have the main function, it is not, but, what every program has is a definition of the entry point and I assume all those information are attached in a kind of “metadata” or “headers”. So I tried to look at the optional header inside the metadata part and look for something about the entry point and I got it.
Header Part Looking for Entry Point
(cont) To be honest, I was disappointed when I get the message, but I notice something like 0 things. For now, I just want to document it for further evaluation, but I legit think it is referring to our class/module 0 below.
(cont) Because my understanding is not good and furthermore the fundamental reason for considering that 0 referring to module 0, I ran double check on the source code and try to analyze the flow inside.
Examining Module 0
Module 0 Source Code
If we analyze it for just a slight, it is difficult and not making any sense. Let’s examine it more calmly. So basically, it is a class 0 which has 3 attributes/class variables which are string 0, string 1, string 2 which has an initial value of something like 5.8. This class also has 3 methods, something like void 0, bool 1, and void 2.
You can also check for the other class, but for me, all the classes are just complimentary for our main flow. That is why I did not search or examine further. If we examine further the void 0 method, it becomes more interesting.
Examining method 0
It starts with an if statement and its operand is derived from the returned value of the function 1. We can also see that function 1 is indeed return boolean. If it is true, it will call method 2 and end the application. Otherwise, the program calls itself and keeps looping.
Examining method 1
It starts with printing something to the screen. Its text is held on 5.1 module variable. Unfortunately, we can not get the value. The next instruction is to receive the user input via Readline(). Both instructions (the write and read) are repeated and then it returns false.
… It is kind of similar to something, is not it? Yes! That is our program flow. If we recall the program, the program asks for username and password (printing the instruction and then ask for the input). Okay, this is our key 1. The only problem we face is that it is forced to return false, which prevents our program to continue.
Cracking method 1
Now, common sense, we need to modify this false into true so we can continue to the next instruction. To modify the binary, we must choose the function 1 on the tree,
Open the Function 1
And then we click the gear button on the top bar.
Open Editor
KEEP IN MIND, we can not modify the source code. Even they can display the source code, this is a compiled program, which means, we need to modify the assembly code to modify the behavior. Now examine the table below. We can see number 01,02 and 06,07 are about loading the string value to the system and prints them. Line 04 and 09 are handling user input. From 10–15 is just another instruction code to finishing the function. Okay, where is the return false thing? Again! keep in mind that return in a programming language is equal to put something into the stack and that value will be referenced to another process.
Now we examine one by one. You can choose the line you want to examine and right click and choose edit. If we examine one by one, we can find an interesting thing on line 11, ldc.i4.0. If you see the description, it says that
Pushes the integer value of 0 onto the evaluation stack as an int32.
It seems interesting because of the “integer value of 0” and “evaluation stack”. If we learn about comparison (true/false), false is most likely depicted as 0 and the other value than 0 is true. You can change the 0 to 1.
Changing Value
Now we refresh the code,
Refreshing the code, the return false will be changed to true after the click
Okay, interesting, now we want to see is the program behavior is really modified or not. We save this application as bypassPatched.exe.
Saving the Patched Program
Now we can open the program and try to input anything to the prompt.
Patched Program (1) Trial
… WOOPS! We do not know the correct authentication and yet we can bypass the authentication. Now we are facing the second problem, the secret key.
Examining method 2
If we back to our method 0, after we bypass the first authentication, we call method 2. Let’s take a look at method 2.
In this method, there is a string loaded into a kind of “invisible” variable (it happens because it is a decompiled program). The next code is writing string 5.4 to the prompt is continued by reading user input. We can assume that this is the prompt asking for the secret code.
After it receives the user input, it compares the value with the invisible variable above. We can assume that the invisible string is the secret key we have to match in order to continue. If somehow we guess the secret key, the program will print something and maybe that is the flag and then return. If it fails, the program will print something maybe the error message, and call the method 2 again.
When I looked at this problem, I had 2 ideas but I only tried 1. Those ideas are:
- Modify the invisible variable value to something that is readable hence we can supply the exact value (have not been tried)
- Modify the comparison from equality to inequality hence any input will be evaluated as true as long as it is not the same as the invisible variable value. (It works)
I will tell you my solution number 2 by changing the equality function.
Finding the equality function
We analyze the table below like what we did in the previous method and find equality instruction. We can change it to inequality by editing the instruction.
Changing Equality Instruction to Inequality Instruction
And then update the function.
Display after the function is updated
Now it seems work, let’s save and execute our patched program. Enter random data and it will authenticate you
Patched Program (2) Trial
… ups, what happened? The window is immediately closed. We can assume that it is caused by a return instruction. We need to modify it. We analyze the table below further. Remember. our goal is to hold the window so it will not close immediately. My idea to either remove the return instruction or force the system to call method 2 again (looping).
Examine the table further we can see at line number 22, it looks like a jump instruction to line 30, return. I think it will be interesting to remove it. Do not forget to refresh after modifying instruction.
Yep, the return is gone. So, if we read the code, it will be like, the user gives input, the input is evaluated as true, the system writes the flag to the prompt, write the error message and back calling the asking for user input again, repeating the same flow. We save and execute again.
Final Patched Program.