Packer: absent
Compilation date: 2020-08-13
- SHA1 hash: a8bff99e1ea76d3de660ffdbd78ad04f81a8c659
Description
The PlugX backdoor module is written in C and is designed to decrypt the shellcode from the registry that loads the main backdoor into memory.
Operating routine
At the beginning of the work, the backdoor receives the address of the VirtualProtect() function by hash, which it uses to change access rights to PAGE_EXECUTE_READWRITE, starting from the function at 0x10001000 and ending with the entire .text section:
The function of getting the address of the function by the hash passed as a parameter:
Script to get a function by hash:
import struct
[code]ror = lambda val, r_bits, max_bits: \
((val & (2**max_bits-1)) >> r_bits%max_bits) | \
(val << (max_bits-(r_bits%max_bits)) & (2**max_bits-1))
max_bits = 32
library_path_list = [...] # absolute path dlls
def get_func_addr(hash):
for library_path in library_path_list:
library = library_path.split('\\')
name_dll = library[len(library) - 1].upper() + b'\x00'
hash_name_dll = 0
for i in name_dll:
hash_name_dll = ord(i) + ror(hash_name_dll, 0x0D, max_bits)
hash_name_dll = 0 + ror(hash_name_dll, 0x0D, max_bits)
pe = pefile.PE(library_path)
for exp in pe.DIRECTORY_ENTRY_EXPORT.symbols:
func_name = exp.name + b'\x00'
hash_name_func = 0
for i in func_name:
hash_name_func = ord(i) + ror(hash_name_func, 0x0D, max_bits)
if (hash_name_dll + hash_name_func == hash):
print '{}-> 0x{:08x} -> {}'.format(name_dll, hash, exp.name)
return
Changing the permissions to PAGE_EXECUTE_READWRITE was necessary in order to decrypt the code using the XOR operation:
There is also a version of the backdoor with dynamic XOR encryption. With decryption at the beginning of the function:
And with encryption at the end of the function:
Facilitating the work of the script for IDAPython:
import idaapi
def xor_dec(address, count, key):
for i in xrange(count):
idaapi.patch_dword(address, idaapi.get_dword(address) ^ key)
key += idaapi.get_dword(address)
address += 4
Before starting to perform malicious actions, the backdoor, as in the case of VirtualProtect(), receives the addresses of other functions it needs to work:
Received features:
Function name | Hash |
---|---|
CloseHandle | 0x528796C6 |
CreateFileA | 0x4FDAF6DA |
DeleteFileA | 0x13DD2ED7 |
ExitProcess | 0x56A2B5F0 |
GetAdaptersInfo | 0x62C9E1BD |
GetModuleFileNameA | 0xFE61445D |
GetSystemDirectoryA | 0x60BCDE05 |
LoadLibraryA | 0x726774C |
ReadFile | 0xBB5F9EAD |
RegCloseKey | 0x81C2AC44 |
RegDeleteValueA | 0x3846A3A8 |
RegEnumValueA | 0x2EC95AA4 |
RegOpenKeyExA | 0x3E9E3F88 |
RegQueryValueExA | 0x8FF0E305 |
VirtualAlloc | 0xE553A458 |
VirtualFree | 0x300F2F0B |
VirtualProtect | 0xC38AE110 |
WinExec | 0x876F8B31 |
WriteFile | 0x5BAE572D |
In addition, the backdoor checks if it is executed in a sandbox:
After receiving the function addresses and checking for execution in the sandbox, BackDoor.PlugX.93 removes the updatecfgSetup task from the task scheduler:
The key for shellcode encryption is MD5 from the following registry key values:
HKLM\Software\Microsoft\Windows NT\CurrentVersion\InstallDate
HKLM\System\ControlSet001\Control\ComputerName\ComputerName
The shellcode is stored in the following registry keys:
HKLM\Software\BINARY
HKLM\Software\BINARY
Before running the shellcode, it will be decrypted in 2 steps - first, using the RC4 algorithm:
And then with XOR: