# oR10n Labs **[or10nlabs.tech/reverse-engineering-the-mustang-panda-plugx-loader](https://or10nlabs.tech/reverse-engineering-the-mustang-panda-plugx-loader)** By oR10n 2020-05-24 [HomeReverse EngineeringReverse Engineering the Mustang Panda PlugX Loader](https://or10nlabs.tech/) ## Reverse Engineering the Mustang Panda PlugX Loader Hello everyone! In this series, we will be diving into the inner workings of a new-ish variant of PlugX malware gaining traction around the Asia Pacific region for the past few months. ## Introduction PlugX is a fully featured remote access trojan (RAT) with various capabilities such as file upload/download, file operations, registry operations, process operations, keystroke logging, capturing screenshots or videos, and initiating remote shell on compromised systems. [Based on the analysis reports released by two security companies – Anomali and](https://www.anomali.com/blog/china-based-apt-mustang-panda-targets-minority-groups-public-and-private-sector-organizations) [Avira, this](https://insights.oem.avira.com/new-wave-of-plugx-targets-hong-kong/) new variant is primarily used by a suspected China-based APT group being referred to as "Mustang Panda", to target organizations primarily located in the Asia Pacific region. For this post, we will reverse engineer the loader component of the new variant to understand how it loads, decrypts, and executes the encrypted payload in memory. Then, we will create a quick-and-dirty python script to automate the decryption process so we won’t need to run the loader every time we want to do a deeper analysis on a payload or perform bulk analysis. Lastly, I will show you one of the ways to hunt for new encrypted payloads uploaded in VirusTotal. ----- But before we get our hands dirty, let us first take a look on how PlugX is initially delivered and executed on a system. ## PlugX Delivery and Execution As you might have known from previous analysis reports, PlugX is primarily made up of three main components: 1. A legitimate executable used for loading and executing a malicious DLL 2. A malicious DLL used for decrypting, loading, and executing an encrypted payload 3. An encrypted payload containing the main RAT functionalities On the earlier variants of PlugX, these three components are typically delivered via phishing emails containing an attached self-extracting RAR (SFX) archive, acting as a dropper for these components. However for this variant, this RAR archive was replaced by a malicious LNK file as seen on the Anomali analysis report. **[Note: Logrhythm released an article on April 2018, detailing the evolution and variants of](https://logrhythm.com/blog/deep-dive-into-plugx-malware/)** PlugX over the years. A general overview of the delivery and execution flow looks like this: ## Reverse Engineering the Loader For our analysis, we will take a deeper look at a sample discovered by Avira in the wild. These are the details of the PlugX components for our analysis: **Component** **Filename** **MD5** ----- **Component** **Filename** **MD5** Legit exe AdobeInstall.exe c70d8dce46b4551133ecc58aed84bf0e Loader hex.dll eafaba7898e149895b36ee488e3d579c Payload adobeupdate.dat 58bdf783da4c627d2f13612a09a9b5a8 Let’s dive in! As a first step in reverse engineering, it’s a good practice to perform static analysis first to gain a general overview of the sample that can serve as a guide through out the process. Checking the sample on CFF explorer shows us that it has only 1 Export function named **CEFProcessForHandlerEx.** It also has a very few Import functions which suggests that this sample dynamically loads Win32 API functions at runtime via GetModuleHandleA and GetProcAddress. ----- Additionally, we can also run a string utility to check out the strings on the sample. I normally use FIreEye’s [FLOSS tool which is a string utility on steroids. Aside from displaying static](https://github.com/fireeye/flare-floss) ASCII/UNICODE strings, it can also display stack strings and automatically decode strings that are encoded with simple and well known algorithms. Here are some of the interesting strings FLOSS found on the sample: ``` LocalFree LocalAlloc GetProcAddress GetModuleHandleA VirtualProtect CloseHandle CreateFileA ReadFile \adobeupdate.dat GetModuleFileNameA kernel32 GetFileSize lstrcatA strlen ``` Based from these strings, we can come up with a hypothesis on the general flow and functionality of the sample: Dynamically loads Win32 API functions from kernel32 at runtime via **GetModuleHandleA and GetProcAddress** Obtains the full path of the running process via GetModuleFileNameA Performs string operations via strlen and lstrcatA Allocates a memory buffer via LocalAlloc Reads a file named adobeupdate.dat via CreateFileA, GetFileSize, ReadFile, and **CloseHandle** Marks an allocated memory buffer as executable using VirtualProtect Next, we can load the sample on a disassembler like IDA and a debugger like x32dbg. For debugging, you can open AdobeInstall.exe and set a DLL breakpoint on hex.dll in order to debug it. **Note: AdobeInstall.exe loads hex.dll from the same directory and PlugX have taken** advantage of this to load the malicious DLL as a form of anti-detection/anti-analysis technique. ----- Since there’s only one export function in the DLL, it is fairly safe to assume that the sample only has one purpose – to load, decrypt, and execute the encrypted payload. We can easily follow the export function in IDA and dertermine that the main function of the DLL lies in sub_10001354. The first few lines of disassembly will show you "\adobeupdate.dat", "kernel32", and "GetModuleFileNameA" being initiated as stack strings. Usage of stack strings is a common anti-analysis and anti-detection technique employed by malware. This is typically used to prevent certain strings from showing up on basic string utilities. ----- After initiating the stack strings, the address of GetModuleFileNameA is dynamically resolved via GetModuleHandleA and GetProcAddress. Upon resolving its address in **kernel32, GetModuleFileNameA is called.** Running through it in x32dbg shows that GetModuleFileNameA returned the full path of the binary for the running process – which is "C:\Users\user\Desktop\AdobeInstall.exe". Next, the full path and the character "\" is passed as a parameter in a function **sub_10001000. This function splits the full path using "\" as delimiter and returns the** address of the filename – which is "\AdobeInstall.exe". A few lines after, the first character of "\AdobeInstall.exe" is replaced by 0x00, thereby splitting the full path into two different strings in memory "C:\Users\user\Desktop" and "AdobeInstall.exe". Next, the address of lstrcatA is also resolved dynamically using the same **GetModuleHandleA and GetProcAddress technique mentioned earlier. lstrcatA is used to** form the full path of the encrypted payload by concatenating "C:\Users\user\Desktop" and "\adobeupdate.dat". ----- Now that the full path of the encrypted payload is formed, a call to a function sub_10001084 is made in order to read the file contents of the encrypted payload, get the file size, and load the contents into a buffer in memory. The following arguments are pushed into the stack before the function call is made: Looking closely at the disassembly of the function, we can see that same as before, the address of CreateFileA, GetFileSize, ReadFile, and CloseHandle are resolved dynamically using the same GetModuleHandleA + GetProcAddress technique. After resolving the addresses of the functions, a call to each one is made in the following order: **CreateFileA to open the encrypted payload** **GetFileSize to obtain the file size of the encrypted payload** **LocalAlloc to allocate a buffer in memory** **ReadFile to read the contents of the encrypted payload and place it in the buffer** ----- After making these function calls, the address of the buffer containing the contents of the encrypted payload and the file size are stored in the arguments pushed to the stack earlier. Then finally, a call to CloseHandle is made and EIP returns to the main function. ----- Just a few lines of disassembly after, we can see some instructions assigning the address of the buffer to a new variable and that variable being passed as a parameter to strlen. These instructions essentially obtain the encryption key from the encrypted payload and determine its length via strlen. As you might remember, a string is an array of characters terminated with a NULL byte. So passing the address of the encrypted buffer to strlen will give us the length of the first string it sees. Later on, we will see how this string is used as a key for the decryption routine. A few lines after, we can see a sub operation being performed on the file size using the determined key size to compute the file size of the payload without the key and the NULL byte. Moving down further on the main function, we can immediately see that there is a loop before it proceeds to the final set of instructions. What this loop does is basically read the remaining bytes after the encryption key from the original buffer and copy it to a new buffer. ----- Right after the loop, the key size, key, file size, and new buffer is pushed to the stack and a call to a function sub_100018D0 is made. This is the function that contains the algorithm to decrypt the encrypted payload. Looking closely at the disassembly of the function, we can immediately determine that the algorithm performs XOR using a multi-byte key. ----- Running this on a debugger, shows that the decrypted payload is a PE file. Going back to the main function after the call to the decryption function, we can see the address of VirtualProtect being resolved using the same GetModuleHandleA + **GetProcAddress technique.** Upon resolving the address, we can see a call to VirtualProtect to change the access protection of the buffer containing the decrypted payload to 0x40 (PAGE EXECUTE READWRITE) ----- Lastly, we can see a call to the address of the buffer to execute the decrypted payload. ..and there you have it folks, PlugX is now loaded to memory and executed on the system. ## Automating the payload decryption To make our lives easier, I created this quick-and-dirty python script to automatically decrypt payloads for this variant: ## Hunting for encrypted payloads in VirusTotal I’m also sharing this VT hunting YARA rule that I came up with to hunt for encrypted payloads associated with this variant. The rule is based on the filenames mentioned on the Avira report. You may get some false positives on this one, but together with the python script above, this can be an effective approach to hunt for encrypted payloads that may otherwise go unnoticed on VT. That’s it guys! I really hope you learned something new today and as always, thank you for reading my blog! **PS: Stay tuned for the next post on this series where we’ll reverse engineer some interesting** parts of the PlugX payload itself. Cheers! ----- [Tags:MustangPanda,](https://or10nlabs.tech/tag/mustangpanda/) [PlugX,](https://or10nlabs.tech/tag/plugx/) [Reverse Engineering](https://or10nlabs.tech/tag/reverse-engineering/) -----