📜 ⬆️ ⬇️

Hacking Nuclear Crackme



Hi, Habraludi!

The process of solving hacking tasks is especially pleasant, and when there is a solution it is doubly pleasant. Today we decided to disassemble the quacks that we caught at the ZeroNights conference in November, where our team from the cyberbee and IT school HackerU debuted and immediately ran out of ranks took first place in the hardware challenge . The crackme “SHADOW” solution is useful to those who are keen on reverse engineering.
')
For quacks of this level, it is enough to know the assembler and have a basic understanding of the device drivers under Windows.

Fluent analysis


We have the CrackmeZN17.exe file. To begin with, we will conduct its surface inspection in HIEW. This will give us general information about the sample. Opening the file in hiew, we see the standard header of the Windows executable file, which starts with the letters "MZ". You can also see that the file is not packed (due to the presence of a large number of empty spaces) and written in C ++. And if the file is packed, it means that the packer will try to minimize all duplicate bytes. Thus, the packed files have increased entropy.

Now press ALT + F6 and go to row mode. So we see only those bytes that relate to printable characters. But our task is not to look at all the lines, but to look at them and find some kind of clue. This may be obvious, but you can get a bunch of useful information just by looking at the lines: starting with the author of the program and ending with recognizing the file as malicious and making an exact verdict (torjan-psw, trojan-ransom, etc.)! Scroll through the file in HIEW just below - and immediately see something interesting - the lines

«error: Can not extract driver files!», «error: Can not extract driver files! Password: Serial is Valid!»  «error: Can not extract driver files! Password: Serial is Valid! Serial is not Valid»: 



Further more: by the offsets 0x5B8D and 0x63E4 we see the headers of two more executable files:





This can be seen from the same letters "MZ".

If you scroll to the end, it turns out that the program requires administrator privileges at startup, since it contains a manifest:



This can finish the visual analysis. We could already understand the following:

· CrackmeZN17.exe executable file is not packed;
· It is written in C ++;
· When starting the file, it will require administrative rights;
· And it contains two more executable files.

Easy analysis


Well, run this crackme and try to play with it:



After running crackme, two more files appeared in its directory: CrackmeZN17.sys and CrackmeZN17_.sys. Now it becomes clear why we need administrator rights: they are needed to load the driver, which otherwise simply will not load, and why we in HIEW saw the headers of two more executable files starting with “MZ” bytes. These were the very drivers that were extracted when you started crackme.

Well, ok, that's all clear. Let's continue to find the place where the serial is tested. Open CrackmeZN17.exe in IDA. We press Shift + F12 and go to the line view mode. Yes, yes, this has already been done in HIEW, but there we performed only a cursory analysis, and for a deeper IDA it would be more suitable. And here we see the lines already familiar to us:



Now it would be nice to determine in which function the strings are used. This will give us a function where the input validation is implemented. To do this, go through the cross-reference line "Serial is Valid" (press "Ctrl + X") and understand that there is no serialization check logic in the CrackmeZN17.exe file! Why is that?



And all because the pair is considered valid only when the WinApi function returns True to us. Now what? We dig further. We see that an IRP request is sent with IOCTL code 22200Ch.

Using the DeviceIoControl function, you can ensure that the I / O manager will generate and fill the IRP packet with the data we need and send it to the device. A device is usually created by the driver itself when it is loaded in the DriverEntry function. And so that it could be treated as a regular file (for example, read and write), a symbolic link to this device is created. A symbolic link is usually also created when the driver is loaded in the same DriverEntry function. In fact, there is quite a lot of theory, and for solving this task a basic understanding of the principles of operation of kernel mode drivers is necessary. In this analysis it will not be possible to cover this in more detail, let us leave as a topic for a separate discussion.

As a result, the logic turns out to be the following: crackme drops two drivers to the disk and then loads them. One of the drivers creates a device, which then accepts the entered login-password pair. The last device receives from the IRP packet, which is generated by the I / O manager at the request of the DeviceIoControl function. Next, the IRP request is processed by the dispatching function, which is set in DeviceIoControl. This function will catch IRP packets sent to the device and process only those that have its IOCTL codes. Something that resembles the procedure for processing window messages.

In our case, the interesting IOCTL code will be - 0x22200C. If the I / O request completes successfully, DeviceIoControl will return True to us. Therefore, to solve the crackme, we need to find the dispatch function.

Now we need to understand to which device the input pair is being sent. Let's put a breakpoint on the call to the CreateFileA function at 0x402591 and see which device the IRP packet is scheduled to be sent to. After stopping, we see in the esi-register a pointer to the following line: "\\. \ CrackmeZN17". And this line is just a symbolic link to a device that serves one of our two drivers. Which one — CrackmeZN17.sys or CrackmeZN17_.sys — can be understood by quickly looking at these files in HIEW. To begin, open CrackmeZN17.sys. Go to the line view mode - ALT + F6 and see this:



Therefore, the CrackmeZN17.sys driver is responsible for servicing the CrackmeZN17 device. An IRP packet is sent to him. Therefore, the next step will be the reverse of this particular driver.

Reverse CrackmeZN17.sys


Open the file in IDA. We find in it a dispatch function. We have this sub_104F8. This feature is very simple:



Consider the function that is executed in case sub_10F60 returns 0.



Now let's look at the function that is called otherwise:



Now everything is more or less clear: the sub_10F60 function can be renamed to check. In the case of the correct input, it should return 1. Now you need to figure out exactly which parameters are passed to this function. For this we need a detailed description of the structure of the IRP package. But first it is necessary to determine the type of I / O method - the necessary displacements within the structure will depend on this. You can determine the input-output method using the IOCTL code (have you already guessed that it was possible to determine the type of input-output using a user-friendly application?). We used the decoder plugin for this, which can be found here . Here's what happened:



It remains only to compare the displacements within the IRP structure. A detailed description of the structure can be obtained using the WinDbg kernel debugger. In this function, first of all, the pointer to the _IO_STACK_LOCATION structure is extracted from the IRP packet. It is needed to read the IOCTL code. If it is 22200Ch, then the package is ours and it can be processed. If the packet is ours, then from it one should receive data that is transmitted to us from the user mode. Taking into account that the transfer method is METHOD_BUFFERED, the data can be transferred to us both in the input and in the output buffers. When writing, the I / O manager allocates a chunk of memory in the non-swappable system pool, and then copies user data there. The address of the allocated memory is stored in the SystemBuffer field. Thus, taking into account the order in which the login and password were transferred to the DeviceIoControl function in CrackmeZN17.exe, it turns out this:



The case remains for the small - to uncause the check function (sub_10F60). Of interest to us is the sub_10EE2 function, the subfunction of which looks like this:



Immediately we can assume that the sub_10EE2 function most likely calculates the MD5 hash. This is evident by the constants. Looking ahead, I will say that it will be so. So let's rename it to “GetMd5”. After hash calculation, the resulting value is transferred to sub_10EA2. The function looks like this:



At first glance, it is not clear what is happening, but in fact, everything is simple. For all characters except “'.', '@'” A logical OR is applied with 0x20. This is how a quick translation of the Latin letter into lower case is realized. Like this:



Accordingly, the opposite operation is logical AND with 0x5F.



That is, the sub_10EA2 function lowers the register of Latin letters, so we rename it to “toLow”. But this method will not work for Cyrillic letters. Why there is no check for the input language, it will become clear further. As a result, the check function becomes similar to something below:



After the toLow function has been executed, if the first character in the hash is a letter, then it is translated to upper case. From the result obtained, the MD5 hash is again considered, and a pointer to the result is placed in array P. The number of elements in array P is 32 (this can be seen from the end of cycle condition — 31 lines). After that, MD5 in the last iteration is compared with the entered data. If they matched, then - voila! - A couple of login-password - valid!

So, let's summarize the serial generation algorithm:

1) we consider the MD5 hash from the login and translate it into a character form;
2) all large letters in a hash become small;
3) if the hash begins with a letter, then it becomes large;
4) MD5 hash from the received string is considered 32 times. Last time will give us the correct password.

Reverse CrackmeZN17_.sys driver


But do not rush to rejoice! If you implement this algorithm and send a valid pair with a login and password, you will get the answer that the serial number is incorrect. Why is that? The thing is, we completely forgot that we have two drivers. Why, then, use the second? Let's open it in IDA and see what it does.



Important: here the driver does not create a symbolic link to your device. And judging by the call to the IoAttachDeviceToDeviceStack function, we can safely say that our driver is a driver filter!



This driver will first receive all IRP packets sent to the CrackmeZN17 device. Therefore, it is possible that along the way it will modify them. We are interested in the function dispatching requests - sub_10462. Open it and see an interesting picture:



If someone initiated the transfer of the IRP packet with the IOCTL code 22200Ch to the CrackmeZN17 device, then we will catch it here. The received data is received from the packet and fed to the function sub_105B2. And this function is just engaged in checking the valid input. Let's take a look at this sub-function and immediately verify this:



If the string with the login or password contains any other characters, then sub_10438 is called, which will complete the processing of the IRP packet with an error - STATUS_INVALID_PARAMETER.



Thus, the driver filter only passes those IRP packets that contain valid data. That is why in the previous driver there were no checks, for example, on the language of the alphabet. If all the conditions are met, the key function sub_105F8 is called for the login, and sub_10640 is called for the password. We already saw the sub_10640 function in the previous driver. Call it also “toLow”.

Consider while sub_105F8.



If you look closely, it becomes clear that this function raises the character to uppercase, if the number of the letter in the string is odd, and to lowercase, if the number of the letter is even.



Only after this, the modified IRP packet is transferred for further service to the next device by calling IoCallDriver. Given this, you can write keyGen and completely solve this quack. In our case, the keygen will be like this:

 import sys import hashlib def is_hex_number(str): try:   arr1 = int("".join(str), 16)   return True except ValueError:   return False def getLogin(login): result = "" j = 0 for i in login:   if j & 1 == 0:       result = result + i.lower()   else:       result = result + i.upper()   j = j+1 return result  def getPass(login): m = hashlib.md5() m.update(login) tmp = m.hexdigest() login = tmp result = "" i = 0 while i < 32: login = login.lower()  if ord(login[i]) <= ord('z') and ord(login[i]) >= ord('a'):             login = login[:i] + chr(ord(login[i]) & 0xDF) + login[i+1:]   m = hashlib.md5()   m.update(login)  tmp = m.hexdigest()   #print tmp   result = result + tmp[i]   i = i+1 return result def keyGen(argv): email = argv[1] #filter changed login = getLogin(email) flag = getPass(login) return flag def main(argv): try: print keyGen(argv) except:  print('Usage: keygen <login>') if __name__ == "__main__": main(sys.argv) 





Mishn Accomplishd! It took us 3 hours to solve this problem, but this is not accurate.

In addition to the test of their forces in the reverse, there are many other interesting challengers. For example, search and exploitation of vulnerabilities in web applications. The crackme solution allows you to upgrade the skills of the reverse, without which no virus analyst or security recruiter can do. Also, cracks give an understanding of the device of the program or operating system under study at the lowest level, and this is often necessary in system programming.



Well, we quickly dealt with this quack, but, I confess, before that we had a lot of training. Cracks - this is a good simulator for pentester, so future Uyhethetam have to solve dozens of problems in order to get a specialization. We are just recruiting for a full-time 9-month course of our Moscow school HackerU “ Professional Pentester ”. The best students of the introductory course will be able to continue their studies, get the profession of pentester, and then engage in quacks not only for fun, but also for earnings.

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


All Articles