Sorry for the long post, I have no idea how to make it shorter, if you do not have time to read all so you can press a like and then move the other page. On the one hand, I like to write, the other is the way I develop skills … partly because I know that only when I really begin to analyze it is different from what I read with eyes and imagination …

Accidentally caught on twitter of @blu3_team (I do not know why this account often target models in Vietnam), I’m curious to know what technique is behind it because I find it similar to the one I read, and because of the comment, the suspect is OceanLotus, who claims to be 1937CN Team …

1. Implementation environment

1. The REMnux virtual machine ( use to analyze files, emulate Internet services and capture network traffic.
2. The Win10x64 virtual machine (build): use for Static & Dynamic Analysis
a. Installed debugger & disassembler tools: OllyDbg, x64dbg, IDA …
b. Installed Office 2013.
c. Enable the Administrator account (this account is disabled by default) and login with this account to perform the analysis.

2. Behavioral analysis

When opening the document on the Win10 virtual machine, you will see the EQNEDT32.exe application called, and then two additional processes are QcConsol.exe and dllhst3g.exe:

The REMNUX virtual machine runs Wireshark to capture traffic from the Win10 virtual machine, get results connected to C2 server is login [dot] dangquanwatch [dot] com:

Noriben’s log ( provides:


3. Sample analysis on REMnux

Sample received is a RTF file:

Use rtfobj (, you know that this sample has 3 objects:

The object at id 0 has a filename as 8.t, when opened; this file will be dropped into the Temp folder on the machine. The other two objects are identified as “Not a well formed ole object”.

Use rtfobj to dump all of these objects:


According to the data, the file is encrypted and decrypted after the Temo drop.

With the file b45087ad4f7d84758046e9d6eb174530fee98b069105a78f124cbde1ecfb0415_object_000C11FB.raw:

Based on the image as shown in the figure, the RTF file capability may use the exploit CVE-2017-11882 (

Check the other file is b45087ad4f7d84758046e9d6eb174530fee98b069105a78f124cbde1ecfb0415_object_000C11E9.raw:

This file will contain the shellcode to execute after the victim is exploited. Information is afraid of the set, so we will make this debug sample to see how 8.t file is used.

4. Debug maldoc on Windows10

In connection with the exploit CVE-2017-11882, when running the sample, Winword.exe will call the EQNEDT32.exe process to handle the OLE object. However, Winword.exe is not the father process of EQNEDT32.exe, the EQNEDT32.exe process is called by Winword.exe through the use of COM Object as shown below:

So, somehow we have to attach EQNEDT32.exe into the debugger to debug. Here, I use a technique of M$ as Image File Execution Options (IFEO:

Into Registry, create a key as follows, or if you have Word2013 installed, then this key availability (because I see on my machine is available):

Next, create a string value to launch the debugger when EQNEDT32.exe is executed, which will attach the debugger into the process of EQNEDT32.exe.

With this setting, checked by Autoruns will be as follows:

Note: When setting up IFEO, the settings will automatically synchronize between two keys: HKLM  / Software \ Wow6432Node \ Microsoft \ Windows NT \ CurrentVersion \ Image File Execution Options and HKLM \ Software \ Microsoft \ Windows NT \ CurrentVersion \ Image File Execution Options

Next, open WINWORD.EXE, then from Winword open the malicious RTF document. At this point, the EQNEDT32.exe process will also be launched and will be attached to the debugger:

At the debugger, we are stopping at the EP (Entry Point) of EQNEDT32.exe:

When checked, we can see that the 8.t file has been dropped into the Temp folder:

Put BP in the CreatFileW API, then press F9 to execute, we see the program will open 8.t file to read the content:

Trace through this function and return, will be to the shellcode of the exploit:

Call GetFileSize function to get the size of 8.t:

Then, call the VirtualAlloc function to allocate a memory area:

The memory allocated by the EAX register, follow this memory area to see what the code will do on it:

The ReadFile function is called to read the contents of 8.t:

The entire contents of 8.t are read into the allocated memory space above:

The trace will go to the shellcode to decrypt the entire contents of the 8.t file in memory at 0x4F70000:

After a few times the trace code, you will see the MZ sign, but after the solution, the 8.t file will be a PE file:

Execute the whole decoded loop will get a complete PE in memory:

Dumped new PE and saved to perform the following analysis:

File Dumped file is an exe file:

Continuing to debug, shellcode calls the VirtualAlloc function to allocate another memory space at address 0x5170000:

PEs decoded at the 0x4F70000 memory area which will be copied to the newly allocated memory area:

Dump the memory area out of memory, and because this file has been mapping in memory and has changed, use the pe_unmapper utility of hasherezade ( to convert from virtual format to raw format:

The next debug, the shellcode calls the GetModuleFileNameA function to get the path of EQNEDT32.exe:

Note: If you do this again, use Autoruns to discard the use of IFEO and for the CreateProcessA function, you will get the following result:

The next code takes the thread context with GetThreadContext, reads the data from memory with the ReadProcessMemory function, calls VirtualProtectEx (PAGE_EXECUTE_READWRITE 0x40) to change the state of the memory on the Suspend process, and finally shellcode overwrite it with PE at address 0x5170000:

Reset thread context with SetThreadContext, finally shellcode implements ResumeThread function to launch new PE:

To summarize, the whole process of shellcode implementation is to decrypt the 8.t file into a new PE, then replicate to another memory, making a new fork process EQNEDT32.exe; finally, applying the runPE technique to launch new EQNEDT32.exe which has been overwritten by the contents of the 8.t file.

5. Binary analysis has dumped

As known in the dynamic analysis, after the resume thread, malware will drop the following disk files: QcConsol.exe; QcLite.dll; stdole.tlb into the % AppData% \ Microsoft \ Windows \ Printer Shortcuts folder.

I have 2 dumped files that are _04F70000.mem and drop_bin.exe (to be unmap with the pe_unmapper tool). However, only _04F70000.mem file is executable normally, drop_bin.exe file is crashed (although at fix, check with PE bear everything is ok.) I have chat about this problem. Received the response of hasherezade as follows: “dumped samples may not always work, so it is normal”).

Open the IDA and load the _04F70000.exe file (renamed from the .mem file), stopping at WinMain ():

Binary takes the path to the% AppData% \ Microsoft \ Windows \ PrinterShortcuts folder:

It constitutes the path of the files:

Go to the code that executes call sub_331860 3 times to drop files into the specified folder. I changed this sub name to drop_file as shown below:

Being  into this function will see the xor loop implementing decode bytes, then the code executes WriteFile into the directory:

As a result of the first drop_file implementation, we get the QcConsol.exe file:

This is a valid, signed file developed by McAfee, Inc.:

Call the second drop_file function will drop QcLite.dll file, this file does not have information about Signature as well as info, so malicious code will be located in this file:

Call the third drop_file function will drop stdole.tlb. file. Information about .tlb can be found here: (

Continuously, it constitutes a command like this:

Finally, call the WinExec function to execute QcConsol.exe with the parameter “-LowIntegrityServer“:

Thus, with the successful implementation, QcConsol.exe will have to load QcLite.dll to perform malicious code.

6. DLL hijacking – Analyzing the QcConsol.exe file

Load file into IDA to receive the message:

To load QcLite.dll, QcConsol.exe uses the LoadLibraryW API and then calls GetProcAddress to get the address of the function. In essence, when loading a module, the code of the dll will also be executed from DllMain:

7. Preliminary analysis QcLite.dll file

Call the VirtualAlloc function to allocate a memory area:

Get the full path to QcLite.dll:

This DLL will load the .tlb file:

Call CreateFileW to open this file (lpFileName points to stdole.tlb):

Get the size of stdole.tlb:

Read data from stdole.tlb and save it to the allocated memory space:

Perform a loop using xor to decode all data of stdole.tlb has been copied to memory:

After decoding, the result would be another PE file:

Through a lot of rop_chain (I guess :D), it will jump to the memory area to execute the code. (The fastest way is to put an HWBP on Execute at the first 4 bytes 0x50 0x68 0xA7 0x45; then press F9):

The shellcode will access the PEB (Process Environment Block) at 0x01A10000 to retrieve the kernel32.dll base address:

After getting base address of kernel32.dll, shellcode looks for the address of the GetProcAddress API function:

With the GetProcAddress API function; shellcode gets the address of other API functions as LoadLibraryA, VirtualAlloc, FreeLibraryA, Sleep:

Use the VirtualAlloc function to allocate a memory area and call the decode_data () function to decode bytes in the shellcode into the allocated memory:

Continuously, using VirtualAlloc to allocate a different memory size from (dwSize = SizeOfImage = PE_header + 0x50) and setting this new memory area as PAGE_EXECUTE_READWRITE:

After retrieving the section header in the 0x01880000 memory area, execute the loop to copy all section data to the newly allocated memory:

Resolve the entire API address to the IAT of the new memory:

After getting the address of all the necessary APIs, use the call command to jump to memory to execute the command:

Continuously debugging through the caller’s class will go to call the CreateThread function to create a new thread:

Go to the ThreadFunction at address 0x01EE35D0. The code executes the available Windows binaries as dllhst3g.exe here:

Overview the code, you can see a code related to C2 code (login[dot]dangquanwatch[dot]com):

Create another thread that creates Persistent in the HKEY_LOCAL_MACHINE \ SOFTWARE \ Microsoft \ Windows \ CurrentVersion \ Run Registry:

Call the WritePrivateProfileStringW function to write the string to the file at “C: \ ProgramData \ desktop.ini”:

Set attribute for file with SetFileAttributesW function:

Create a Mutex {986AFDE7-F299-4A7D-BBF4-CA756FC01F1B65027208}, but the handle to this mutext will be closed immediately:

Continue using the CreateFileW, GetFileSize, VirtualAlloc, ReadFile APIs to read again the content stored in the % AppData% \ Microsoft \ Windows \ Printer Shortcuts \ stdole.tlb file and execute to decode the same data as mentioned in Previous step:

Perform the inject code technique by calling the CreateProcessW function to start the dllhst3g.exe process in the Suspended state:

Allocate memory in this process through the VirtualAllocEx function:

Call the WriteProcessMemory function to write data from 0x00F90000 (the buffer containing the decoded data of stdole.tlb) into the allocated memory at the dllhst3g.exe process, reset thread context and resume thread. At this point dllhst3g.exe will execute normally and execute malicious code:

8. Debug dllhst3g.exe

Completing the inject code into dllhst3g.exe will call ExitProcess to finish the QcConsol.exe process and continue executing dllhst3g.exe process. Since dllhst3g.exe is injected with the code of the stdole.tlb file after the decode in memory, the way it works is the same. To be able to debug what dllhst3g.exe will do before executing the b step above, edit the first 2 bytes to 0x50 0x68 to 0xEB 0xFE. After resuming the thread, open another debugger to attach and restore 2 undefined bytes.

At this point, the debugger will see the code create a mutext and reread the content from the “C: \ ProgramData \ desktop.ini” file and decode the string in this file:

Add the following parameter: 0206F4E4 00D80B30 UNICODE “C: \ Users \ REM \ AppData \ Roaming \ Microsoft \ Windows \ Printer Shortcuts \ QcConsol.exe” –LowIntegrityServer “and call WinExec function to execute

This new process will connect to C2 (Here I am driving traffic to REMnux):

At the REMNUX machine, using wireshark will capture the following information:

9. IOCs

Domain: login[dot]dangquanwatch[dot]com / IP:

RTF: b45087ad4f7d84758046e9d6eb174530fee98b069105a78f124cbde1ecfb0415

8.t: 6328dd14eda2ef983810c0c7b3af47298b5998e4fa52d97b204be2818f08bb69


QcConsol.exe: 9f3114e48dd0245467fd184bb9655a5208fa7d13e2fe06514d1f3d61ce8b8770

QcLite.dll: 5b652205b1c248e5d5fc0eb5f53c5754df829ed2479687d4f14c2e08fbf87e76


stdole.tlb: ba620bad026f25ba6decc4bdcefc6415b563503cf9eaddc4e1137a5871d5cee2

desktop.ini: 31c2be9ca29fb2bd8096720c221ee9682f013eee119b02d390d6efc12684392d


HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Run & HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Run

ValueName: Windows HD Audio Manager

Data: %AppData%\MICROS~1\Windows\PRINTE~1\QcConsol.exe -LowIntegrityServer

No Comments
Post a Comment