In the last post, Lab52 covered the new Mustang Panda’s campaing against Australia. Now is time to talk about the malware used by the APT group Mustang Panda in said campaing.
Indeed, the malware used to commit the attack is not enterely new; there are previous reports from TrendMicro and Talos where similar tactics and procedures are detailed. However, some parts highlighted below differ and should be known in order to prepare our detection systems.
Summarizing, this post covers the following aspects:
- Continues the previous post, from a “malware” perspective, detailing the piece of malware used in this campang.
- The encryption algorithm using during the Stage 2 is different from those observed in similar reports.
- Both shellcodes for Stage 2 and 3 are considered new artifacts.
- There are two commands used by the shellcode in Stage 3 identified during the analysis.
Stage 1: Execution and persistence
This analysis starts with a zip file called “Biography of Senator the Hon Don Farrell.zip”. As mentioned in the previous article, Hon Don Farrell is the current Secretary of State for Trade and Tourism of Australia.
The zip file contains two files. On one hand, there is a legitimate application for processing PDF files called Solid PDF Creator, renamed as “Biography of Senator the Hon Don Farrell/Biography of Senator the Hon Don Farrell.exe”. On the other hand, we have identified a malicious payload named SolidPDFCreator.dll. The artifact is executed using the Dll Side Loading technique.
File | Hash |
---|---|
Biography of Senator the Hon Don Farrell/Biography of Senator the Hon Don Farrell.exe | e2acbc36c2cce4050e34033c12f766fea58b4196d84cf40e979fac8fed24c942 |
SolidPDFCreator.dll | 3c4671b4a0c3e7da186bd356e07cf0daca7267addde668044b1ded42c6dbe09b |
Once executed, the payload will check if it is located in the path “C:\Users\Public\Libraries\PhotoTvRHD“. If not, it will create the folder and copy the contents of the zip file into it.
Furthermore, the following command is executed, which adds persistence, copies the DLL to the correct path, and creates a scheduled task that executes the payload every minute in that location.
C:\Windows\SysWOW64\cmd.exe /C copy SolidPDFCreator.dll C:\Users\Public\Libraries\PhotoTvRHD\SolidPDFCreator.dll & reg add “HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Run” /v SolidPDF /t reg_sz /d “C:\Users\Public\Libraries\PhotoTvRHD\SolidPDFCreator.exe” /F & schtasks /F /Create /TN SolidPDF /SC minute /MO 1 /TR C:\Users\Public\Libraries\PhotoTvRHD\SolidPDFCreator.exe |
In the event that it is located in the correct path, the payload will decrypt a shellcode and execute it. During the realization of this investigation, this shellcode has not been found published.
Hash of the first shellcode (used during stage 2) |
---|
8059E00CFB899B5BCDE4607AFB6858A922C71AEA1D744EFBE93D922868C34489 |
Stage 2: Downloader disguised as Microsoft update
The shellcode employs well-known anti-analysis techniques, such as using the ror13 hashing algorithm to resolve DLLs and functions.
Moreover, it uses the kernel32 functions GetProcAddress and LoadLibraryA, and gets the addresses for a list of functions listed at the end of this post.
The shellcode will create a socket to port 80 of the malicious IP address 123.253.35[.]231. It will then send an HTTP request that uses legitimate headers, attempting to impersonate common Microsoft update communications.
The body of the message contains a base64-encoded string. It uses a protocol identical to the one seen in the TrendMicro report. It is as follows:
17 03 03 + <payload size> + <payload> |
The above protocol will be present in all base64-encoded strings exchanged between the victim and the C2. In this particular request, the payload being sent contains the following information.
<tick count> + <computer name> + <username> |
Next, the payload content will be encrypted with the following key:
78 5a 12 4d 75 14 14 11 6c 02 71 15 5a 73 05 08 70 14 65 3b 64 42 22 23 20 00 00 00 00 00 00 00 |
It should be noted that although this key is identical to the one used in the Talos report, the algorithm used is different. An implementation of the protocol in Python can be found at the end of this post. In this case the algorithm used for encryption is based on applying XOR four times. In this algorithm, the XOR operation is used to encrypt the data in the “original” array with the data in the “key” array in a four-step iterative process. Each step is performed using a different position in the “key” array, making it harder to decipher.
Note that the previous algorithm has some similarity with the RC4 encryption, which is also a symmetric stream cipher. Both ciphers use the XOR operation to combine the data with a sequence of bytes generated from a shared secret key.
However, there are some significant differences between the algorithm analyzed in this report and the RC4 encryption. In the algorithm under consideration, four iterative cycles are used to combine the data with the secret key, while in the RC4 encryption, a single iterative cycle is used. Additionally, RC4 encryption is a stream cipher algorithm that generates a pseudo-random byte sequence using an internal state table, while in the algorithm you shared, the byte sequence is obtained directly from the secret key.
Using the above information, the C2 will return a second shellcode.
During the realization of this investigation, this shellcode has not been found published.
Hash of the second shellcode (used during stage 3) |
---|
E184F099957C27A04ADF2F5F46772D0FD86B27084CBDDCDEAC5EB7CFA23604F7 |
Stage 3: Communication with the C2
This second shellcode is very similar to the first one and also uses the same hashing algorithm to resolve functions.
Analogous to the TrendMicro report, this variant has several functionalities.
Most of the commands identified correspond to those listed in this TrendMicro report from March, with the exception of 0x05 (keep alive) and 0x20 (close shell). The following table summarises a list of commands used by the shellcode.
Value | Description | TrendMicro report |
---|---|---|
0x05 | Keep Alive* | – |
0x03 | Unknown | Unknown |
0x01 | Unknown | Unknown |
0x1b | Begin upload | UploadBegin error: %d! |
0x1D | Data upload | UploadData error:%d! |
0x1A | Unknown | Unknown |
0x1E | cmd start | CmdStart error:%d! |
0x1F | cmd write | CmdWrite error:%d! |
0x30 | cmd read | CmdWrite error:%d! |
0x20 | Close shell* | Unknown |
The shellcode is used to obtain a shell on the victim’s computer. It uses pipes to read and write commands from it. To achieve this, it uses the CreatePipe function.
Unlike the previous stage, the shellcode no longer impersonates www.asia.microsoft.com. Instead it uses www.download.windowsupdate.com.
On each request the last 25 characters of the URL are changed to random characters using a call to CryptGenRandom.
Conclusions
Since there were already different articles that extensively detailed similar malware to that used in the targeted campaign, it was desired to verify if the malware samples used in the new campaign significantly varied their operation compared to the previous reports. During the analysis, it was observed that the malware authors have changed the encryption algorithm and some aspects slightly, without drastically modifying the malware or improving its operational security. This has several implications, but at least the most immediate ones are: (1) cybercriminal groups can still cause impact with samples that should be identified by detection systems, and (2) sometimes slight modifications are enough to make detection and analysis more complicated. Through this post, we aim to minimize the second point.
Summary of functions used in the shellcodes
First shellcode (stage 2)
Kernel32 | User32 | Ws2_32 | advapi |
VirtualAlloc | MessageBoxA | WSAStartup | GetUserNameA |
VirtualProtect | wsprintfA | gethostbyname | |
VirtualFree | inet_ntoa | ||
Sleep | WSAGetLastError | ||
GetComputerNameA | socket | ||
GetVolumeInformationA | closesocket | ||
GetTickCount | shutdown | ||
htons | |||
inet_addr | |||
recv | |||
send | |||
connect | |||
setsockopt |
Second shellcode (stage 3)
Kernel32 | User32 | Ws2_32 | advapi |
CloseHandle | MessageBoxA | WSAStartup | CryptAcquireContextA |
GetLastError | MessageBoxW | gethostbyname | CryptGenRandom |
VirtualAlloc | wsprintfA | inet_ntoa | CryptReleaseContext |
VirtualFree | wsprintfW | WSAGetLastError | RegOpenKeyExA |
VirtualAllocEx | socket | RegQueryValueExA | |
VirtualFreeEx | closesocket | GetUserNameA | |
VirtualProtect | shutdown | ||
Sleep | htons | ||
CreateMutexA | inet_addr | ||
OpenMutexA | recv | ||
WriteProcessMemory | send | ||
CreateFileA | connect | ||
CreateFileW | setsockopt | ||
GetFileSize | |||
SetFilePointerEx | |||
ReadFile | |||
WriteFile | |||
DeleteFileA | |||
DeleteFileW | |||
GetFileAttributesA | |||
GetFileAttributesW | |||
GetModuleFileNameA | |||
GetModuleFileNameW | |||
GetModuleHandleW | |||
CreateToolhelp32Snapshot | |||
Process32FirstW | |||
Process32NextW | |||
OpenProcess | |||
CreateProcessA | |||
CreateProcessW | |||
CreateThread | |||
CreateRemoteThread | |||
WaitForSingleObject | |||
GetDriveTypeA | |||
GetDiskFreeSpaceExA | |||
GetLogicalDriveStringsA | |||
GetNativeSystemInfo | |||
GetComputerNameA | |||
GlobalMemoryStatusEx | |||
Wow64DisableWow64FsRedirection | |||
Wow64RevertWow64FsRedirection | |||
OutputDebugStringA | |||
OutputDebugStringW | |||
TerminateProcess | |||
LocalAlloc | |||
LocalFree | |||
lstrcmpW | |||
CreatePipe | |||
PeekNamedPipe |
Indicators of Compromise (IOCs)
Hash | Description | Access |
---|---|---|
e2acbc36c2cce4050e34033c12f766fea58b4196d84cf40e979fac8fed24c942 | Legitimate exe | Public |
3c4671b4a0c3e7da186bd356e07cf0daca7267addde668044b1ded42c6dbe09b | Malicious dll | Public |
8059E00CFB899B5BCDE4607AFB6858A922C71AEA1D744EFBE93D922868C34489 | First Shellcode | Private |
E184F099957C27A04ADF2F5F46772D0FD86B27084CBDDCDEAC5EB7CFA23604F7 | Second Shellcode | Private |
Domains | Stage |
---|---|
www.asia.microsoft.com | Stage 1 |
www.download.windowsupdate.com | Stage 2 |
Python helper functions for protocol decryption and encryption
import base64
import struct
key=bytearray([0x78, 0x5a, 0x12, 0x4d, 0x75, 0x14, 0x14, 0x11, 0x6c, 0x02, 0x71, 0x15, 0x5a, 0x73, 0x05, 0x08, 0x70, 0x14, 0x65, 0x3b, 0x64, 0x42, 0x22, 0x23, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00])
def cipher_xor(message, key):
key_length = len(key)
length = len(message)
for i in range(length):
message[i] ^= key[(i + 1) % key_length] ^ 0xff
for i in range(length):
message[i] ^= key[(i + 9) % key_length] ^ 0xff
for i in range(length):
message[i] ^= key[(i + 4) % key_length] ^ 0xff
for i in range(length):
message[i] ^= key[i % key_length] ^ 0xff
return message
def decipher_payload(message):
msg=bytearray(base64.b64decode(message))[5:]
return cipher_xor(msg,key)
def cipher_payload(message):
header=bytearray([0x17, 0x03, 0x03])
size=len(message)
return base64.b64encode(header + struct.pack('>H',size) + cipher_xor(bytearray(message),key))
Leave a Reply