📜 ⬆️ ⬇️

CTFzone write-ups - Shall I reverse it?

image


Friends, the stormy weekend has passed, and we are ready to present you with a new batch of WTAs - this time we will analyze the tasks of the Reverse branch in detail. We hope you have already dealt with two tasks from OSINT and are ready to fully immerse yourself in the reverse engineering process. We promise, it will be interesting;)


This direction was very popular among the participants - 103 people alone decided on 100 tasks. However, the task for 1000 remained unresolved. Therefore, as in the case of OSINT, the kit for the most difficult task CTFzone will be published later in a separate post. Now drop all your business, and full speed ahead!



Reverse_50. Console version 1.337


AURORA: SCI430422 LED lights are blinking in hypnotic patterns. As you know, this system is renowned for its top-notch security measures. Only the most expert or resourceful hackers are available.


Decision:


In this task, we needed to get into the console system. To begin, run the file for execution and see a window with a greeting and an invitation to enter a password:


image


What to do? Open the file in the OllyDbg debugger and find the line “Please enter password:” - in the main window, scroll up to the address 004010F9:


image


As you can see, the entered password is checked in the function located at 00401000. Let's try to set a breakpoint at this address (F2 key in OllyDbg), then press F9 (continue execution) and for example, enter some line in the program window:


image


Press "Enter", and the breakpoint is triggered. Next, go to OllyDbg and press the F7 key to go inside the function at 00401000:


image


It can be immediately noticed that at the address 004010CF, the function strcmp () is called from the standard C library. This function compares two strings and returns 0 if the strings are the same. We set a breakpoint on the function call (F2 key), press F9 (continue execution) and see which other string, besides the “password123” entered by us, will be passed to it:


image


In the stack window (and in the register window) we see that the string s1 is equal to "ctfzone {l33t_haxx0r_is_you !! 1}" (without quotes).


image


Here is the flag!


Answer: ctfzone {l33t_haxx0r_is_you !! 1}


Reverse_100. The doors of dorun


* AURORA: Lieutenant, your co-pilot was abducted by prison. They are out for free! Doors couldn’t be seen.

The inscription on the archivolt read:


"The Doors of Dorun, Lord of Omega. Speak, friend, and enter. I, Norvy, made them. Calabrimbor of Alpha Centauri drew these signs."


But be careful and hurry up. They can be back any moment. *


image


Decision:


In this task, we had to pick up the password to the gate, behind which our co-pilot was held captive. First of all, we launch CrackMe, and the following window appears on the screen:


image


Let's try to enter any word and click "Try", but the password does not fit, and we see the following message:


image


CrackMe itself is a 64-bit executable file of PE format. Open it in IdaPro and try to find the line “the door is still closed!”:


image


There is only one cross reference to this line:


image


Here is the function in which IdaPro found references to this line:


image


Here we also see the encrypted flag (it is easy to make sure that the sub_140001160 function is engaged in decryption) and the function determining the correctness of the password: "sub_1400012C0". Using GetDlgItemTextW, the string entered in the password field is passed to this function. Let's analyze this function:


image


There is a cycle and two arrays of five elements. It also checks the length of the entered password:


image


After analyzing this function, we see that the four-character password in UTF-16 encoding (encoding for WideChar in Windows) consists of two numbers with the size of DWORD. Further, we see that the residuals from dividing these numbers by the numbers ((1 << (1 << i)) + 1) are compared with hard-hitting values.


It can be noted that ((1 << (1 << i)) + 1) = 2 ^ (2 ^ i) + 1 , and that these are Fermat numbers: 3, 5, 17, 257, 65537. The password check algorithm is further can be reduced to two systems of comparisons:


X1 % 3 = 0 X1 % 5 = 0 X1 % 17 = 1 X1 % 257 = 241 X1 % 65537 = 995 

 X2 % 3 = 1 X2 % 5 = 4 X2 % 17 = 6 X2 % 257 = 104 X2 % 65537 = 413 

The Chinese Residue Theorem will help us restore the original numbers. Solvers of such comparisons can be found on the Internet:


image


image


So we got two numbers that now need to be converted to the string UTF16. To do this, you can use Python (while not forgetting the reverse order of bytes):


image


Now it remains to verify the result. Enter this line in the password entry box.


image


image


That's all! We opened the gate.


Answer: ctfzone {ch1n4_t0wn}


Reverse_300. Python's urn


AURORA: Lieutenant, the Doors But we don’t get it.


Decision:


After we opened the gate, it turned out that we face the next door. A new task - a new key :) Let's go!


Run the file for execution, enter the key, but nothing comes out.


image


Let's try to figure it out. First of all, we should find out what we are dealing with. To determine the type of executable file, you can use the CFF explorer:


image


Obviously, .Net Assembly, Ida and traditional debuggers are powerless in this case. At this point, you could recall dnSpy - one of the tools that can help in the study of code that uses .Net. We load into it an executable file.


image


By the name of the main class (PythonMain) and the name of the resources, it can be concluded that this program was written in IronPython. In the main function, only the .NET assembly from the IPDll.WFCrackMe resource occurs. You will have to extract a resource with this name and load it into dnSpy.


image


Everything is much more interesting here, there are even function names. Immediately suppose that a function with the name “verifyPassword” checks the password we entered, and to verify this, it is enough to test in the debugger which string will be passed to it as an argument.
Next, we need to understand how the password is verified. The biggest problem when analyzing code is to deal with the functioning of “strongBox” and “globalArrayFromContext”. These variables are actually filled in the " __main__ " function.


Consider the reversePassword reversal process using the example of a small piece of code with branching:


 if ((arg = (CallSite<Func<CallSite, object, int, bool>>)strongBox.Value[37]).Target(arg, (arg2 = (CallSite<Func<CallSite, CodeContext, object, object, object>>)strongBox.Value[38]).Target(arg2, globalContext, globalArrayFromContext[26].get_CurrentValue(), password), 81)) { num = 73; num = 73; result = globalArrayFromContext[16].get_CurrentValue(); } 

First of all, we need to substitute the values ​​in the places “strongBox” and “globalArrayFromContext”. Values ​​are taken from the following lines " __main__ ":


 strongBox.Value[38] = CallSite<Func<CallSite, CodeContext, object, object, object>>.Create(PythonOps.MakeInvokeAction($globalContext, new CallSignature(1))); strongBox.Value[37] = CallSite<Func<CallSite, object, int, bool>>.Create(PythonOps.MakeComboAction($globalContext, PythonOps.MakeBinaryOperationAction($globalContext, ExpressionType.NotEqual), PythonOps.MakeConversionAction($globalContext, typeof(bool), 1))); 

If there is no confidence in the correctness of the value obtained for “strongBox”, it can be checked in the debugger. Substitute the values ​​into the code section under study and simplify it:


 if (NotEqual(len(password), 81)) { result = false; }  if (len(password) != 81) result = false; 

Thus, we found that the password length should be equal to 81 characters. Further analysis of the function is difficult, since the function is quite large, but, due to superficial analysis and debugging, we can draw several conclusions:


  1. With further analysis, we immediately notice the table 9x9 of 81 numbers.
  2. When you try to enter as a password a string of 81 characters that contains not only numbers, we will get to handle the exception: “IronPython.Runtime.Exceptions.ValueErrorException: Invalid integer literal”. Therefore, it is necessary to enter only numbers.
  3. Also, one of the branches will indicate to us that all digits, except 0, must coincide with the digits from the array.
  4. Zero cells of the table are filled with the corresponding cells in the entered line.

Then we get into branching with a terrible, at first glance, condition:


 if ((arg49 = (CallSite<Func<CallSite, object, bool>>)strongBox.Value[59]).Target(arg49, (!(arg50 = (CallSite<Func<CallSite, object, bool>>)strongBox.Value[60]).Target(arg50, obj9 = (arg51 = (CallSite<Func<CallSite, CodeContext, object, object, object>>)strongBox.Value[61]).Target(arg51, globalContext, globalArrayFromContext[11].get_CurrentValue(), obj))) ? obj9 : ((!(arg52 = (CallSite<Func<CallSite, object, bool>>)strongBox.Value[62]).Target(arg52, obj10 = (arg53 = (CallSite<Func<CallSite, CodeContext, object, object, object>>)strongBox.Value[63]).Target(arg53, globalContext, globalArrayFromContext[12].get_CurrentValue(), obj))) ? obj10 : (arg54 = (CallSite<Func<CallSite, CodeContext, object, object, object>>)strongBox.Value[64]).Target(arg54, globalContext, globalArrayFromContext[13].get_CurrentValue(), obj)))) 

After substituting the corresponding values ​​and simplifying this condition turns into a simple test:


 if (BoolConvert((!BoolConvert(obj9 = CheckLines(obj))) ? obj9 : ((!BoolConvert(obj10 = checkColumns(obj))) ? obj10 : CheckSquares(obj))))    BoolConvert    «a?b:c».   : if(CheckLines(obj) && heckColumns(obj) && CheckSquares(obj)) 

We need to get to the true branch of this condition, i.e. all functions should return “True”.
Now let's analyze the CheckLines function. It is quite simple - the function checks that each row contains numbers from 1 to 9. The CheckColumns and CheckSquares functions are more complicated, but at this stage you can already guess that we are dealing with Sudoku:


image


On the Internet, you can find a lot of solvers, so you can practice on your own;)


So, using the solver, we get the desired key.


image


The door is open!


Answer: ctfzone {1_v3ry_l1k3_5ud0ku_9arm3!}




Reverse_500. Bridge repair


AURORA: Lieutenant, watch your step! There is a pit infested with worms down the road. It was destroyed. I have to go and repair the bridge. Hurry up, we have to save our pilot!


Decision:


So, in this task you need to save the pilot, but you can only reach it by overcoming the destroyed bridge. We need to restore it. The “Bridge.txt” file is proposed as a “bridge”.


By opening the "Bridge.txt" file in a hex editor, we can make sure that it is encrypted. From the job description, it is clear that we were provided with a program that encrypted this file, and we need to decrypt it.


Next, check what is wrong with the file "reverse500.exe". Run the file, and in response to the console a message appears - "you must specify the file for encryption":


image


With the help of cross references, look for the use of this line in the executable file. To do this, open the file in the IdaPro compiler. Obviously, the file that is passed as a command line argument is encrypted.


image


From the above code snippet, you can see that function 401F60 performs encryption.
Having examined this function in a superficial way, we can highlight the functions of allocating memory, reading a file, encrypting the contents of a file, and writing encrypted content to the source file:


image


Next, we will try to understand the structure of the encrypted file by examining the simple “WriteCryptedData” function:


image


Of course, you can determine the type of hash used, but this is optional. However, for further work, we need to understand how the hash sums are calculated. Looking at the “HashCalculate” function in detail, it becomes obvious that when calculating the hash, 8 bytes of the “IV” vector are used.
So, we found out that the encrypted file has the following structure:


image


Let's look at the “Bridge.txt” file:


image


Next, we define the last byte of the vector "IV". To do this, open the program in the debugger, specifying any file as an argument, and put a breakpoint at the address of the WriteCryptedData function. After that, having previously corrected the assembler code a little, let's organize the search of the last byte of the vector “IV”. The result of the search is shown in the screenshot:


image


Now we have the complete vector “IV”: [47 08 8F E7 C4 C0 E9 AB] .


Next, you need to check the encryption mode and the symmetry of the cipher used.
Let's go back to the “CryptData” function in the debugger by submitting a test file to the program (in my case the file contained the string “HelloWorld!”). Next, we write the “IV” vector (located in EAX before calling CryptData) and the encryption result (located in EAX after calling CryptData).


image


Verify the identity of the decryption and encryption procedure. To do this, restart the program under the debugger and re-set the breakpoint on “CryptData”. Only now, before its execution, we fix the vector “IV” and the encrypted buffer.


image


As a result of re-encryption of encrypted data, we get the source text!


Now you can decrypt the file from the job. To do this, remove the first 15 bytes from it and run the program under the debugger, specifying the file with encrypted data as a parameter. Again, we set the breakpoint on “CryptData” and, before calling the function, correct the “IV” vector to [47 08 8F E7 C4 C0 E9 AB]. After correcting the “IV” vector, press the F9 key, as a result of which the program will encrypt the data. Delete the first 15 bytes from the resulting file and open it in a text editor:


image


Flag found!


Answer: ctfzone {3RR4dIC473_7HIS_WORM!}


PS For those who are interested, we used in this task the Salsa encryption algorithm and the CubeHash hashing algorithm. But it can be noticed only by an experienced eye;)


It seems now everything fell into place. If you have any questions or requests - write to our chat in the telegraph and leave comments. And new knowledge in the field of reverse engineering can be demonstrated at this link - tasks will be available until December 15.


Good luck to everyone and see you soon!


')

Source: https://habr.com/ru/post/316420/


All Articles