My First Malware Analysis: A Nymaim Sample (Part 3 - The Real Deal)

Stage 3 - The Payload

3.1 Anti-Analysis Techniques

Since the launcher employs process hollowing to spawn the payload, one of the earliest hurdles is the issue of attaching a debugger to the spawned child process before it begins execution. This was overcome using a technique called "Jump to Self" described here. Essentially, before the main thread of the child process could execute, I used Process Hacker to change the first 2 bytes at the entry point to EB FE (jmp 0x0).



This would later cause the main thread to loop infinitely at the entry point. Then, the thread is allowed to resume before I finally attach my debugger on it. I then reverted the two bytes at the entry point and dumped the process into an executable file using OllyDumpEx for later analysis.

After plunging into the code of the payload, I also found it to be terribly obfuscated. Therefore, I wrote several scripts using IDAPython to deobfuscate the nasty-looking assembly code. The scripts have been made publicly available at https://github.com/thngkaiyuan/myamyn and I hope that they will benefit other analysts studying Nymaim malwares. Summarily, some of the obfuscation techniques used are:


3.1.1 Hiding a Register Push

Instead of executing "push edi" for instance, the malware first executes "push 0x56" followed by "call reg_push" where reg_push is a function that will replace the argument (e.g. 0x56 with the value of edi)
Calls to the "register push" function

The "register push" function


3.1.2 Hiding Function Calls Using Proxy Functions

Instead of calling a function directly, the malware would, for example, push two values on the stack and call a proxy function which does some arithmetic before returning to a different address (the intended function)
Calls to the proxy function
One of the proxy functions


3.1.3 Storing Strings in Encrypted Hash Form

Instead of storing module or function names, for instance, the malware stores only encrypted hashes of these strings and uses the encrypted hash to search for functions in its modules. The address of the functions would then be encrypted and stored sparsely across the process.



3.1.4 Obfuscating the Call Stack in API Calls

Instead of calling an API directly, the malware goes through a rather lengthy process that wraps the actual API call.

Firstly, before any API calls can be made, the malware would have searched through all the loaded modules to find locations containing the two bytes "FF D3" (which assembles to "call ebx"). These addresses would then be encrypted and stored in the process memory. When the malware intends to make an API call, it would decrypt one of these addresses and validate that the bytes at the address are still "FF D3".

Subsequently, it stores the address of a post-API call function in the ebx register. This address is then hashed and used to xor-encrypt the actual return address. The xor key and the encrypted return address is then stored in the stack before the malware pushes all the function arguments onto the stack.

Finally, the "call ebx" address decrypted earlier would be pushed onto the stack before the malware jumps to the address of the API function. Therefore, the call stack would not accurately reflect the caller of the API function.

Another issue that I then encountered was that of the malware not behaving maliciously. Therefore, I suspected that it was doing some check on the environment and indeed, I found that the malware had multiple checks before it began its malicious behaviour.

3.1.5 Date Check

The first check was on the system date:



This check ensures that the payload only activates in the month of April in 2016. In fact, if we take into account the start date of malware campaign, we see that the malware had only a few days of potency. This makes it more difficult to analyse the malware beyond its potent period since the C&C servers would also have been taken down. However, for the purpose of this analysis, I patched the jump instructions to bypass the date check:



3.1.6 OUI Check

However, that was not all. Besides checking the date on the system, this malware also checked many other properties of the system which include the organizationally unique identifier (OUI) from the network interface card's (NIC) MAC address, the system BIOS version and the video BIOS version.

The technique which the malware used to obtain the MAC address' OUI is rather interesting. It first calls UuidCreateSequential from rpcrt4.dll to generate a UUID. Then, it extracts the OUI from the MAC address contained in the UUID:


Since it uses such an indirect method to check for the NIC's OUI, it wasn't immediately obvious what the malware was trying to do. Here are the OUIs that the malware was checking against:
  • F0:1F:AF (DELL INC)
  • 00:50:56 (VMWARE, INC)
  • 08:00:27 (CADMUS COMPUTER SYSTEMS)
  • 00:0C:29 (VMWARE, INC)
  • 00:05:69 (VMWARE, INC)
  • 00:03:FF (MICROSOFT CORPORATION)
  • 00:1C:42 (PARALLELS, INC)
  • 00:16:3E (XENSOURCE, INC)


3.1.7 System & Video BIOS Versions Check

Also, the malware checks for the following keywords in the system BIOS version contained in the HKLM\HARDWARE\DESCRIPTION\System registry key:
  • AMI
  • BOCHS
  • VBOX
  • QEMU
  • SMCI
  • INTEL  - 6040000
  • FTNT-1
  • SONI
Finally, it checks to see if the video BIOS version contains the keyword "VirtualBox".

Overcoming these checks will allow us to analyse the malicious behaviour of the malware but it is certainly not the end of the malware's attempt at obfuscation. In the following section, we will see how the malware author continues to throw analysts off track while analysing its malicious behaviour.


3.2 Malicious Behaviour

3.2.1 Analysis Methodology

With the help of my deobfuscation scripts, I was able to discover that all API calls were made through a single wrapper function. Therefore, I inserted a breakpoint at a strategic point in this function and wrote a script to log the function names and parameters of all API calls made by this malware. I also used the script to amend the call parameters as and when needed. With the API log, I was able to peer into the behaviour of the malware at a deeper level.

A snippet from the API call log


3.2.2 Process Hollowing

The first interesting thing that the malware does after the checks above is to create a rundll32.exe process with the command line arguments "-%random1% %random2%.dll" (e.g. -hs sneqd.dll) and hijack the main thread of execution to run its malicious shellcode. It does this in several steps:
  1. Calls CreateProcess to spawn the benign looking rundll32.exe process in a suspended state
  2. Calls NtProtectVirtualMemory to allow overwriting of code at the entry point
  3. Calls NtWriteVirtualMemory to write the bytes EB FE (jmp 0x0) at the entry point
  4. Reverts the protection bits of the page (NtProtectVirtualMemory) before flushing the instruction cache (NtFlushInstructionCache) and resuming the thread (NtResumeThread)
  5. Suspends the main thread using NtSuspendThread
  6. Calls NtAllocateVirtualMemory to request for memory to store its shellcode
  7. Writes its malicious shellcode into the allocated memory space
  8. Overwrites code at the entry point to hijack flow of the main thread into the injected shellcode
  9. Resumes execution of the main thread

The shellcode is almost a copy of itself with minor variation in several areas. The resulting process becomes the main gateway for communication between other infected processes and the C2 server.

Since it copied itself almost wholesale into the rundll32.exe process, one hiccup which I encountered was running into debugger interrupts (int 0x3) while debugging the injected process. I later realised that these interrupts originated from the debugger breakpoints that I had inserted into the parent process and quickly patched them back to their original bytes. One lesson which I learned from this though was to make use of hardware breakpoints more extensively so as to prevent modifications to the malware code.


3.2.3 File & Registry Modifications

After creating the rundll32.exe process, the parent process then goes on to mark several files for deletion using SetInformationFile by setting the FileDispositionInfo:
  • %allusersprofile%\%random3% (e.g. C:\ProgramData\yd43r13)
  • %appdata%\%random4% (e.g. C:\Users\Analyst\AppData\Roaming\j6et74)
  • %localappdata%\%random5% (e.g. C:\Users\Analyst\AppData\Local\7mz84)
These files, however, seem to be non-existent at the time when they are marked for deletion. Therefore, they could merely be API calls made to throw the analyst off track. The parent process then goes on to set a registry key value at \Registry\User\LogonUser\Software\Microsoft\%variable6% using a call to NtSetValueKey. The length of this key is usually larger than 0x700 bytes.



Subsequently, the parent process creates and writes a small amount of data to 2 files:
  • %allusersprofile%\%variable7%\%variable8% (e.g. C:\ProgramData\qpm\dxvuns.gco)
  • %temp%\%variable6%.%variable9% (e.g. C:\Users\Analyst\AppData\Local\Temp\lvypi.esn)
Interestingly, %variable6-9% seem to be generated based on some machine configuration as the same names were used across different runs. It is likely that these files and registry value are used to store some central data.

3.2.4 Privilege Escalation & Process Injection

It then escalates privilege by calling AdjustTokenPrivileges to grant itself SeDebugPrivilege (if allowed to). With elevated privilege, it then searches for other high/system integrity processes that it may inject itself into. Examples of processes that it may inject itself into include:
  • winlogon.exe
  • taskhost.exe
  • dwm.exe
  • explorer.exe
  • chrome.exe
It injects itself into these processes using the typical chain of API calls (NtAllocateVirtualMemory -> NtProtectVirtualMemory -> NtWriteVirtualMemory -> NtProtectVirtualMemory -> NtFlushInstructionCache) and by creating a new user thread using RtlCreateUserThread to run the injected shellcode. Each injected process would contain the same but slightly varied shellcode of the parent process and their behaviour are mostly similar to the parent process. They will:
  1. Mark 3 files for deletion (refer to the paths described above)
  2. Query for and set a registry value if it does not already exist (refer to the key described above)
  3. Write to 2 files if they do not already exist (refer to the paths described above)
  4. Communicate with the hollowed 'rundll32.exe' process via pipes

3.2.5 Network Behaviour

Since it is already past the potent period of the malware, I had to spoof the DNS response from 8.8.8.8 using a PCAP captured while the servers were still active. I originally used FakeNet to accomplish this but decided to use a different approach for greater customizability. This was FakeNet originally in action:
FakeNet redirecting DNS and HTTP packets to localhost

My final approach involved assigning multiple IP address to my network interface in order to redirect all traffic to the C&C servers to localhost:

Multiple IP addresses assigned to my network interface

I then ran my own DNS and HTTP servers to imitate a live environment:
DNS server script

With the fake network set up, I was able to learn more about how the malware communicates. The way in which it establishes communication with the C&C server is rather interesting:
  1. It first calls urlmon_ObtainUserAgentString to retrieve the user-agent string from the victim's default browser 
  2. It checks for internet connectivity by attempting to resolve 'microsoft.com' or 'google.com' using ws2_32_gethostbyname
  3. Then, it constructs a DNS query packet with a question for the A record of pfghmj.com
  4. Next, it creates a socket using a call to ws2_32_socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)
  5. It then sends the DNS query to either 8.8.8.8 or 8.8.4.4 using a call to ws2_32_sendto, followed by calls to ws2_32_recvfrom and ws2_32_closesocket to receive data from the socket and close it
  6. It uses the answers from the DNS response to generate the list of actual C&C addresses
  7. It calls ws2_32_inet_ntoa on each of the C&C IPv4 addresses to convert them into ASCII strings
  8. Finally, it sends a POST request containing data to one of the C&C addresses
By allowing the malware to self-decrypt, I was able to read the list of IPv4 addresses that the malware attempts to connect to:

  • 82.13.46.90
  • 54.186.122.8
  • 85.171.195.89
  • 194.149.138.49
Note that the malware does not connect to the IP addresses listed in the DNS response, but instead uses them to construct the actual addresses that it should connect to. Subsequently, it sends POST requests to the IP addresses and processes the response from the C&C server. The steps which it takes to communicate with the C&C server is as follows:
  1. Opens a WinHTTP session using a call to WinHttpOpen with the victim's default user-agent string retrieved via an API call earlier
  2. Connects to one of the IP addresses at port 80 using WinHttpConnect
  3. Calls WinHttpOpenRequest to open a POST request to /s2ldhtwpb/index.php using the WinHTTP session created above
  4. Adds the following request headers:
    • Host: pfghmj.com
    • Cache-Control: no-cache
    • Content-Type: application/x-www-form-urlencoded
  5. Sends the request using WinHttpSendRequest with a base64-encoded data
  6. Receives the response using WinHttpReceiveResponse before reading the response using WinHttpQueryHeaders, WinHttpQueryDataAvailable and WinHttpReadData
  7. The response is parsed and decrypted using a custom routine
Network traffic generated by a similar sample
The communication cycle repeats itself about every 5 to 15 minutes and this continues until the infected processes are terminated. Here, we note that even though the malware has tried hard to spoof its request as originating from a browser, its requests are still missing the "Referer" header commonly found in requests dispatched by web browsers.


3.2.6 Information Stolen

Based on the API calls made by the malware, any of the following information could have been stolen:
  • Computer Name
  • Username
  • System/Video BIOS Version
  • Machine GUID
  • Machine MAC Address
  • Volume Name and S/N of Primary Partition
  • System Default Locale & Language
  • Default Browser User-Agent
  • Information on Running Processes

3.2.7 Persistency

The infected rundll32.exe process deletes the parent file from the filesystem while it continues to communicate with the C&C server in the background.

Based on existing analysis using the autoruns tool, the malware itself is not persistent.




3.2.8 Indicators of Compromise

The registry value and dropped files (including the original Word document which runs the downloader) remain on the victim's computer as possible indicators of compromise. Also, infected endpoints which are still in communication with the C2 server would have a running 'rundll32.exe' process with invalid command line parameters described above. The endpoints would also be making DNS queries to resolve the malicious domain name and sending base64-encoded POST requests to the malicious domain.


3.28 Summary

In a nutshell, the malware:
  1. Performs checks on its environment to detect virtualization and debugging attempts
  2. Performs time check to ensure that the malware runs only in the month of April 2016
  3. Duplicates itself into a hollowed rundll32.exe process and other 'system/high' integrity level running processes
  4. Creates several files and a registry key
  5. Checks for internet connectivity by attempting to resolve 'microsoft.com' or 'google.com'
  6. Queries DNS records for 'pfghmj.com' using low-level sockets to construct the list of C&C IPv4 addresses
  7. Exfiltrates information via data in POST requests to the C&C server
  8. Parent file is deleted while the injected processes continue running


3.3 Lessons Learned

Through the analysis of this malware, I learned not only skills specific to malware analysis but also a ton of knowledge on Windows internals. This specific sample also showed me how a determined threat actor can create an asymmetrical battle by spending a lot of resources obfuscating the malware code and behaviour while giving analysts little time to study and respond to the threat. Such situations highlight the need for cyber defenders to constantly innovate and stay ahead of threat actors with creative and disruptive technologies like FireEye's MVX engine. However, technologies alone cannot win the war against malicious actors. Therefore, the information security industry needs to stay ahead of malicious actors by also having the best intelligence and expertise to pre-empt and neutralise threats.


3.4 Extras

For completeness, here is the list of APIs that the malware called:

advapi32_AccessCheck
advapi32_AdjustTokenPrivileges
advapi32_AllocateAndInitializeSid
advapi32_ConvertStringSecurityDescriptorToSecurityDescriptorW
advapi32_DuplicateToken
advapi32_EqualSid
advapi32_FreeSid
advapi32_GetFileSecurityW
advapi32_GetSidSubAuthority
advapi32_GetTokenInformation
advapi32_GetUserNameW
advapi32_LookupPrivilegeValueW
advapi32_MapGenericMask
advapi32_OpenProcessToken
kernel32_CallNamedPipeW
kernel32_CancelIo
kernel32_CloseHandle
kernel32_ConnectNamedPipe
kernel32_CreateEventA
kernel32_CreateEventW
kernel32_CreateFileW
kernel32_CreateMutexW
kernel32_CreateNamedPipeW
kernel32_CreateProcessW
kernel32_CreateThread
kernel32_DisconnectNamedPipe
kernel32_ExpandEnvironmentStringsW
kernel32_FlushFileBuffers
kernel32_FreeEnvironmentStringsW
kernel32_GetComputerNameA
kernel32_GetComputerNameW
kernel32_GetEnvironmentStringsW
kernel32_GetLastError
kernel32_GetOverlappedResult
kernel32_GetSystemDefaultLCID
kernel32_GetSystemDefaultUILanguage
kernel32_GetSystemTime
kernel32_GetSystemTimeAsFileTime
kernel32_GetSystemWindowsDirectoryA
kernel32_GetTempPathW
kernel32_GetTickCount
kernel32_GetVolumeInformationA
kernel32_GetWindowsDirectoryW
kernel32_IsWow64Process
kernel32_MultiByteToWideChar
kernel32_OpenEventW
kernel32_OpenMutexW
kernel32_ReadFile
kernel32_ReleaseMutex
kernel32_ResetEvent
kernel32_SetErrorMode
kernel32_SetEvent
kernel32_SetLastError
kernel32_SwitchToThread
kernel32_WaitForMultipleObjects
kernel32_WaitForSingleObject
kernel32_WaitNamedPipeW
kernel32_WideCharToMultiByte
kernel32_Wow64DisableWow64FsRedirection
kernel32_Wow64RevertWow64FsRedirection
kernel32_WriteFile
ntdll_LdrLoadDll
ntdll_NtAllocateVirtualMemory
ntdll_NtClose
ntdll_NtCreateFile
ntdll_NtCreateKey
ntdll_NtDelayExecution
ntdll_NtFlushInstructionCache
ntdll_NtFreeVirtualMemory
ntdll_NtGetContextThread
ntdll_NtOpenFile
ntdll_NtOpenKey
ntdll_NtOpenProcess
ntdll_NtOpenProcessToken
ntdll_NtProtectVirtualMemory
ntdll_NtQueryDirectoryFile
ntdll_NtQueryInformationFile
ntdll_NtQueryInformationProcess
ntdll_NtQueryInformationToken
ntdll_NtQuerySystemInformation
ntdll_NtQueryValueKey
ntdll_NtReadFile
ntdll_NtReadVirtualMemory
ntdll_NtResumeThread
ntdll_NtSetInformationFile
ntdll_NtSetValueKey
ntdll_NtSuspendThread
ntdll_NtWriteFile
ntdll_NtWriteVirtualMemory
ntdll_RtlAddVectoredExceptionHandler
ntdll_RtlAllocateHeap
ntdll_RtlCreateHeap
ntdll_RtlCreateUserThread
ntdll_RtlDosPathNameToRelativeNtPathName_U_WithStatus
ntdll_RtlEnterCriticalSection
ntdll_RtlFormatCurrentUserKeyPath
ntdll_RtlFreeAnsiString
ntdll_RtlFreeHeap
ntdll_RtlInitializeCriticalSection
ntdll_RtlLeaveCriticalSection
ntdll_RtlReleaseRelativeName
ntdll_RtlSizeHeap
ntdll__snprintf
ntdll__snwprintf
rpcrt4_UuidCreateSequential
urlmon_ObtainUserAgentString
winhttp_WinHttpAddRequestHeaders
winhttp_WinHttpCloseHandle
winhttp_WinHttpConnect
winhttp_WinHttpCrackUrl
winhttp_WinHttpCreateUrl
winhttp_WinHttpOpen
winhttp_WinHttpOpenRequest
winhttp_WinHttpQueryDataAvailable
winhttp_WinHttpQueryHeaders
winhttp_WinHttpReadData
winhttp_WinHttpReceiveResponse
winhttp_WinHttpSendRequest
winhttp_WinHttpSetTimeouts
ws2_32_WSAGetLastError
ws2_32_WSAStartup
ws2_32_closesocket
ws2_32_connect
ws2_32_gethostbyname
ws2_32_inet_addr
ws2_32_inet_ntoa
ws2_32_recvfrom
ws2_32_sendto
ws2_32_setsockopt
ws2_32_shutdown
ws2_32_socket


Some of the tools and resources that I found useful for my analysis are:
  • Aggregated Scanner
    • VirusTotal
  • Sandboxes
    • Hybrid-Analysis
    • Cuckoo Framework
    • Malwr
  • Network Tools
    • Wireshark
    • FakeNet
  • Process Monitoring
    • ProcMon + Noriben
    • Process Hacker
    • Process Explorer
  • Debugger & Disassembler
    • IDAPro + IDAStealth
    • OllyDbg
    • WinDbg
  • Persistence Monitoring
    • Autoruns
  • API Monitoring
    • APIMon
  • Others
    • PEiD
    • ExeInfoPE
    • PEViewer

Comments