# PlugX: Bad guy disguises as an msi file ## I. Overview ![plugx.drawio](https://hackmd.io/_uploads/S1Rfac2OC.png) ## 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. ![1](https://hackmd.io/_uploads/SyWP4hoDC.png) 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` ![image](https://hackmd.io/_uploads/HJhcgMS_0.png) ### 2. DLL Side-loading technique `security.dll` export 3 functions ![2](https://hackmd.io/_uploads/HJjwVhiv0.png) `2024Contact.exe` load `security.dll` which trigger the call to `NimMain` in Dll dispatcher. Then, the executable calls `InitSecurityInterfaceW` after resolving its address. ![3](https://hackmd.io/_uploads/B1d_E3oP0.png) 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` ![4](https://hackmd.io/_uploads/S1JFEnsDC.png) For `InitSecurityInterfaceW`, it registers a Window class object and a message dispatcher. ![5](https://hackmd.io/_uploads/HkDKV2svR.png) The call to `RegisterClassW` at line 50 requires a `WNDCLASSW` object. According to this [docs](https://learn.microsoft.com/en-us/windows/win32/api/winuser/ns-winuser-wndclassa), the object's structure is below ![6](https://hackmd.io/_uploads/rJkcVnsw0.png) 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. ![7](https://hackmd.io/_uploads/BJfiEhjPC.png) ![8](https://hackmd.io/_uploads/BycsV3iP0.png) The window proc opens `contactDB.dat` file, reads it to an allocated memory, decrypt and use it as a callback in `EnumSystemGeoID` ![9](https://hackmd.io/_uploads/H10jV2iP0.png) The data after decrypted is an PE file. ![10](https://hackmd.io/_uploads/H1xhVhovA.png) 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. ![11](https://hackmd.io/_uploads/rJ-0EhswR.png) ### 3. Depression: Navigating through the control-flow obfuscation `Initialize` function implemented a control flow obfuscation method. ![image](https://hackmd.io/_uploads/rJBx4C2vR.png) 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. ![image](https://hackmd.io/_uploads/Sy1-SRhwA.png) 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 ![image](https://hackmd.io/_uploads/HyZsvChw0.png) For `fdwReason` equal to 4, it calls this function, which contains some suspicious activity of decrypting strings ![image](https://hackmd.io/_uploads/Sk4dtA2v0.png) The decryped string is used to resolve api, here at line 66 it resolves `SetUnhandledExceptionFilter` and call it imediately. ![image](https://hackmd.io/_uploads/rk_bbb6DA.png) Then the program changes a few byte from the start of `SetUnhandledExceptionFilter` ![image](https://hackmd.io/_uploads/Skk4fZTvR.png) ![image](https://hackmd.io/_uploads/Syv7VW6vR.png) ![image](https://hackmd.io/_uploads/S1b-r-6wR.png) A new thread is created to call `sub_5B313F3` ![image](https://hackmd.io/_uploads/rygUDbaDA.png) The thread first applies an anti debug check using 2 api `kernel32_GetCurrentProcess` and `kernel32_CheckRemoteDebuggerPresent` ![image](https://hackmd.io/_uploads/HyKYsb6DA.png) 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 ![image](https://hackmd.io/_uploads/rkvGgXaPA.png) 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`. ![image](https://hackmd.io/_uploads/SyS-yTNdC.png) 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. ![image](https://hackmd.io/_uploads/HJTbtXjqA.png) Then, payload of the pdf is written to `%temp%/Meeting Invitation.pdf` ![image](https://hackmd.io/_uploads/SkZOFQi9C.png) 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. ![image](https://hackmd.io/_uploads/HJUmHQpwA.png) #### Anti-debug This sample actively checks for debugger by running a thread just to call `CheckRemoteDebuggerPresent` every 1 second with a call to `Sleep` ![image](https://hackmd.io/_uploads/H17rTyruR.png) #### 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 ![image](https://hackmd.io/_uploads/BkunAM0PC.png) It also copies modules to `%programdata%\VirtualFile\` and creates a value of the key `CurrentVersion\Run` to achieve persistence. ![image](https://hackmd.io/_uploads/rypFOxHdR.png) 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 ![image](https://hackmd.io/_uploads/ryLb9sCwR.png) 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 ![image](https://hackmd.io/_uploads/SJbYTSr_A.png) 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` ![image](https://hackmd.io/_uploads/BJqi3UH_C.png) After sending those data, it read 32 bytes from the response of the server. ![image](https://hackmd.io/_uploads/SkRZkLrdA.png) 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`. ![image](https://hackmd.io/_uploads/HJ8dhW8OC.png) ### 9. Command and Control #### 0x1005: Bro gonna leave This option deletes `%programdata%\VirtualFile\`, notice the server and then kill the process. 1. 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 ``` 2. Create `%temp%\del_Contact Update.bat` using `kernel32_CreateFileW` and `kernel32_WriteFile`. ![image](https://hackmd.io/_uploads/rJ0AuDbOR.png) The written content: ![image](https://hackmd.io/_uploads/BkrkFDW_0.png) 3. Execute the `.bat` file using `kernel32_CreateProcessW` ![image](https://hackmd.io/_uploads/ry7NwZf_R.png) 4. Exit the process ![image](https://hackmd.io/_uploads/ByH0IbM_0.png) #### 0x1007: Bro leaves violently ![image](https://hackmd.io/_uploads/B1jk2omOA.png) 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. ![image](https://hackmd.io/_uploads/Bkf_njXOA.png) 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. ![image](https://hackmd.io/_uploads/HkOgr3m_R.png) #### 0x3004: Drop / modify a file 1. Create new thread execute `sub_541312A` ![image](https://hackmd.io/_uploads/H1Fi8wMOC.png) 2. Query `SOFTWARE\Microsoft\Internet Explorer\Version Vector` value `IE` ![image](https://hackmd.io/_uploads/SyfvbGG_C.png) 3. Enumerate through all the values in `SOFTWARE\Microsoft\Windows\CurrentVersion\Internet Settings\50\User Agent\Post Platform` ![image](https://hackmd.io/_uploads/rkCKdvfOR.png) 4. Send data to the server, then receive the file name and file data 5. Open file then write the content. ![image](https://hackmd.io/_uploads/S1ZI5wGdC.png) #### 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. ![image](https://hackmd.io/_uploads/S1PsysmuA.png) 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. ![image](https://hackmd.io/_uploads/BJmMxsQd0.png) Finally, it creates a process `cmd.exe` using a crafted struct. ![image](https://hackmd.io/_uploads/BJbc-dfO0.png) ## 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 ![image](https://hackmd.io/_uploads/BkzIEi3uR.png)