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 |
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
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:- Calls CreateProcess to spawn the benign looking rundll32.exe process in a suspended state
- Calls NtProtectVirtualMemory to allow overwriting of code at the entry point
- Calls NtWriteVirtualMemory to write the bytes EB FE (jmp 0x0) at the entry point
- Reverts the protection bits of the page (NtProtectVirtualMemory) before flushing the instruction cache (NtFlushInstructionCache) and resuming the thread (NtResumeThread)
- Suspends the main thread using NtSuspendThread
- Calls NtAllocateVirtualMemory to request for memory to store its shellcode
- Writes its malicious shellcode into the allocated memory space
- Overwrites code at the entry point to hijack flow of the main thread into the injected shellcode
- 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)
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)
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
- Mark 3 files for deletion (refer to the paths described above)
- Query for and set a registry value if it does not already exist (refer to the key described above)
- Write to 2 files if they do not already exist (refer to the paths described above)
- 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:
- It first calls urlmon_ObtainUserAgentString to retrieve the user-agent string from the victim's default browser
- It checks for internet connectivity by attempting to resolve 'microsoft.com' or 'google.com' using ws2_32_gethostbyname
- Then, it constructs a DNS query packet with a question for the A record of pfghmj.com
- Next, it creates a socket using a call to ws2_32_socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)
- 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
- It uses the answers from the DNS response to generate the list of actual C&C addresses
- It calls ws2_32_inet_ntoa on each of the C&C IPv4 addresses to convert them into ASCII strings
- Finally, it sends a POST request containing data to one of the C&C addresses
- 82.13.46.90
- 54.186.122.8
- 85.171.195.89
- 194.149.138.49
- Opens a WinHTTP session using a call to WinHttpOpen with the victim's default user-agent string retrieved via an API call earlier
- Connects to one of the IP addresses at port 80 using WinHttpConnect
- Calls WinHttpOpenRequest to open a POST request to /s2ldhtwpb/index.php using the WinHTTP session created above
- Adds the following request headers:
- Host: pfghmj.com
- Cache-Control: no-cache
- Content-Type: application/x-www-form-urlencoded
- Sends the request using WinHttpSendRequest with a base64-encoded data
- Receives the response using WinHttpReceiveResponse before reading the response using WinHttpQueryHeaders, WinHttpQueryDataAvailable and WinHttpReadData
- The response is parsed and decrypted using a custom routine
Network traffic generated by a similar sample |
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:- Performs checks on its environment to detect virtualization and debugging attempts
- Performs time check to ensure that the malware runs only in the month of April 2016
- Duplicates itself into a hollowed rundll32.exe process and other 'system/high' integrity level running processes
- Creates several files and a registry key
- Checks for internet connectivity by attempting to resolve 'microsoft.com' or 'google.com'
- Queries DNS records for 'pfghmj.com' using low-level sockets to construct the list of C&C IPv4 addresses
- Exfiltrates information via data in POST requests to the C&C server
- 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
Post a Comment