Friday morning, UTC+1:00. Rumor has it that employees from a big Telecom company in Spain are heading back home. Company loudspeaker recordings and internal emails start being disclosed on the Internet where employees are being told to shut down all the computers immediately.
Internal email that told employees to shut down all the computers immediately
A while after, other big companies in Spain began experiencing a similar situation which did not take long to extend to the rest of Europe, followed by Asia and the USA as their respective business hours began for the day. What seemed to be a targeted attack to specific large Spanish companies soon turned out to be a major worldwide cyberattack.
WannaCry, the latest ransomware threat, was causing mayhem by spreading like a plague through Windows systems all over the globe and encrypting important files in its way. At AttackIQ we soon realized that this was not another typical ransomware threat and spent some time analyzing it - here are the results.
The sample analysis was done using radare2, which is a well known open source reverse engineering framework. First steps involved looking at some specific data of the binary such as strings to see relevant information (e.g. urls, mutexes…), imports to know what API functions are being used by the malware, exports to check if the program can provide functionalities to other software, and resources to check if the binary has more information stored inside itself (images, executables, dlls…).
This information allows us to make an initial guess of the capabilities of the malware. In this case it revealed the existence of URLs and resources within the executable file, both of which will be further explained in this post.
Starting with the detailed analysis, a quick look at the main function revealed the existence of a URL (http://www.iuqerfsodp9ifjaposdfjhgosurijfaewrwergwea[.]com) that was being passed to the InternetOpenUrlA call in order to connect to it. If the sample was able to successfully connect to the URL it would do some cleanup tasks and finish the execution. Otherwise, it would continue executing.
Given the described behavior, it was not hard to see that the URL acts as a kill switch. That is, if the URL was reachable by the malware, it would close the opened handles with InternetCloseHandle (call esi) and finish its execution without performing any other malicious activities.
One of the many interesting features of radare2 is a view for displaying only interesting instructions such as calls and strings among others, so applying that view to the previous function we will obtain the following reduced basic block graph that allows to quickly see the described behavior:
If the URL was not reachable, the execution flow would reach another function, which we called InstallAsService. The function first obtained its fully qualified path and checked if arguments were provided when it was executed. If no arguments were provided the malware would reach the CreateAndInstallAsServiceAndExtractResource function and exit. When one or more arguments were provided it would change the service configuration with the call to ChangeServiceConfig and set the main function of the service with the call to StartServiceCtrlDispatcher.
The function CreateAndInstallAsServiceAndExtractResource called the CreateAndStartService function followed by the ExtractResource function:
The former function created a new service named “mssecsvc2.0” with the description “Microsoft Security Center (2.0) Service”. After that, it configured the service to start on boot.
The latter function was a little bit more complex as it performed several tasks. First, it used a common trick to use internal Windows functions without being explicitly imported. This trick relies on the use of the calls to GetModuleHandle and GetProcAddress to obtain a pointer to the desired Windows API call. In this case, the sample used the trick to prepare calls to CreateProcessA, CreateFileA, WriteFile and CloseHandle, which are commonly used to create and write to files, and to create a new processes.
However, before using these previously loaded functions, the calls to FindResourceA, LoadResource, LockResource and SizeofResource were used to extract content from the Resource section. After extracting this information and storing it on memory, the program continued with the execution of MoveFileExA to backup the file C:\WINDOWS\tasksche.exe into C:\WINDOWS\qeriuwjhrf.
After that, the file C:\WINDOWS\tasksche.exe was created / overwritten with the content of the previously extracted resource, that would be written by using the functions loaded with the trick. Then the function executed C:\WINDOWS\tasksche.exe /i through CreateProcessA (0x431478), and ended.
Going back to InstallService function, we checked with more detail what happens when the binary is executed with one or more arguments. As we already described, the malware would obtain the handle to the previously created service, the “mssecsvc2.0”, change its configuration by calling ChangeServiceConfig2, and later specify the pointer to the main function of the service by calling CreateServiceCtrlDispatcher.
The function StartServiceCtrlDispatcher receives a pointer to an array of SERVICE_TABLE_ENTRY structures. This structure has two variables, one that contains the service name, and the other that contains the pointer to the main function for the service.
Taking a look at address 0x0040810e, we could see a mov dword [local_ch], 0x408000. This address specified as an operand, was the address of the main function. Since radare2 did not analyze that function, as it can be considered an indirect call, we could explicitly tell to analyze it by using the af command: af @ 0x408000, and we named it ServiceMain.
Diving into the ServiceMain function, we quickly observed that it would modify the status of the service with SetServiceStatus, start a thread, and sleep for 0x5265c00 milliseconds (i.e. 24 hours) and call ExitProcess. The code of the created thread was located at address 0x407720, as it could be observed in the push instructions previous to the call to _beginthreadex.
All the behavior of the malware described until now belongs to the dropper of the actual ransomware. By now, it is widely known that the WannaCry malware spreads itself by exploiting an SMB vulnerability with the EternalBlue (MS17-010) exploit. Without analysing further, we strongly believe that the spreading functionality is implemented within the created thread, and that the actual ransomware will be the executable that the dropper extracted from its resource section.
The analysis of the spreading code and the actual ransomware binary is outside the scope of this article, in which we wanted to give a quick dive into the piece of malware that caused so many headaches for important companies around the globe, by using free software tools.
Wrapping up, the execution flow described in the analysis can be represented as follows:
The parts that are represented with a cloud are the areas that were not covered in this analysis. The spreader will be executed with the installed service and the ransomware is extracted from the resource section of the binary and later executed.
Finally, we would like to provide a small python script that leverages the power of radare2’s r2pipe that will extract the identified IOCs during this short analysis so you can retrieve all these IOCs on your own. Obviously, these are just a subset of all the IOCs that a full analysis would provide. The script works for either version 1 and 2 of the malware.
Also, just in case the reader would like to take a look at the sample, here are the hashes (SHA256) of v1 and v2:
#!/usr/bin/env python import r2pipe import sys banner = """ _ _ _ _____ ____ /\\ | | | | | | |_ _/ __ \\ / \\ | |_| |_ __ _ ___| | __ | || | | | / /\\ \\| __| __/ _` |/ __| |/ / | || | | | / ____ \\ |_| || (_| | (__| < _| || |__| | /_/ \\_\\__|\\__\\__,_|\\___|_|\\_\\_____\\___\\_\\ WannaCry IOCs extractor - Powered by r2 (http://radare.org)\n """ if len(sys.argv) != 2: print "usage: %s <wannacry_sample>" sys.exit(1) r2 = r2pipe.open(sys.argv) killswitch_domain = r2.cmd("ps @ 0x004313d0") service_description = r2.cmd("ps @ 0x431308") service_name = r2.cmd("ps @ 0x4312fc") service_program_fmtstr = r2.cmd("ps @ 0x431330") service_start_type = r2.cmdj("pdj 1 @ 0x00407c87")["val"] filename_tasksche = r2.cmd("ps @ 0x43136c") filename_windir = r2.cmd("ps @ 0x431364") path_tasksche_fmtstr = r2.cmd("ps @ 0x431358") tasksche_fullpath = path_tasksche_fmtstr % (filename_windir, filename_tasksche) filename_qeri_fmtstr = r2.cmd("ps @ 0x431344") qeri_fullpath = filename_qeri_fmtstr % filename_windir original_filename = r2.cmd("psw @ 0x0038c344") print banner print "Hostname: %s" % killswitch_domain print "ServiceName: %s" % service_name print "ServiceDescription: %s" % service_description print "ServiceStartType: %s" % service_start_type print "FilePath: %s" % tasksche_fullpath print "FilePath: %s" % qeri_fullpath print "File: %s" % original_filename
ServiceDescription: Microsoft Security Center (2.0) Service
ServiceDescription: Microsoft Security Center (2.0) Service