Predator the Thief is a sophisticated malicious stealer which has been on the scene for around one and a half years. What started as coding experiments in malware development later evolved into a full-fledged menace to be reckoned with. Current versions of Predator use various anti-debugging and anti-analysis techniques to complicate analysis on the part of researchers while still smoothly performing data stealing.
In this article we provide a retrospective analysis of the evolution process for Predator the Thief, journey into the Darknet and its malware sales, take peeks at the data left behind by the malware authors, and describe the malware itself.
Predator the Thief was initially offered for sale on a Russian Darknet forum by Alexuiop1337 more than 1.5 years ago, on June 17, 2018.
It is no longer available there, as this forum states it doesn’t deal with malicious tools anymore:
Screenshot from forum with question about Predator the Thief thread and response stating that selling malware is now forbidden
This was the initial sale offer for Predator:
And some details on the offer:
We have counted at least 5 different versions since then. While it may seem a low number, each version added something significant to the malware and the author is still continuing to enhance its capabilities.
Currently Predator is offered on another Russian forum as well as on the Telegram channel. Its price is $150, plus an additional $100 may be paid for the Clipper module which allows the buyer to customize stealing options for crypto-wallets.
At the end of September 2019, this channel had only 773 subscribers. In a single month, the number of subscribers increased by one hundred, with a total of 875 subscribers to Telegram channel as of October 30.
Development of the malware is ongoing and updates to Predator are posted regularly.
This is the transcript of the Telegram message which describes changes in Predator v.3.3.0, the sample we obtained:
Update v.3.3.0 as of 19.08.2019:
Module Clipper
* new module clipper, price is $100
Module Loader
* added option “random filename” for methods ShellExecute and CreateProcess
Panel
* “Domain Detect” for cookies was re-written with AJAX and now works realtime as well as passwords
* changed log view
* separated filters by general parameters, passwords and cookies
* fixed bug with transferring logs to other users
* fixed bug with wrong loader statistics
Stealer:
* temprorary removed support of *.onion domains
* completely changed Outlook harvesting
* implemented harvesting of new cold wallets and fixed paths for harvesting of old ones
* mail client ThunderBird is now displayed correctly in passwords file
* possibility to add one reserve domain to the build
* other minor fixes and tweaks
Peculiar thing is that author uses this channel not only for pure advertisement, but for publications on general topics as well. For example, there is a post on anti-disassembly tricks and their implementation is C/C++ code. Some methods which are implemented in Predator to harden the analysis are described in this article. Sources used in this publication reside in author’s GitHub
https://github.com/Alexuiop1337/AntiDisassembly
It appears that the author puts some love in his creation as well as in what he’s doing overall.
We were able to obtain Predator the Thief panel source code which helped us to gather more information.
Panel interface looks like this:
It is in Russian which once again proves its origin.
The author of certain panel scripts identifies himself as “melloy”:
* author of this script Melloy
Panel is written in PHP while malware is written in C++. There is nothing controversy in the ability to write code in both languages, however we can’t say for sure that the author of Predator and the author of its panel is the same person.
The table below contains resources which are linked with the authors of Predator the Thief.
Description | Link | Where this lead was got from |
GitHub | https://github.com/Alexuiop1337 | Darknet and Telegram channel |
Predator Beta (C#) | https://github.com/Alexuiop1337/PredatorTT-Beta-Old | |
https://twitter.com/alexupi1 | ||
VK | https://vk.com/alexinde | Panel source code |
Judging by avatars in Twitter and VK, we can say that people (or a single person) behind the accounts like anime girls:
Avatar of the malware author in Twitter
Page of the malware author in VK
We can’t say for sure that both malware and panel were written by the same person though certain patterns from these leads match:
The entire malicious job is conducted in the address space of original executable. Several layers of unpacking and XOR-decryption are performed until main payload is reached.
Malware steals data of various browsers, email clients, crypto-wallets and other software. It also takes desktop screenshot. This information is stored in ZIP archive which resides in memory and then sent to C2C.
It steals credentials from various applications:
Browser and email clients | Chrome based browsers |
Firefox based browsers | |
Opera | |
Outlook | |
Thunderbird | |
Crypto-wallets
(any crypto-system which stores `wallet.dat` is affected) |
Armory |
Atomic | |
Bytecoin | |
Electrum | |
Ethereum | |
Jaxx | |
MultiBit | |
Other software and accounts | Authy |
BattleNet | |
Discord | |
Jabber | |
NordVPN | |
Osu | |
Pidgin | |
Skype | |
Steam | |
Telegram | |
WinFTP | |
WinSCP |
It also gets system information (clipboard included) in the following manner:
As a final touch, Predator the Thief grabs the list of installed software and takes desktop screenshot.
The data is archived and is organized like shown in the following screenshot:
Some recent versions of Predator the Thief create mutex with name transformed from disk volume serial number.
Predator the Thief implements various techniques to harden the analysis.
Current thread is hidden from debugger with the call of NtSetThreadInformation
and HideFromDebugger enum argument (11h):
In order to bypass this detection method, we have to skip the call and return 0 (STATUS_SUCCESS) as a result.
Here lies another trap. There is another call right after this call. This time with slightly different arguments:
Note the difference in 3rd and 4th arguments which should be set to NULLs in order for the function to be executed correctly. If wrong arguments are passed, then 0xC0000004 error code is returned. And this is precisely what malware expects from the 2nd call: it is waiting for the error code to be returned. Thread will not be hidden from debugger in this case.
If researcher gives up to temptation of skipping this 2nd call to NtSetThreadInformation function with wrong arguments and will set result to 0, it will lead malware through another branch – which will cause termination instead of correct execution.
To sum it up: 1st call must be skipped. 2nd call must be executed as is, or result must be set to non-zero value.
This check may be faked in many ways, via plugins (ScyllaHide, OllyAdvanced) or manually. Either way, in order to continue malware debugging, result of this function must be set to 0.
Consider this code sample:
This code is executed differently depending on whether debugger is present or not. Malware uses this method to check if debugger was detected: it checks if exception handler was executed and that 1 was returned from it. If all the conditions fulfill, malware terminates its execution.
When exception handler returns 1 – which corresponds to EXCEPTION_EXECUTE_HANDLER – it is a sign that exception has been handled, as described here:
https://docs.microsoft.com/en-us/cpp/cpp/try-except-statement?view=vs-2019
To bypass this check method we have to set return value from exception handler to 0:
Malware pollutes the API call analysis with a lot of non-meaningful calls:
This is done 28878 times:
This is also true for meaningful operations where the same actions are performed several times to make the log larger:
Malware terminates its execution if successfully loads one of the following DLLs:
`ActaRes.dll` and `SavUIRes.dll` are both libraries from Symantec Endpoint Protection.
Our guess about `taleOfTheWorstOne.dll` is that this library was used as a kill-switch during malware development.
All the key strings are obfuscated with the help of one of the following instructions:
They are put on stack before decryption. It looks like this:
When calling a function, malware first decrypts names of function and corresponding DLL. Then it refers to GetProcAddress wrapper and makes the call afterwards. It looks like this (decryption is not shown here):
This method was described by the author of Predator in Telegram channel with source code residing on GitHub:
https://github.com/Alexuiop1337/AntiDisassembly
Below we will show how to bypass it during research.
Consider this code in debugger:
Jump is taken in any case and leads to the middle of instruction. The following code is the right one:
Here is the code representation in IDA:
Outlined in red are fake instructions. When attempting to create a function in IDA for this case, an error is shown stating that wrong instructions are encountered.
Replacing the fake function call with NOP is the way to go in such a case so that the function will be created indeed:
These checks are performed only if corresponding command is received from the server.
This trick involves looking at the pointers to critical operating system tables that are typically relocated on a virtual machine. It’s what called “Red Pill” and was first introduced by Joanna Rutkowska:
http://web.archive.org/web/20070325211649/http://www.invisiblethings.org/papers/redpill.html
There is one Local Descriptor Table Register (LDTR), one Global Descriptor Table Register (GDTR), and one Interrupt Descriptor Table Register (IDTR) per CPU. They have to be moved to a different location when a guest operating system is running to avoid conflicts with the host.
On real machines, for example, the IDT is located lower in memory than it is on guest (i.e., virtual) machines.
Note: does not work on newer versions of VMWare Workstation (tested in v10 and v12).
Three instructions are used to check locations of these tables:
Predator the Thief uses only two of them: sidt and sgdt.
Code sample:
idt_vm_detect = ((get_idt_base() >> 24) == 0xff);
ldt_vm_detect = (get_ldt_base() == 0xdead0000);
gdt_vm_detect = ((get_gdt_base >> 24) == 0xff);
// sidt instruction stores the contents of the IDT Register (the IDTR which points to the IDT) in a processor register.
ULONG get_idt_base() {
UCHAR idtr[6];
#if defined (ENV32BIT)
_asm sidt idtr;
#endif
return *((unsigned long *)&idtr[2]);
}
// sldt instruction stores the contents of the LDT Register (the LDTR which points to the LDT) in a processor register.
ULONG get_ldt_base() {
UCHAR ldtr[5] = "\xef\xbe\xad\xde";
#if defined (ENV32BIT)
_asm sldt ldtr;
#endif
return *((unsigned long *)&ldtr[0]);
}
// sgdt instruction stores the contents of the GDT Register (the GDTR which points to the GDT) in a processor register.
ULONG get_gdt_base() {
UCHAR gdtr[6];
#if defined (ENV32BIT)
_asm sgdt gdtr;
#endif
return gdt = *((unsigned long *)&gdtr[2]);
}
This technique uses `cpuid` instruction to check if malware is run in VM. EAX is set to 1, ECX is set to 0, then `cpuid` instruction is called. If run in VM, 31st bit in ECX is set to 1. Malware checks exactly this bit.
This technique uses the smsw instruction which is described in the Intel Software developer’s manual:
Store machine status word in low-order 16 bits of r32/m16; high-order 16 bits of r32 are undefined.
The key here is the undefined high order bits. It has previously been observed that on Intel processors the return value of top 16 bits is consistently 0x8001, while on a virtualized CPU in VMware the target register contains the value preserved before the instruction was executed.
When using this technique, first the target register is initialized with a “magic” value, then smsw is executed. If after execution the target register still contains the “magic” value, the application is treated as if it’s running inside a virtual machine.
This technique doesn’t work in VMware workstation 12.
Predator the Thief initiates two connections to the hardcoded C2C address. First it sends a warm welcome gets configuration file, then it sends stolen data in ZIP archive.
Config arrives in a classic Base64+RC4 encryption layer:
Please find the decryption script in the `Attachments ` section.
The following five configuration entries are stored in encrypted form:
1 – settings for stealing in this order (for Boolean value: 1 – feature is on; 0 – feature is off):
1 | Take webcam snapshot (-3f4) |
2 | Check VM (-3f3) |
3 | Steal Skype |
4 | Steal Steam (-3f1) |
5 | Take screenshot (-3f0) |
6 | No log if CIS (-3e8) |
7 | Self-delete (-3e7) |
8 | Steal Telegram (-3e6) |
9 | Steal Windows cookies |
10 | Max quantity of stolen items |
2 – directories where to steal files from and appropriate file masks
3 – host location info
4 – empty field. According to what we have seen on current site, it’s a place for loader module.
5 – contact server for an additional module (in this case: HTTP POST /api/clipper.get). Clipper is a module for grabbing additional crypto-wallets with custom settings.
Note: self-deletion is performed with the help of batch command “/c ping 127.0.0.1 && del [filename]”.
Example of these values is listed below:
Logical part | Value |
1 | [0;0;0;0;1;0;1;0;0;1000] |
2 | [[%userprofile%\Desktop|%userprofile%\Downloads|%userprofile%\Documents;*.txt,*.png,*.jpg,*.jpeg,;1000;;1]] |
3 | [Montreal;Canada;45.5029;-73.5723;54.39.186.180;America/Toronto;H4] |
4 | [] |
5 | [Clipper] |
At this step stolen data is sent to C2C server in ZIP archive:
“p” parameters in the request indicate how many items of certain have been stolen, in this example:
p1=0 & p2=243 & p3=0 & p4=0 & p5=0 & p6=0 & p7=0 & p8=0 & p9=0
And the same information in `Information.txt` file:
Check `What it is targeting` section for what is stored in the archive.
After sending the archive malware terminates its execution.
Predator is actively evolving; here are the descriptions of changes in versions 3.3.1 and 3.3.2 taken from its official Telegram channel:
* screenshot from changes in version 3.3.1
Predator the Thief update v.3.3.1
Comeback of onion domains
Now build can send data to onion domains so that chance of your server ban is really low.
* build sends info directly without using proxies and the like
** there is no guarantee for the same receive rate as for usual domain. Rate may be lower for onion domain
*** admin panel in TOR doesn’t eliminate chance of server ban but minimizes it to zero
Panel update
Module update: loader
Module update: stealer
Changes in project terms
Screenshot with changes in version 3.3.2 follows:
Predator the Thief update v.3.3.2
Stealer update
First publicly known research was conducted by fumik0_:
https://fumik0.com/2018/10/15/predator-the-thief-in-depth-analysis-v2-3-5/
It was v.2.3.5, year 2018. fumik0_ has also covered v.2.3.7.
Since then Predator has evolved and different versions were covered by more researches:
https://securelist.com/a-predatory-tale/89779/
https://www.fortinet.com/blog/threat-research/predator-the-thief-new-routes-delivery.html
There are quite a few differences between current sample (v.3.3.0) and the ones researched previously. We will list them as bullet points:
Mutex name generation algorithm has completely changed
Though not among the most prevalent malware’s nowadays, Predator the Thief has all the possibilities to become one of them.
The level of dedication that the author puts into his creation promises even more danger from this stealer in the future. Regular updates, evasions, anti-debug techniques, its capabilities, the fact that it can be easily used by thread actors without a lot of technical knowledge – all of these points make Predator a rising star player on malware stealer market, the one that should be definitely reckoned with.
MD5 | SHA1 |
3cb386716d7b90b4dca1610afbd5b146 | 6e7c5d252c3836eff17a3ad9bf69b8d4be4b81a1 |
cbcc48fe0fa0fd30cb4c088fae582118 | b1114fe6add1b570d16822a80678a0c7bef91795 |
c44920c419a21e07d753ed607fb6d7ca | 28dd84fd59868bf2bacfa49d7c5aa29cd1558e61 |
cf2273b943edd0752a09e90f45958c85 | 7df2f80abd86898c9befe482ce558541fa5d4efb |
b2cbb3d80c8d830a3b3c2bd568ba1826 | c8f3171868b065dcb3af82c9813a35cefa6928e6 |
dff67a78bb4866f9da5a0c1781ed5348 | 7d9aa5ca823cd77430063a1f92b737722ee0f05a |
25f9ec882eac441d4852f92e0eab8595 | 3753d1c51cf9612f50817165bbbdca5951e736fd |
052ef78b897f555cd79805544e59746e | 7c69ca83d9f5a206326562fdf190e444269d2485 |
b380e0abd3c9515a23cc0ed5a25bd4b9 | e7bfd515ac0a0df4e80b43485b0b91ed62e63349 |
Domain |
kent-adam.myjino[.]ru |
denbaliberdin.myjino[.]ru |
15charliescene15.myjino[.]ru |
axixaxaxu1337[.]us |
madoko.jhfree[.]net |
kristihack46.myjino[.]ru |
j946104.myjino[.]ru |
sayhello[.]host |
u96191l2.beget[.]tech |
www.haijiangfriut[.]com |
btcinvest[.]company |
import struct
import win32apidisk_num = 0
res = win32api.GetVolumeInformation(‘c:\\’)
if len(res) != 5:
print ‘Something went wrong with API function’
raise Exception
disk_num = res[1]
print ‘Volume serial number (hex): %#08x’ % struct.unpack(‘<I’, struct.pack(‘<i’, disk_num))[0]
init_value = (disk_num * 5) & 0xFFFFFFFF
print ‘Initial value (dec): %d’ % init_value
init_string = str(init_value)
transformed_string = init_string + init_string[2:]
print ‘String value: %s’ % transformed_string
mutex_name = ”
for i in range(0, len(transformed_string)):
mutex_name += transformed_string[i] if i % 2 == 1 else chr(ord(transformed_string[i]) + 0x40)
print ‘Mutex name: %s’ % mutex_name