17 minute read

WannaCry Malware Analysis

WannaCry is a ransomware that encrypts a victim’s machine, specifically targeting the Windows Operating System. The coordinated attack in May 2017 targeted vulnerable Windows around the world and encrypted thousands of workstations. The only solution to decrypt the files, which was not guaranteed, was to pay using Bitcoin due to its nature of anonymity. The malware specifically targeted unsupported versions of Windows XP, Windows Server 2003, and Windows 7. The exploit of EternalBlue, the backbone vulnerability of the WannaCry malware, was discovered by the NSA. It was privately disclosed but was leaked to the ShadowBrokers, an underground marketplace for vulnerabilities and exploits. It is hard to trace a single block to a user unless the user discloses it using their E-Wallet. While ransomware combined with Bitcoin created a challenge for tracing the attack, the preliminary testing showed that the attack originated from North Korea. The additional investigation revealed that the attack is highly suspected to be the infamous Lazarus group from North Korea. WannaCry consists of two parts: a ransomware portion and worm with a kill switch. In this study, we solely focus on the ransomware portion using the poweful tool IDAPro.

We begin the investigation using static analysis.

STATIC ANALYSIS

Imports

The malware uses about 64 imported functions. Listed below are the names of the functions and by referencing the MSDN, their usage:

Address Ordinal Name Library  
00408000 CreateServiceA ADVAPI32  
* 1) Creates a service and passes it to a service control manager.      
00408004 OpenServiceA ADVAPI32  
* 2) Open the service and returns the handle if it succeeds.      
00408008 StartServiceA ADVAPI32  
* 3) Starts a given service with the following nonzero return if succeded.      
0040800C CloseServiceHandle ADVAPI32  
* 4) Returns the handle to the service control manager. The handle returned after the open service was requested (service opened must be closed).      
00408010 CryptReleaseContext ADVAPI32  
* 5) Function releases the handler to the CSP (Cryptographic service provide) and a key container to store a key.      
00408014 RegCreateKeyW ADVAPI32  
* 6) Creates the specified registry key. However, if the key already exists in the registry it opens it.      
00408018 RegSetValueExA ADVAPI32  
* 7) Sets the data and type of the created open registry key.      
0040801C RegQueryValueExA ADVAPI32  
* 8) Retrieves the type and data of the specified open registry key.      
00408020 RegCloseKey ADVAPI32  
* 9) Closes the handle to the registry key.      
00408024 OpenSCManagerA ADVAPI32  
* 10) Establishes a connection to the service control manager and open the database of the specified target machine.      
0040802C GetFileAttributesW KERNEL32  
* 11) Retrieves the file system attributes of a file/directory.      
00408030 GetFileSizeEx KERNEL32  
* 12) Retrieves the size of the files in bytes.      
00408034 CreateFileA KERNEL32  
* 13) Creates/Open the file/IO device. The file lpFileName should be given to specify the file being opened or created.      
00408038 InitializeCriticalSection KERNEL32  
* 14) Initialize a critical section of an object. Critical section allowed for mutually exclusive access to shared resources. Multiple threads are synchronized when accessing shared resources.      
0040803C DeleteCriticalSection KERNEL32  
* 15) Release all resources used by an unowned critical object.      
00408040 ReadFile KERNEL32  
* 16) Read data from a specified file or IO device. Lbuffer must be set to create a buffer where the data being read will be stored.      
00408044 GetFileSize KERNEL32  
* 17) Retrieves the file size in bytes. Usually, the GetFileSizeEx is recommended instead of this API.      
00408048 WriteFile KERNEL32    
* 18) Writes data to a specified file.      
0040804C LeaveCriticalSection KERNEL32  
* 19) Release the ownership of the critical section object.      
00408050 EnterCriticalSection KERNEL32  
* 20) Waits for the ownership of a critical section object and once the thread gains ownership, the function returns.      
00408054 SetFileAttributesW KERNEL32  
* 21) Set the attributes of a file/directory.      
00408058 SetCurrentDirectoryW KERNEL32  
* 22) Change the current directory based on the parameters of the lpPathName.      
0040805C CreateDirectoryW KERNEL32  
* 23) Creates a new Directory.      
00408060 GetTempPathW KERNEL32  
* 24) Retrieves the path of the directory designated for temporary files.      
00408064 GetWindowsDirectoryW KERNEL32  
* 25) Retrieves the path of the Windows directory excluding the last backslash.      
00408068 GetFileAttributesA KERNEL32  
* 26) Retrieves file system attribute of a file or a directory. Unlike GetFileAttributeW, GetFleAttributeA handles only strings. GetFileAttributeW allows for handling Unicode.      
0040806C SizeofResource KERNEL32  
* 27) Retrieves the size of a specific resource in bytes.      
00408070 LockResource KERNEL32  
* 28) Retrieves a pointer to the specified resource in memory.      
00408074 LoadResource KERNEL32  
* 29) Retrieves a handle that can be used to obtain the first byte of the specified resource. This is used before LockResource, which requires a parameter of the handle.      
00408078 MultiByteToWideChar KERNEL32  
* 30) Maps a string to a UTF-16 string (Usually requires two calls to use) {: style=”background: grey”}      
0040807C Sleep KERNEL32  
* 31) Suspends the execution of the thread until the timeout interval ends (given the parameter of milliseconds).      
00408080 OpenA KERNEL32  
* 32) Creates/Opens a named or unnamed mutex object.      
00408084 GetFullPathNameA KERNEL32  
* 33) Retrieves the full path and file name of a specific file.      
00408088 CopyFileA KERNEL32  
* 34) Copies an existing file to a new file.      
0040808C GetModuleFileNameA KERNEL32  
* 35) Retrieves the full qualified path of a file that has a specified module.      
00408090 VirtualAlloc KERNEL32  
* 36) Allocates memory in the address space of the calling process.      
00408094 VirtualFree KERNEL32  
* 37) Release a region of pages within the virtual address space. (VirtualFreeEx frees memory in another      
00408098 FreeLibrary KERNEL32  
* 38) Frees the loaded DLL and decrements the reference count.      
0040809C HeapAlloc KERNEL32  
* 39) Allocates a block of non-movable memory from the heap.      
004080A0 GetProcessHeap KERNEL32  
* 40) Retrieves a handle to default heap to the calling process. A process can allocate memory without needing to create a private heap.      
004080A4 GetModuleHandleA KERNEL32  
* 41) Retrieves a module handle for the specified module that has been loaded by a current process.      
004080A8 SetLastError KERNEL32  
* 42) Sets the last error code for the calling thread. Functions usually call the SetLastError when it fails.      
004080AC VirtualProtect KERNEL32  
* 43) VirtualProtectEx changes the access protection of a process. However, VirtualProtect changes the protection of a page in the virtual address space of a process. If the page is not committed, the function fails and returns a 0.      
004080B0 IsBadReadPtr KERNEL32  
* 44) Verifies that the current process has access to read the specified range of memory.      
004080B4 HeapFree KERNEL32  
* 45) Frees the memory block allocated from heap usdeHeapAlloc.      
004080B8 SystemTimeToFileTime KERNEL32  
* 46) Converts system time to file time. System time is in UTC, file time is a 64-bit value.      
004080BC LocalFileTimeToFileTime KERNEL32  
* 47) Converts a local file time to UTC.      
004080C0 CreateDirectoryA KERNEL32  
* 48) Creates a directory given a parameter of pathname and security attributes.      
004080C4 GetStartupInfoA KERNEL32  
* 49) Retrieve contents of the STARTUPINFO structure that was specified.      
004080C8 SetFilePointer KERNEL32  
* 50) Moves the file pointer of an open file.      
004080CC SetFileTime KERNEL32  
* 51) Set the time and date of a specific file/directory last access, created and modified.      
004080D0 GetComputerNameW KERNEL32  
* 52) Retrieves the NETBIOS name of the local computer that is established during startup.      
004080D4 GetCurrentDirectoryA KERNEL32  
* 53) Retrieves the current directory of the current process.      
004080D8 SetCurrentDirectoryA KERNEL32  
* 54) Changes the current directory for the current process. The pathname must be given as a parameter.      
004080DC GlobalAlloc KERNEL32  
* 55) Allocates a specified number of bytes from the heap. Unlike HeapAlloc, this memory is movable.      
004080E0 LoadLibraryA KERNEL32  
* 56) Loads the specified module in the space of the current process.      
004080E4 GetProcAddress KERNEL32  
* 57) Retrieves the address of an exported function from a specified DLL.      
004080E8 GlobalFree KERNEL32  
* 58) Frees the specified global memory called by GlobalAlloc and nullifies the handles.      
004080EC CreateProcessA KERNEL32  
* 59) Creates a new process and its main thread.      
004080F0 CloseHandle KERNEL32  
* 60) Closes an Object handle. Usually, the close handle is called once a handle is opened.      
004080F4 WaitForSingleObject KERNEL32  
* 61) Wait for an object to be in a signaled state or when the time out interval passes (which is given as milliseconds)      
004080F8 TerminateProcess KERNEL32  
* 62) Terminates the process and its corresponding threads.      
004080FC GetExitCodeProcess KERNEL32  
* 63) Get the termination status of a specified process.      
00408100 FindResourceA KERNEL32  
* 64) Determines the location of resource given the module, name, and type.      
Foot1 Foot2 Foot3  

An image from IDAPro showing an excerpt of the imports: image-center


Packed

It is common for malware to be packed to avoid firewall detection and reverse engineering analysis. Packing is a subset of obfuscation to make the file more difficult to analyze through encryption, compression, or both. A part of the packed program is a stub, a small portion of the code containing the decryption or decompression agent used on the packed file. In most instances, the packaging tool used by the program are commercially or open-sourced available, but custom made packers can be used. To analyze if the WannaCry ransomware is packed, we use Krypt ANAlyzer(KANAL) in PEiD.

image-center

As shown above, the malware is not packed, but it was created using Microsoft Visual C++.

We see quite a bit of cryptosystem being used in the program, see figure below. The first one is Adler32, a checksum to detect errors during transmission. Another checksum program is CRC32, which offers the same functionality as Adler32 but is more popularly used. CryptEncrypt and CryptDecrypt are two functions that work mutually; if the file is encrypted with CryptEncrypt, the decrypt API must be called (CryptDecrypt). Towards the bottom of the list, we can see Rijndael’s cryptosystem, or what is commonly referred to as the AES cryptosystem. AES is a ubiquitous cryptosystem due to its robustness against decryption techniques. Lastly, the Zip is easy to describe as the zip extension.

image-center


From the imports, our guess is that the program is using startservice. Let us jump to sub_401F5D. We see:

image-center

We see a hard-code .exe file name, which could be a duplicate of the ransomware in the system. Let’s jump into sub_401CE8, and look into the funtion calls:

lpDisplayName and lpServiceName are initialized using the ESI

When using any service or API, the required parameters must be initialized or passed. In most instances of regular programs, the service parameters are hardcoded. However, we can see above that both the parameters lpDisplayName and lpServiceName are initialized using the ESI (the ESI register, a pointer type register). Hence, the name is not hardcoded but is passed by a pointer. We can only know the name of the service using the dynamic analysis (running the malware). Even in the OpenService, the service Name is still relatively hidden.

Mutex

A mutex is a unique identifier to a program. A mutex is designed to allow only one program with the given mutex to run its processes and child threads and avoiding race conditions. It’s a locking mechanism to protect resources from being visited twice or in a racing manner. Mutex allows the program to operate normally, and in a malware sense, it avoids competing with a copy of itself for the resources. image-center

image-center

For the malware, we can find its mutex by proceeding into the sub_401EFF function. It is easy to assume that the mutex is Global\MsWinZoneCacheCounterMutexA, but there is a padded 0 at the end. Using sprintf and push offset aSD, the program concatenates the strings Global\xx with the variable 0, as shown above.

DYNAMIC ANALYSIS - Debugger

Unique identifier

Now let us go to WinMain(x,x,x,x), and go into the function call sub_401225. We can see a function call (malware signature) and also some random number generations (e.g. seed). First, we will use an advanced feature in IDAPro called Win32 debugger; Select Debugger->Local Win32 debugger, and then set a breakpoint before call sub_401225. Next, we go into sub_401225 and press F5 to change into pseudocode, which will make it easier for us to analyze. As shown below, we can recognize some if and do while condition, and as well as a srand function. The srand (or the seed generator before rand() is called) is set to 1.

image-center

Set a breakpoint at 0x0040202F, as shown below. If we examine the EAX in the general register, it should have the address: 0x0040F8AC. A unique string of values can be seen in the following address, called rfoufmcqgjq199. The malware generates the naming scheme based on the computers name (8-15 random lowercase letters followed by 3 numbers).
image-center


Further in our investigation, we face a conditional branch here:

image-center

We can suspect that this may be a secret command line start-up. It seems the program is comparing the argument with 2 before the conditional branch is set up. If no, it will take the left branch; if *yes, it will take the right branch. If we run the program to the jnz short loc_40208E, we will get the following result:

image-center

The green arrow points to the location of 0040208E, meaning we did not go into the correct branch of operation.


Let us redo the dynamic analysis, but in this case, we put the proper argument as the parameter. Using Debugger->Process Options, we set up the Parameters to be “/i”, as seen on the push offset al :”/i” assembly code on our conditional branch (two images above).

When we run the malware again with the correct parameters, we can see that we arrive at the right branch. We run the program until the code .text 00402057 call sub_401B5F, and view inside sub_401B5F. We can see some PathName such as C:\ProgramData, etc. We can guess that it creates something inside these folders. Now let us F8 through sub_401B5F, and then go to C:\Intel or ProgramData, if the folder exists, using File Explorer. We will see that the new folder that was created and the folder name should be the unique identifier we found earlier (rfoufmcqgjq199).

Hidden Folder

After running sub_401B5F we can see that there was a folder being created inside c:\ Intel folder. By examining the folder, we can see the executable tasksche.exe inside the folder; presumably, the executable that runs the malware. If the folder inside ProgramData or Intel is hidden, we can change the File Explorer settings to show hidden files and folders.

How is it possible to write hidden files? When creating a file, we usually have to set the file attributes and the file name first. To create a hidden file, we must specify it under the dwFileAttributes parameter. For example, a 0x02 input would set the file to be hidden. However, that is just one way to hide files and folders. The command “attrib +h”, which we will see late will do the same function.

image-center

Service Name

We previously found that lpDisplayName and lpServicename (and at the .data:DisplayName) are pointing to the address of the following string rfoufmcqgjq199. Additionally, the folder name where taskche.exe is stored is called the rfoufmcqgjq199 folder under Intel. To prove our speculation on the service name, we need to examine the running services after running WannaCry. As shown below, we can see the malware service at the list of Services: image-center

STATIC ANALYSIS Part 2

Let us stop the debugging process and continue our static analysis: For this part, we will examine call sub_401F5D. Let us rename it StartWannacry since if we run the function call, the malware will infect our VM completely. Before running the function, make sure to have a clean Snapshot of the VM. Now let us run to cursor at .text:0040207F jz short loc_40208E”, a line before StartWannacry. Then we will see a file called tasksche.exe being dropped inside the folder created previously. The file is just another copy of the WannaCry Ransomware we are currently investigating (used for persistence).

Let’s proceed with our analysis in the static analysis by restoring to the previous clean snapshot. After “SetCurrentDirectoryA”, we see a function call “sub_4010FD”, which has the following pseudocode on the image below. By using the tool RegShot before and after the instruction calls, we will notice that the malware is trying to create a registry entry under HKLM\Software as Wannacry0r. The function call sub_4010FD is designed is to create a service for the persistence of the taskche.exe in the system.

(Left) Pseudocode showing Registry keys being created, (Right) Regshot shows Wannacry0r being added to HKLM\Software

We see can instruction ahead call sub_4010FD with the string WNcry@2ol7, which is passed to the function call sub_401DAB. The function is quite complex, but to my general knowledge, it is loading a Resource module called XIA and decompressing it. It compares if one of the loaded modules is called c.wncry and loads it up in memory.

Let us assume that WNcry@2ol7 is a hard-coded password used to unzip the resources (recall this is ransomware). Let us confirm this by using the tool ResourceHacker to open WannaCry and save it as a Zip file. If we unzip this the resource will be released (even if we receive a warning that says your password is incorrect). By opening the /wcry folder, there should be two .exe files, several .wnry files, and a msg folder. The malware authors may have zipped the resources to avoid firewall detection due to its sheer size.

image-center

The message folder contains the infamous “Warning Message” in different languages, which WannaCry posts detailing that all the files have been encrypted.

(Left) Different languages for the WannaCry ransom, (Right) English warning message

Bitcoins

Let’s move into sub_401E9E. We see a combination of a hard-coded hash string and a rand number 3. These peculiar hashes are the attacker’s Bitcoin address and the program will randomly choose one Bitcoin address among the 3. If we use www.blockchain.com or other blockchain websites to trace the number of transactions and the money received by those Bitcoin addresses, we can observe the balances accumulated by each account. About 54.44 BTC were paid, assuming the malware authors accumulated all the bitcoins and sold them around Dec. 2017 (shows that people paid for the ransom even if it was not guaranteed).

image-center

Balances of each Bitcoin address as of November 2019


Hidden files again

We see a command line right before sub_401064: image-center If we trace back our steps in this task with the XIA resource loading and the current directory being Software\WanaCrypt\wd. We may conclude that “attrib +h ” is a command-line command that hides the files inside this directory. The only way to see hidden files would be to set the current configuration to display hidden files (File Explorer) or under the command prompt, type the command “dir /A”. Finally, the malware grants all users permissions using the command: “icacls ./grant Everyone:F /T /C /Q”. image-center

Next, we click into sub_401064. Now, let us retrace a bit, we have renamed sub_401F5D into StartService, and we identified that the malware wants to start itself as a service. By following the logic flow, we see that sub_401064 is running in parallel with StartService; tasksche.exe is executed as command-line commands much like attribute +h and icalc.After sub_401064, we see another function sub_40170A. Using the cross-reference tool, we can see it calls another function sub_401A45. From the keywords, we know that it focuses on cryptography. Sub_401A45 generates the key that may be needed for encrypting a file. It calls the CryptAPI for Microsoft, which generates a key for encryption and decryption.

This page is part of final for CS495-Reverse Software Engineering , Fall 19’, at Old Dominion University by Dr. Cong Wang.
For more info: Practical Malware Analysis, by Michael Sikorski and Andrew Honig.