PlugX: Bad guy disguises as an msi file
I. Overview

II. Analysis
1. Locate suspicious files
Use msitool to extract msidump -s -t mal.msi. In File.idt, we can see that there are 3 embed file.

These files are extracted to %LOCALAPPDATA\kjnBsLsJo\
2024Contact.exe
security.dll
contactDB.dat
Upon running, the program opens an pdf file called Meeting Invitation.pdf

2. DLL Side-loading technique
security.dll export 3 functions

2024Contact.exe load security.dll which trigger the call to NimMain in Dll dispatcher. Then, the executable calls InitSecurityInterfaceW after resolving its address.

The NimMain function's role is to resolve global variables's value. Most of them are api's address and config. Below is one of the functions called in NimMain

For InitSecurityInterfaceW, it registers a Window class object and a message dispatcher.

The call to RegisterClassW at line 50 requires a WNDCLASSW object. According to this docs, the object's structure is below

The second entry WNDPROC is A pointer to the window procedure. The function pointed to by that entry is going to be registered for this window class.


The window proc opens contactDB.dat file, reads it to an allocated memory, decrypt and use it as a callback in EnumSystemGeoID

The data after decrypted is an PE file.

The interesting thing is the header can be converted to executable code to be able to pass into EnumSystemGeoID. It will eventually jump into Initialize function.

3. Depression: Navigating through the control-flow obfuscation
Initialize function implemented a control flow obfuscation method.

Checking the functions calls, there are only a few call instructions in this function. The obfuscation method set up the call manually and place the called address to eax.

By placing breakpoints in all these calls, we can monitor the api used by this function as below:
kernel32_VirtualAlloc
kernel32_LoadLibraryA (kernel32.dll)
kernel32_GetProcAddress
GetProcAddress
LoadLibraryA
GetLastError
WriteConsoleW
CloseHandle
CreateFileW
QueryPerformanceCounter
GetCurrentProcessId
GetCurrentThreadId
GetSystemTimeAsFileTime
...
kernel32_LoadLibraryA (User32.dll)
kernel32_GetProcAddress
kernel32_VirtualProtect
kernel32_VirtualProtect
kernel32_VirtualProtect
kernel32_VirtualProtect
ntdll_NtFlushInstructionCache
DllEntryPoint
DllEntryPoint
It just initialize api and memory, the 2 importance calls are to DllEntryPoint function with args fdwReason equal to 1 and 4

For fdwReason equal to 4, it calls this function, which contains some suspicious activity of decrypting strings

The decryped string is used to resolve api, here at line 66 it resolves SetUnhandledExceptionFilter and call it imediately.

Then the program changes a few byte from the start of SetUnhandledExceptionFilter



A new thread is created to call sub_5B313F3

The thread first applies an anti debug check using 2 api kernel32_GetCurrentProcess and kernel32_CheckRemoteDebuggerPresent

To bypass this, we just have to change the return value of eax from 1 to 0
4. The config
Tracing for a while, the program hit this function which decrypt a block of data

The algorithm used is RC4 with key 10C6F. After being decrypted, the data block contains many interesting strings.
ZwNlqMnRE
529
Meeting Invitation.pdf
buyinginfo[.]org
5. Victim's info gathering
This sample collects multiple information about the victim to for identification. That information is exfiltrate through http.

Here is the list of infos that this malware collects:
Window's version
Computer name
Username
File in %appdata%/Render
Machine's IP
Architecture
6. Suspicious pdf file opening
The malware first create %temp%/tmp.dat file by copying contactDB.dat. Read the content and then decrypt it.

Then, payload of the pdf is written to %temp%/Meeting Invitation.pdf

Finally, it call ShellExecuteW to open the pdf as we see at first. %temp%/tmp.dat then is deleted.
7. Anti-debug, mutex, and other shenanigans...
Mutex
Mutex ZwNlqMnRE is created to ensure that one instance of malware runs concurrently. The mutex name is taken from the config data.

Anti-debug
This sample actively checks for debugger by running a thread just to call CheckRemoteDebuggerPresent every 1 second with a call to Sleep

Registry activities
This sample creates/queries many registry keys for multiple purposes, like info collection or defense bypass.
SOFTWARE\Microsoft\Windows\CurrentVersion\InternetSetting - ProxyEnable
SOFTWARE\Microsoft\Internet Explorer\Version Vector - IE
SOFTWARE\Microsoft\Windows\CurrentVersion\Internet Settings\50\User Agent\Post Platform - *
SOFTWARE\Microsoft\Windows\CurrentVersion\Internet Settings\User Agent\Post Platform - *
Software\CLASSES\ms-pu - CLSID
To disable the proxy, it set the value of ProxyEnable to 0

It also copies modules to %programdata%\VirtualFile\ and creates a value of the key CurrentVersion\Run to achieve persistence.

The copy destination folder like%programdata%\VirtualFile\ is randomly set, which variant between these:
%userprofile%\Intelnet
%allusersprofile%\VirtualFile
%public%\SamsungDriver
%appdata%\SecurityScan
%localappdata%\DellSetupFiles
8. How it communicate under the hood

This sample interacts with buyinginfo[.]org at port 443, it first receives a key from malicious server then uses it to encrypt traffic afterward.
The first request to the server includes randomly generated data

Another piece of data included is the CLSID value of registry key Software\CLASSES\ms-pu
Note that these data is located in the header section of the http request as it is added using winhttp_WinHttpAddRequestHeaders

After sending those data, it read 32 bytes from the response of the server.

For the second round, it collects the host's info (which was mentioned in section 5 before), encrypts it using RC4 algorithm using those 32 bytes of data as a key and sends the encrypted data to the server. Then, if there is a response from the server, it decrypt the data and uses the dword value at offset 4 as command and control option. For example, here it checks if the option value is equal to 0x7002.

9. Command and Control
0x1005: Bro gonna leave
This option deletes %programdata%\VirtualFile\, notice the server and then kill the process.
- Search for module
2024 Contact.exe
It uses APIs below to iterate through processes running on the machine and stop when 2023 Contact.exe is found.
kernel32_CreateToolhelp32Snapshot
kernel32_Process32FirstW
kernel32_OpenProcess
kernelbase_EnumProcessModules
kernelbase_GetModuleFileNameExW
kernel32_Process32NextW
- Create
%temp%\del_Contact Update.batusingkernel32_CreateFileWandkernel32_WriteFile.

The written content:

- Execute the
.batfile usingkernel32_CreateProcessW

- Exit the process

0x1007: Bro leaves violently

First it call kernel32_SetUnhandledExceptionFilter, which was previously modified from the begining. This fuction now leads us to a piece of 2 line instruction which only returns 0.

Then it call kernel32_UnhandledExceptionFilter to detect debugger and invoke the pre-registered handler.
Finally, GetCurrentProcess and TerminateProcess is called to exit.
However, if there is no debugger, it executes the exception handler. The handler then call FatalAppExitW with this message.

0x3004: Drop / modify a file
- Create new thread execute
sub_541312A

- Query
SOFTWARE\Microsoft\Internet Explorer\Version VectorvalueIE

- Enumerate through all the values in
SOFTWARE\Microsoft\Windows\CurrentVersion\Internet Settings\50\User Agent\Post Platform

Send data to the server, then receive the file name and file data
Open file then write the content.

0x7002: spawn cmd shell
To establish a connection between cmd shell and a malicious server, it uses a named pipe to send and receive data from the shell. By using 2 threads for reading and writing, it can interact with cmd simultaneity.
Here is the function that creates 2 named pipes and stores those handles to global variables.

It then cratf a STARTUPINFO structure and stores our handle to hStdInput and hStdOutput. By doing so, it can read data and write commands to the shell through the named pipe.

Finally, it creates a process cmd.exe using a crafted struct.

III. IOC
| Type | Value |
| Mutex | ZwNlqMnRE |
| URL | buyinginfo[.]org |
| Filename | Meeting Invitation.pdf |
| Filename | del_Contact Update.bat |
| Filehash | 7486cefa12be05d7c027c6d85b024835346e2450 |
| Filehash | 3e67011bbb2867de4ba17c0d2bb64db324d3b0c9 |
| Filehash | 2705079351efebe935ccbc395cbc16a523f69125 |
| Filehash | dc62cb7c24c14d6dbb16412e6553c4f10c34051d |
| Registry key | HKCU\SOFTWARE\Microsoft\Windows\CurrentVersion\Run - Contact Update |
| Folder | %userprofile%\Intelnet |
| Folder | %allusersprofile%\VirtualFile |
| Folder | %public%\SamsungDriver |
| Folder | %appdata%\SecurityScan |
| Folder | %localappdata%\DellSetupFiles |
IV. Att&ck technique used






