Updates on Quickly-Evolving ThiefQuest macOS Malware
We discuss our discoveries on ThiefQuest, such as the differences between the old and new versions of the malware, and why we believe ThiefQuest is an example of highly capable malware that should be kept under close monitoring.
Save to Folio
Right as July of this year began, we noticed an emerging malware dubbed by most as ThiefQuest (also known as EvilQuest), a threat that targets macOS devices, encrypts files, and installs keyloggers in affected systems. It has been found in pirated versions of macOS shared on popular torrent sites. Developments on the malware have been reported by MalwareBytes, BleepingComputer and security researchers Dinesh Devadoss, Phil Stokes, Patrick Wardle, and Thomas Reed.
The aforementioned reports state the assumption that the malware’s ransomware activity is not its main attack method; rather, it is a pre-emptive move to disguise its other capabilities such as file exfiltration, Command and Control (C&C) communication, and keylogging. This assumption is also supported by our recent discoveries.
Given that both the previously mentioned researchers and the updated report from Objective-See have conducted an in-depth look into the malware, in this blog post we will discuss our own discoveries such as the differences between the old and new versions of the malware, including unusual observations in VirusTotal. More importantly, we’d like to add to the current information provided by published reports that prove our belief that ThiefQuest is an example of highly capable malware that should be kept under close monitoring.
New ThiefQuest variants
Besides the old ThiefQuest variant that has been reported by various researchers, we also discovered some improved variants with stronger capabilities and other changes compared with earlier iterations of the malware. For instance, these new variants seem to emerge only days after the detection of older variants. Notably, previously encountered ransomware behavior, such as file encryption and ransom note dropping, have been removed.
These new updates are not called by the main code of the malware, and through further investigation, we discovered that the authors have implemented a new routine for computing and calling the new functions’ addresses. Other versions of these new variants have even obfuscated the function names to make malware tracing more difficult.
The following are the new functions, some of which will be discussed:
- fb_uniconf_* (other related functions)
Payload reading and attaching
The extract_payload() function loads the embedded (and encoded) payload data from the specified file, where the offset and length of its data are saved at the end of the file. After reading the data, it calls eib_secure_decode to decode the payload data.
The attach_payload() function is the opposite to extract_payload(). It reads payload data from a specified source file, encodes them, and saves the encoded data to a specified target file.
Bundle compression and decompression
The compress_bundle() function encodes the contents of each file in a bundle and saves them to a specified file. On the other hand, the decompress_bundle() function is the opposite of compresss_bundle(). It loads and decodes bundle files from a specified file.
C&C IP generation
The ei_rfind_cnc() function uses the current time as a seed for random number initialization in a 1000-counter loop. It calls ei_getip() to generate an IP address with the generated random number and tries to connect to it via http_request(). If it can be reached, it will then be used as the C&C server address.
Improved anti-analysis techniques
In the function is_virtual_mchn(), condition checks including getting the MAC address, CPU count, and physical memory of the machine, have been increased.
In its string decryption function eip_str(), anti-analysis checks have also been added. One of these checks is eisl_debugging_um(), a new function that calls task_get_exception_ports() to check if the current process is being debugged. However, it seems that it does not fully work yet since the functions always return 0.
We also found several new functions that are used for anti-analysis; however, a few of these functions are still empty. We suspect that these will be populated soon:
The function _react_updatesettings() has been added as well. This is used for getting updated settings from the C&C server.
Ability to run image and sound files
Meanwhile, run_audio and run_image are new functions that are meant to save a target file into a hidden .m4a sound file or .jpg image file respectively. These functions would then be run through a hidden opened terminal. The malware simply calls “open.filename.m4a” or “open.filename.jpg” to play it with default applications associated with either, such as Music.app or Preview.app.
With these two functions, threat actors behind ThiefQuest may be preparing for new features of the malware. Possibly, the group is planning for ThiefQuest to have a similar concept to the previous version that uses text-to-speech to read its dropped ransom note.
The next image shows the disassembly of the run_audio function. It displays the filename that the target will be saved as and the encrypted strings (decrypted as an AppleScript command for launching a hidden terminal) for running them.
More security tools terminated
Aside from the tweet by user @Myrtus0x0, which states that ObjectiveSee’s KnockKnock solution has been added to the list of security tools running in the system to check and terminate, we also discovered that a few other security vendors have made it to this updated list:
- Little Snitch
Changes in file name and server
The dropped file name, persistence item PLIST file name, and connected server’s subdomain name of both previous and new variants have also been changed.
|Primary Executable file path
|Persistent item plist file path
Analyzing samples from VirusTotal
Data from VirusTotal submissions for the first versions of the malware shows that ThiefQuest had already been lurking since early to mid-June. These older samples don’t exhibit as many features as newer ones; additionally, we did notice some gradual changes in them that demonstrate the malware author’s efforts to continuously improve ThiefQuest.
One notable characteristic of the early versions is their lack of ransomware capability. In fact, ThiefQuest was initially a backdoor with the capability to modify the victim’s hosts file(/private/etc/hosts). In one of these earlier samples, such as effeeeadfdc3caf523635fcb86581a807f719fa5e322872854499, we observed it adding entries for certain domains to redirect to the C&C server. The following are some entries for hosts file modification:
The file path of the submission to VirusTotal contains /Users/user1 and country code RU (referring to Russia). Another submission name, with the country code BG (referring to Bulgaria), also contains the notable com.apple.questd.
The first ransomware version
In the beginning, we identified an older variant that wasn’t as comprehensive as the samples analyzed by other reports. This variant had no viral infector routines, and certain C&C tasks had no functioning code yet. However, it did demonstrate ransomware behavior.
Observations from infected samples
Given the viral infector routine of later samples, we checked VirusTotal Intelligence using the similar-to condition and found the results of many samples.
From June 29 to July 3, there were more than 30,000 similar new samples submitted to VirusTotal. Most of them come from the same API submission with country code ZZ, which means that the country where the submission originated from is unknown.
The folder /Users/user1 is the same one used in older samples, which indicates that these samples are from the same machine where the older samples came from. In an estimated five-minute period, the same file path “/Users/user1/Library/Google/GoogleSoftwareUpdate/GoogleSoftware.bundle/Contents/Helpers/crashpad_handler” was submitted two times and file size increased from 16.27MB to 32.09MB. On our testing machine, the size of the file is only 499,264 bytes, or less than 500KB.
After analyzing the sample with 32.09 MB file size, here are some findings:
- a) When searched, the unique string “/toidievitceffe/libpersist/rennur.c” appears for 366 times. This means that the file is infected repeatedly by the sample for at least 366 times.
- b) Dumping the last Mach-O file at the end of the file resulted in confirming that the file is the crashpad_handler.exe. This is the same file that exists in our clean machine.
- c) There were some incomplete copies of the sample in the file, which might have been caused by improper handling of multiple infection instances that were being run at the same time or other potential issues.
- d) The malware allows multiple instances of malicious code to be run at the same time in one system.
- e) Infected samples can reinfect themselves.
- f) Although it avoids infecting Mach-O files that are inside app bundles, the malware still infects files in ~/Library/Google/GoogleSoftwareUpdate/GoogleSoftwareUpdate.bundle/, which is similar to an app bundle folder.
Generally, these processes are not usually included by experienced malware authors when they produce a file infector malware.
Generations of ThiefQuest
We recorded the preceding changes based on the infector samples we sourced. The following is the outline of the malware’s evolution:
|Date First Seen
Backdoor capability was first implemented here
hxxp://andrewka6[.]pythonanywhere[.]com/ret[.]txt and 167[.]71[.]237[.]219 were both blocked and categorized as C&C server
Ransomware capability first implemented
File name is /Library/LiveSupport/CrashReporter
File infector capability first implemented
File exfiltration implemented
Ransomware capability existed
File infector capability existed
Ransomware capability removed
hxxp://lemareste[.]pythonanywhere[.]com/cfgr[.]txt was blocked and categorized as C&C server
The malware also downloads certain files, such as Python dependencies and two particular files, p.gif and pct.gif. BleepingComputer has uploaded the raw text of p.gif, revealing how heavily-obfuscated it is.
Based on this, we assume that the malware authors used nested Lambda function abused from a tool to make the script difficult to read. In researching further on the structure of this nested Lambda obfuscation style, we came across a tool developed by Chelsea Voss and a team that converts any Python2 script into a single line via Lambda functions.
The authors used python string obfuscation for the strings. For example, line 3 has the string “‘r%squ%ssts”, while line 28 used the pattern “’r%squ%ssts” %(‘e’,’e’)” . By manually deobfuscating this, the real string is revealed to be “requests”. Another similar string at line 10 reads as “’__b%s%slt%s%ss__’ % (‘u’, ‘i’, ‘i’, ‘n’)”, which is then revealed as “__builtins__”
Using this logic, we also deobfuscated the file on our end, confirming that p.gif is only for installing dependencies.
While Bleeping Computer obtained an earlier version of pct.gif, during our investigation we observed that the malware author updated pct.gif to exhibit the same nested Lambda obfuscation as well.
A more recent version of pct.gif that we uncovered also reveals that a string decryption routine is in place due to the presence of unreadable, encrypted strings. We will update this space once the code has been deciphered.
Additional input for the core features
While much of our investigation of the core components for this malware (especially for its older versions) matches the findings of Objective-See’s Patrick Wardle, we would like to highlight additional information that may prove useful for those who might want a deep dive into the workings of ThiefQuest.
We would like to point out that the malware’s encryption logic branches depending on the size of the target file.
In the core encryption function carve_target(), there are calls to three different branches:
- The first branch targets files with sizes of less than 2MB.
- The second branch targets files with sizes between 2MB and 30MB.
- The third branch targets files with sizes greater than 32MB.
While all the parameters for the three callings are the same, there are certain differences in the second and third branches. For example, the malware limits the number of files to encrypt to 3,000, and if by the second branch it already encrypts 3,000 files, the third branch will then be skipped.
What we find odd in its logic, however, is that if the second branch already encrypts 2,900 samples, the counter for the third branch still starts from 0.
Mach-O File Infection
The function append_ei() is where the routine performs the actual infection. It also adds the original/host file size and magic number at end of infected file as seen in the following figure:
The function pack_trailer(), on the other hand, is used to prepare the trailer data such as the file size of the host file for the infection.
Comparing the original file and infected file in Figure 20 shows the data added. The original malware sample is appended on top of the infected Mach-O file.
The malware was found to exhibit a few differences in behavior depending on whether it is the original malicious sample or an infected file. The following differences have been observed between these two types of samples:
- Some anti-analysis check procedures were not executed on infected samples (__is_debugging, _prevent_trace,_ kill_unwanted).
- The routine in infected samples that drop the original/host code as a hidden file might deceive the user into thinking that the infected executed file was not affected while the malware performs malicious routines in the background.
- When an infected sample is executed, the dropped file .<filename>1 is not removed after the execution.
Observable in the following code snippets is the disassembly that shows the following processes: calling the unpack_trailer, extracting it from the same directory with ‘1’ suffix, and saving it as a hidden file. It also shows the infected sample and the hidden dropped file (assumed normal file).
The main() function of the binary first installs an autorun/persistence mechanism using the functions persist_executable_frombundle(), ei_persistence_main, and install_daemon() to ensure that the malware is running on startup.
Running through this part of the code reveals that the malware installs a launch agent (~Library/LaunchAgents) and launches daemon (Library/LaunchDaemons) as com.apple.questd.plist; this targets another copy of the malware binary ~/Library/AppQuest/com.apple.questd, if certain conditions are met.
The function lfsc_dirlist() is called by the main exfiltration function ei_forensic_thread() where it concatenates all files under the /Users folder into one long string. A check for this string’s length is first performed. If the string is longer than 10,240 characters, it separates the string into 10,240 character-sized blocks which are sent one by one to the server.
After sending, it sleeps for 10 seconds to prevent making the network load too high. This 10-second sleep routine is also observed each time the malware sends exfiltrated data to the server.
C&C communication routines
As reports about the malware only mention the presence and a few features of the C&C server, we would like to share more information on this, especially on these functions:
One of the functions of the C&C server is the _react_exec(). When the malware receives the _react_exec command from the attacker, it will attempt to decode the data and load or run this from the memory.
When unsuccessful, it will write the file into a .xookc hidden file and run it with elevated privileges through AppleScript.
For the _react_save command, the sample decodes the data received from the server into a file, through the function eib_decode(). This file will be saved as the filename that is also included in the encoded data received from the server.
Inside eib_decode() is the final function called eib_unpack_i, which is used for setting the decoded file onto the memory, then for saving as a file.
_react_ping is a command used to decrypt a string received from the server. Once successfully decrypted, the sample sends a message to the server, possibly indicating that it is working and ready to receive more commands from the server.
The binary waits for a response from the C&C server. Depending on the command received, it can initiate a keylogger through _react_keys().
First, it collects user information, such as the user ID of the ransomware binary called and environment path of the HOME variable, and then creates a thread for eilf_rglk_watch_routine() that contains the keylogger function.
In this thread, the routine uses the CGEventTapCreate() function to log and print keystrokes, where one of its parameters, process_event(), is the callback function for converting keystroke into strings to print.
The kconvert() function formats keystroke into strings. All possible button presses are found, including volume up/down/mute and function keys. However, the logged keystrokes are only printed through the console.
Given the questionable keylogger printing and the null functions, we believe that the malware still lacks capabilities for C&C-related tasks. Perhaps the malware author will improve this part, as well as its file encryption and infection routines in later variants.
MITRE ATT&CK Techniques
Based on the information we obtained from investigating both the previous and newer versions, here is the malware’s coverage using MITRE’s Tools, Techniques, and Procedures (TTPs). Entries in orange indicate observed and implemented behavior, while entries in yellow indicate identified, but not fully implemented, code for behavior.
As some variants of ThiefQuest exhibit ransomware capabilities, it is interesting to note that compared with some platforms, fewer ransomware detections are found to affect macOS. The emergence of ThiefQuest might mean that cybercriminals are finding more ways to target macOS with such attacks, or there is a higher interest in targeting the OS in general – or perhaps even both.
There is a misconception that Mac software is secure from malware; however, cybercriminals seek to target software that is used by a large number of people, and macOS is not exempt from such a basis of consideration. For example, besides ransomware, there have been other types of attacks against macOS. Last year, the most detected of these was Shlayer, a trojan that spreads adware and unwanted applications.
Newer variants of ThiefQuest with more capabilities are released within days. Having observed this, we can assume that the threat actors behind the malware still have many plans to improve it. Potentially, they could be preparing to make it an even more vicious threat. In any case, it is certain that these threat actors act fast, whatever their plans. Security researchers should be reminded of this and strive to keep up with the malware’s progress by continuously detecting and blocking whatever ThiefQuest variants cybercriminals come up with.
The threat actors discussed here constantly and quickly update this malware; therefore, security teams and users alike should remain vigilant for any curveballs that this malware could throw at them. To do so, the following actions are recommended:
- Only download applications from trusted sources such as official application stores or download centers.
- In emails, never download attachments or click links from untrusted sources.
- Patch and update software to ensure that vulnerabilities are protected.
The following solutions are also recommended to detect and block threats before they can infiltrate the system:
- Trend Micro™ XDR– gathers and correlates data across multiple vectors – such as email, endpoints, servers, cloud workloads, and networks – to better analyze and detect threats.
- Trend Micro Apex One™ – employs advanced endpoint detection and response (EDR) to provide actionable insights, expanded investigative capabilities, and centralized visibility across the network.
Indicators of compromise
Trend Micro pattern detection
|Blocked and categorized as C&C server