Recent events have highlighted the fact that security researchers are high value targets for threat actors, and given that we deal with malware samples day in and day out, the possibility of either an accidental or intentional compromise is something we all have to take extra precautions to prevent.
Most security researchers will have some kind of AV installed such that downloading a malicious file should trigger a static detection when it is written to disk, but that raises two problems. If the researcher is actively investigating a sample and the AV throws a static detection, this can hamper the very work the researcher is employed to do. Second, it’s good practice not to put known malicious files on your PC: you just might execute them by mistake and/or make your machine “dirty” (in terms of IOCs found on your machine).
One solution to this problem would be to avoid writing samples to disk. As malware reverse engineers, we have to load malware, shellcode and assorted binaries into IDA on a daily basis. After a suggestion from our team member Kasif Dekel, we decided to tackle this problem by creating an IDA plugin that loads a binary into IDA without writing it to disk. We have made this plugin publicly available for other researchers to use. In this post, we’ll describe our Memory Loader plugin’s features, installation and usage.
Memory Loader Plugin
If you have not used IDA Pro plugins before, a plugin basically takes IDA Pro database functionality and extends it. For example, a plugin can take all function entry points and mark them in the graph in red, making it easier to spot them. The plugin feature runs after the IDA database is initialized, meaning there is already a binary loaded into the database. A loader loads a binary into the IDA database.
Our Memory Loader plugin offers several advanced features to the malware analyst. These include loading files from a memory buffer (any source), loading files from zip files (encrypted/unencrypted), and loading files from a URL. Let’s take a look at each in turn.
Loading Files From a Memory Buffer
This plugin offers a library called Memory Loader that anyone can use to extend further the loading capability of IDA Pro to load files from a memory buffer from any source.
MemoryLoader is the base memory loader, a DLL executable, where the memory loading capabilities are stored. Its main functionally is to take a buffer of bytes from a memory buffer and load it into IDA with the appropriate loading scheme.
You will then have an IDA database file and be able to reverse engineer the file just as if it were loaded from the disk but without the attendant risks that come with saving malware to your local drive.
After you’ve analyzed the binary, save your work and close IDA Pro. The temporary IDA db files will be deleted and you will be left with your IDA database file and no binary on the disk.
Loading Files From a Zip/Encrypted Zip
MemZipLoader is able to load both encrypted and plain ZIP files into memory without writing the file to the disk. The loader accepts specific zip format files (.zip). After accepting a zip file, it will display the zip files and allow you to choose the file you want to work with.
MemZipLoader will extract the file from the input ZIP into a memory buffer and load it into IDA without writing it to disk and storing the encrypted zip file on your drive.
Loading Files From a URL
UrlLoader makes loading a file from a URL very easy. The loader is always suggested for any file you open. After you select UrlLoader, you will be asked to enter a URL, and the file downloaded will be stored in a memory buffer.
You will be able to reverse engineer the file and make changes to the IDA database. After you close the IDA window, you will be left with only the database file.
Place the loaders in the loaders directory of IDA.
MemoryLoader.dll -> (C:\Program Files\IDA Pro 7.5)
MemoryLoader64.dll -> (C:\Program Files\IDA Pro 7.5)
Place the memory loader DLL in the IDA directory folder.
MemZipLoader64.dll -> (C:\Program Files\IDA Pro 7.5\loaders)
UrlLoader64.dll -> (C:\Program Files\IDA Pro 7.5\loaders)
UrlLoader.dll -> (C:\Program Files\IDA Pro 7.5\loaders)
MemZipLoader.dll -> (C:\Program Files\IDA Pro 7.5\loaders)
How to Use MemZipLoader & UrlLoader
You can load binaries with MemZipLoader and UrlLoader as follows:
MemZipLoader:
Open IDA and choose zip file.
IDA should automatically suggest the loader:
Once selected, a list of the files from the zip will be displayed:
IDA will then use the loader code and load it as if the binary was a local file on the system.
UrlLoader:
Open any file on your computer in a directory you have write privileges to.
The UrlLoader will suggest a file to open.
After you chose UrlLoader, you will be asked enter a URL:
The loader will browse to the network location you entered. Then IDA Pro will use the loader code and load the binary as if it was a local file.
Setting Up Visual Studio Development
In order to set up the plugin for Visual Studio development, follow these steps.
Open a DLL project in Visual Studio
An IDA loader has three key parts: the accept function, the load function and the loader definition block. Your dllmain file is the file where the loader definition will be.
accept_file – this function returns a boolean if the loader is relevant to the current binary that is being loaded into IDA. For example, if you are loading a PE, the build_loaders_list should return PE.dll as one of the loading options.
load_file – this function is responsible for loading a file into the database. For each loader this function acts differently, so there is not much to say here. Documentation on loaders can be found here.
The project can be compiled into two versions x64 for IDA with x64 addresses, and x64 for IDA x64 with 32 bit addresses. From this point forward we will mark them:
X64 | X64 – 64 bit IDA with 64 BIT addresses
X32 | X64 – 64 bit IDA with 32 BIT addresses
Target file name (Configuration Properties -> Target Name)
When downloading malware to analyze from repositories like VirusTotal, the sample is usually zipped so that the endpoint security doesn’t detect it as malicious. Using our Memory Loader plugin will enable you to reverse engineer malicious binaries without writing them to the disk.
Using the Memory Loader plugin also saves you time analyzing binaries. When working with malicious content in IDA Pro often a different environment is created for it, usually in a virtual machine. Copying the binary and setting up the machine for research every time you want to open IDA is time-expensive. The Memory Loader plugin will allow you to work from your machine in a safer and more productive way.
Please note that a IDA professional license is needed to use and develop extensions for IDA Pro.
Not yet two years old and already in its seventh iteration, Ransomware as a Service variant Conti has proven to be an agile and adept malware threat, capable of both autonomous and guided operation and with unparalleled encryption speed. As of June 2021, Conti’s unique feature set has helped its affiliates extort several million dollars from over 400 organizations.
In this report, we describe in unprecedented detail the rapid evolution of this ransomware and how it has adapted quickly to defenders’ attempts to detect and analyze it. In this post, we summarize our main findings.
Conti is developed and maintained by the so-called TrickBot gang, and it is mainly operated through a RaaS affiliation model. The Conti ransomware is derived from the codebase of Ryuk and relies on the same TrickBot infrastructure.
Initially, Ryuk and later Conti were delivered exclusively by TrickBot. However, by March 2021, as detections for TrickBot improved, BazarLoader/BazarBackdoor began to be used as the tool of choice for the delivery of Conti.
Conti samples first began to be seen around October 2019. Recent attacks, such as that on Ireland’s public health service, demonstrate that Conti has succeeded in becoming just as dangerous if not more so than its predecessor, for both organizations and the public at large. There are 399 reported Conti incidents at the time of writing:
In common with many other ransomware families, Conti also operates a leaks site in order to put further pressure on its victims to pay.
Conti – Evolution With Focus
This technical analysis aims to outline the Conti phylogenesis since the ransomware first appeared on the scene, in order to build a comprehensive knowledge of Conti’s evolution and its development pipeline.
For this study, we clustered Conti samples by timestamps. All the samples used in this research are readily available from OSINT and are recognized as Conti both by the community and by static and dynamic analysis done herein.
We found that each iteration implemented new features in Conti and evolved existing ones. In particular, we see a focus on the following key ransomware characteristics across the evolution of Conti variants:
Obfuscation: Since the early ‘test samples’ (late 2019), Conti started implementing a simple XOR mechanism to hide the API names resolved at runtime. From June 2020, a custom encoding function for string obfuscation was also employed, creating difficulties for static analysis and detection tools.
Speed: Conti uses up to 32 concurrent CPU threads for file encryption operations. Starting from the iteration of September 2020, the developers switched from AES to the CHACHA algorithm to further speed up the encryption process. This translates into less time required to lock victims’ data and reduce the chance of the operation being blocked.
File Encryption: starting from September 2020, a new logic for file encryption was added. The logic implements two different modes: full and partial. depending on file extension and file dimension. From January 2021, encryption through IoCompletionPorts was replaced by C++ queues and locks.
The Early Samples
The earliest sample of Conti we found dates from the end of 2019 and includes indications that it’s an early test version (e.g., the ransom note contains the text “test note”). It took eight months for this version to make headlines, but analysis of this ‘prototype’ helps us understand how Conti developed over time.
These early samples have only a few imported functions linked at load time. Therefore, the first thing the code does is manually load required libraries at runtime using LoadLibraryA and GetProcAddress.
Moreover, all API names are encoded using a simple XOR with the byte 0x99. The names of DLLs are not encoded in this early version, save for some optional imports from Rstrtmgr.dll, the DLL responsible for Microsoft’s Windows’ Restart Manager function. The GetProcAddress function ends by making sure it’s got all the mandatory APIs it was looking for. Otherwise, it exits the program with ExitProcess.
Getting the last import and checking all imports are found
Two resources loaded from the PE file are of particular note. The first will be used as the text for the ransom note (which is set to “test note” in this earliest version), while the second is a list of comma-separated strings denoting files that should be encrypted in case they contain a substring from the list.
The hardcoded ransom note
In cases where the resource has a value of “null”, all files are encrypted except for a hardcoded list. This allows for simple modifications to the ReadMe text or for targeted encryption of specific files, without recompiling the ransomware.
In this early version, all running processes on the system are iterated. Processes containing “sql” in them are terminated with TerminateProcess.
Terminating processes containing ‘sql’
Our full technical report explores more details of this prototype version, but the last point we shall note here is that at the end of the encryption process, the file will be moved, adding the extension .CONTI to the end of it.
Conti Appears In The Wild
Two months later a new version appeared with the inclusion of a real ransom note instead of the embedded “test note”. Other minor changes include changes to the XOR key from 0x99 to 0x0F. More significantly, the ransomware now loads all imports at runtime, with the exception of LoadLibraryA, GetProcAddress, and for some unknown reason, CreateThread. This import is used to boost speed through parellelization as the ransomware looks for files to encrypt across all available drives.
Six months later, in July 2020, Conti had a third iteration and hit the headlines for the first time. String obfuscation has received a significant upgrade with the single-byte XOR key replaced by a custom encoding function, represented by the following pseudo code:
Improved string obfuscation method
The constants (a, b) are different for every encoded string. Additionally, more strings are obfuscated in comparison with the previous samples, although some are still left open on the stack (i.e., DLL names).
There are further changes to how APIs are loaded, but a noticeable lack of consistency, which reinforces the view that multiple developers with different areas of responsibility may be involved in Conti.
A notable new feature is the ability to accept command line arguments, meaning Conti can now be controlled by a human operator for improved targeting. The options include the ability to select the encryption mode (only local, only SMB shares, or both) as well as allowing a list of network locations to search for shares, and adding files found on such shares to the encryption list.
Conti’s Developers Respond To Detection Engines
By September 2020, Conti was making bigger waves, with press reports of an attack on the Fourth District Court of Louisiana claiming the U.S. court’s website was knocked offline and that stolen documents relating to defendants, witnesses and jurors were leaked.
By this time, Conti was on the radar of most endpoint security solutions and the developers clearly took notice. The next iteration includes a greater number of changes than the previous versions, with a heavy emphasis on evasion and anti-analysis.
For the most part, Conti now does not embed the plain names of DLLs and their required exports, but instead, only keeps a hash of the strings it needs. To get the requisite imports, it iterates through NtCurrentPeb()->Ldr->InLoadOrderModuleList, at first looking for the module kernel32.dll by the hash of its name, later on finding the LoadLibraryA API in the same manner, iterating over exports until the hashes match.
Only kernel32.dll is found by hash. The rest of the DLL names are embedded in the executable, now obfuscated, and are loaded using the LoadLibraryA API.
A newly implemented hook removal logic takes place after loading all the necessary DLLs. For each loaded DLL, Conti reads its file on disk and goes through all the exports in it, looking for a difference in the first few bytes. If any such difference is found between the disk version and the in-memory version, the bytes in memory are replaced by the bytes read from disk. This feature is aimed at bypassing some modern EPP/EDR platforms. Security products will often hook processes in order to fully monitor malicious activity. Conti targets this methodology specifically in the hopes of disarming security products lacking robust anti-tamper features.
There are a number of significant changes to the main logic, features and encryption, explored in greater detail in the full technical report. For example, the encryption algorithm is changed from AES to ChaCha. The keys are still generated randomly per file and written to the end of the file after being encrypted with an embedded RSA public key located in the data section of the binary.
Ever-focused on speed to beat mitigation attempts, Conti now includes a hardcoded list of 171 file extensions for which the whole content of the file is encrypted along with a further list of 20 file extensions for which only some part of the file is encrypted. Other files are categorized by size such that:
Files smaller than 1MiB are encrypted whole.
Files larger than 1MiB and smaller than 5MiB have only their first 1MiB encrypted.
Files larger than 5MiB are partially encrypted in jumps.
The extension of encrypted files is now changed from .CONTI to .YZXXX in a bid to avoid simple ransomware detection logic based on known extension changes.
Refining a Successful RaaS Model
Late 2020 saw further iteration with Conti now refining its ransom note to contain more contact information including website, TOR node, email and a “customer” UUID.
Example of recent Conti ransom note
Affiliates were offered a new command line option for logging errors as well as other improvements. To keep detection engines at bay, Conti included more dead code and busy loops to hinder simulation and static analysis.
Through early 2021, the developers changed the seed for their custom hash function twice across two more iterations. From this point on, we find samples more frequently, both packed and unpacked. Some samples are practically the same, except for the embedded public RSA key, the extension used for encrypted files, and the text placed inside the ReadMe file. Other than that, most changes going forward per new sample are minor.
Conclusion
We took a deep dive into the evolution of Conti ransomware, gaining some insight into the process of developing ransomware. Most notably, we saw how many changes take place to increase the evasiveness of the malware from detections and complicate the analysis process. Most meaningful changes and additions to the ransomware were done prior to September-October 2020, at which point, the developers needed only to make minor refinements to stay ahead of the detection curve and keep the money rolling in for their affiliates. Today, Conti is a mature project that is being used actively and aggressively to compromise and extort victims on a daily basis. Read the full report for further details and a complete list of IOCs.
SentinelLabs has discovered a high severity flaw in HP, Samsung, and Xerox printer drivers.
Since 2005 HP, Samsung, and Xerox have released millions of printers worldwide with the vulnerable driver.
SentinelLabs’ findings were proactively reported to HP on Feb 18, 2021 and are tracked as CVE-2021-3438, marked with CVSS Score 8.8.
HP released a security update on May 19th to its customers to address this vulnerability.
As part of our commitment to secure the internet for all users, our researchers have engaged in an open-ended process of vulnerability discovery for targets that impact wide swaths of end users. Our research has been consistently fruitful, particularly in the area of OEM drivers[1, 2]. Many of these drivers come preloaded on devices or get silently dropped when installing some innocuous legitimate software bundle and their presence is entirely unknown to the users. These OEM drivers are often decades old and coded without concern for their potential impact on the overall integrity of those systems.
Our research approach has allowed us to proactively engage with vendors and manufacturers to patch previously unknown vulnerabilities before they can be exploited in the wild. We will continue our efforts to reduce the overall attack surface available to cunning adversaries.
Discovering an HP Printer Driver Vulnerability
Several months ago, while configuring a brand new HP printer, our team came across an old printer driver from 2005 called SSPORT.SYS thanks to an alert by Process Hacker once again.
This led to the discovery of a high severity vulnerability in HP, Xerox, and Samsung printer driver software that has remained hidden for 16 years. This vulnerability affects a very long list of over 380 different HP and Samsung printer models as well as at least a dozen different Xerox products.
The beginning of a long list of affected HP and Samsung productsA number of Xerox Products are also affected by CVE-2021-3438
Since all of these models are in fact manufactured by HP, we reported the vulnerability to them.
Technical Details
Just by running the printer software, the driver gets installed and activated on the machine regardless of whether you complete the installation or cancel.
Thus, in effect, this driver gets installed and loaded without even asking or notifying the user. Whether you are configuring the printer to work wirelessly or via a USB cable, this driver gets loaded. In addition, it will be loaded by Windows on every boot:
This makes the driver a perfect candidate to target since it will always be loaded on the machine even if there is no printer connected.
The vulnerable function inside the driver accepts data sent from User Mode via IOCTL (Input/Output Control) without validating the size parameter:
The vulnerable function inside the driver
This function copies a string from the user input using strncpy with a size parameter that is controlled by the user. Essentially, this allows attackers to overrun the buffer used by the driver.
An interesting thing we noticed while investigating this driver is this peculiar hardcoded string: "This String is from Device [email protected]@@@ ".
The hardcoded string in the vulnerable driver
It seems that HP didn’t develop this driver but copied it from a project in Windows Driver Samples by Microsoft that has almost identical functionality; fortunately, the MS sample project does not contain the vulnerability.
Impact
An exploitable kernel driver vulnerability can lead an unprivileged user to a SYSTEM account and run code in kernel mode (since the vulnerable driver is locally available to anyone). Among the obvious abuses of such vulnerabilities are that they could be used to bypass security products.
Successfully exploiting a driver vulnerability might allow attackers to potentially install programs, view, change, encrypt or delete data, or create new accounts with full user rights. Weaponizing this vulnerability might require chaining other bugs as we didn’t find a way to weaponize it by itself given the time invested.
Suggestions
Generally speaking, it is highly recommended that in order to reduce the attack surface provided by device drivers with exposed IOCTLs handlers, developers should enforce strong ACLs when creating kernel device objects, verify user input and not expose a generic interface to kernel mode operations.
Remediation
This vulnerability and its remedies are described in HP Security Advisory HPSBPI03724 and Xerox Advisory Mini Bulletin XRX21K. We recommend HP/Samsung/Xerox customers, both enterprise and consumer, to apply the patch as soon as possible.
To mitigate this issue users should use this link and look for their printer model and then download the patch file as shown in the picture:
Some Windows machines may already have this driver without even running a dedicated installation file, since the driver comes with Microsoft Windows via Windows Update:
The driver is marked as “File Distributed by Microsoft” in VirusTotal
Note: Not all affected products were initially listed on the advisory page. We initially conducted a small sample test and found other products vulnerable, so we recommend further verification.
Conclusion
This high severity vulnerability, which has been present in HP, Samsung, and Xerox printer software since 2005, affects millions of devices and likely millions of users worldwide. Similar to previous vulnerabilities we have disclosed that remained hidden for 12 years (1, 2), the impact this could have on users and enterprises that fail to patch is far-reaching and significant.
While we haven’t seen any indicators that this vulnerability has been exploited in the wild up till now, with millions of printer models currently vulnerable, it is inevitable that if attackers weaponize this vulnerability they will seek out those that have not taken the appropriate action.
We would like to thank HP for their approach to our disclosure and for remediating the vulnerabilities quickly.
Disclosure Timeline
18 Feb, 2021 – Initial report. 23 Feb, 2021 – We notified HP that the same issue exists in Samsung and Xerox printers. 19 May, 2021 – HP released an advisory for CVE-2021-3438. 20 May, 2021 – We notified HP that the “affected products” listing is incomplete and provided extra information. 01 Jun, 2021 – HP updated the list of affected products.
On July 9th, 2021 a wiper attack paralyzed the Iranian train system.
The attackers taunted the Iranian government as hacked displays instructed passengers to direct their complaints to the phone number of the Iranian Supreme Leader Khamenei’s office.
SentinelLabs researchers were able to reconstruct the majority of the attack chain, which includes an interesting never-before-seen wiper.
OPSEC mistakes let us know that the attackers refer to this wiper as ‘Meteor’, prompting us to name the campaign MeteorExpress.
At this time, we have not been able to tie this activity to a previously identified threat group nor to additional attacks. However, the artifacts suggest that this wiper was developed in the past three years and was designed for reuse.
To encourage further discovery of this new threat actor, we are providing indicators as well as hunting YARA rules for fellow security researchers.
Introduction
On July 9th, 2021 reports began to surface of a wiper attack disrupting service for the Iranian railway system. The attack included epic level trolling as reports suggest that train schedule displays cited “long delay[s] because of cyberattack” along with instructions to contact ‘64411’ –the number for the office of Supreme Leader Ali Khamenei.
Early reporting did not pick up much steam as it’s not uncommon for Iranian authorities to vaguely point the finger towards cyber attacks only to retract the claims later. But it doesn’t hurt to check.
We would like to acknowledge security researcher Anton Cherepanov who pointed out an early analysis (Farsi) by an Iranian antivirus company. Despite a lack of specific indicators of compromise, we were able to recover most of the attack components described in the post along with additional components they had missed. Behind this outlandish tale of stopped trains and glib trolls, we found the fingerprints of an unfamiliar attacker.
The Attack Chain
MeteorExpress Attack Chain
Though early reports did not include technical specifics, we were able to reconstruct most of the attack components relying on a combination of factors – early analysis by Padvish security researchers as well as a recovered attacker artifact that included a longer list of component names. The attackers abused Group Policy to distribute a cab file to conduct their attack.
The overall toolkit consists of a combination of batch files orchestrating different components dropped from RAR archives. The archives decompressed with an attacker supplied copy of Rar.exe coupled with the password ‘hackemall’. The wiper components are split by functionality: Meteor encrypts the filesystem based on an encrypted configuration, nti.exe corrupts the MBR, and mssetup.exe locks the system.
While we were able to recover a surprising amount of files for a wiper attack, some have eluded us. The MBR corrupter, nti.exe, is most notable among those missing components as Padvish researchers noted that the sectors overwritten by this component are the same as those overwritten by NotPetya. Until we are able to find this file, we can’t corroborate their finding.
The following is a breakdown of the central components of this attack.
The Batch Files
The majority of the attack is orchestrated via a set of batch files nested alongside their respective components and chained together in successive execution.
The following is a short description of the main functionality of these batch files.
setup.bat
setup.bat is the first component executed via group policy. Interestingly, it deletes a scheduled task called ‘AnalyzeAll’ under the Windows Power Efficiency Diagnostics directory. At this time, we haven’t been able to identify this task. This batch file is responsible for copying the initial components via a CAB file in a network share within the Iranian railways network. The CAB file is expanded and update.bat is executed with the parameters ‘hackemall’, relevant paths, and the Meteor wiper executable (env.exe).
envxp.bat
envxp.bat appears to be a simpler alternative version of setup.bat. As the name suggests, perhaps it’s intended for Windows XP.
update.bat is a well written batch script that takes care of placing the remaining files and directing the remainder of the execution flow by calling the successive batch scripts. It takes three arguments: the password for the rar archives, the working directory, and the location of the payload. If the first two parameters are empty, it’ll exit smoothly. In the absence of a payload, the script attempts to run msapp.exe. That component is listed in the Padvish security writeup but the execution flow via setup.bat points to env.exe as the intended payload. We’ll delve into this component below.
update.bat’s makeshift mutex
The script checks for a hardcoded ‘lock_file’ under C:WindowsTemp__lock6423900.dat. The file serves as a makeshift mutex to avoid double execution and could double as a vaccine to avoid infection during development.
update.bat directing the execution flow to subsequent batch files
The batch file uses its own copy of WinRAR to decompress additional components from three additional archives (programs.rar, bcd.rar, ms.rar) using the same Pokemon-themed password, “hackemall” (Hack ’Em All). With each RAR archive, update.bat calls a subsequent batch archive before deleting the respective archive. The developers are very careful about cleaning up their components as soon as they’re used.
At this point the execution begins to bifurcate into other scripts. The first one is cache.bat, which focuses on clearing obstacles and preparing the ground for subsequent elements with the use of PowerShell.
cache.bat disabling network adapters and checking for Kaspersky antivirus
cache.bat performs three main functions. First, it will disconnect the infected device from the network. Then it checks to see if Kaspersky antivirus is installed on the machine, in which case it’ll exit.
cache.bat creating Windows Defender exclusions for attack components
Finally, cache.bat will create Windows Defender exclusions for all of its components, effectively clearing the way for a successful infection without impediments. This script proved particularly valuable for us in rebuilding the entire attack chain as it lists most of the attack components giving us a threat hunting shopping list of sorts. It’s worth noting that this is the only batch script we’ve recovered that embeds PowerShell.
Subsequently, update.bat calls bcd.bat, which serves two functions: rendering the machine unbootable and cleaning up event logs.
bcd.bat script overwrites boot.ini
In order to disable the machine’s ability to boot up, bcd.bat creates an alternative boot.ini file that points the bootloader to impossibly high disk and partition numbers (10000000) and overwrites the system’s copy of boot.ini. The script then uses the native bcdedit command to list boot option identifiers and deletes each.
bcd.bat clears event logs
The attackers then use the native wevtutil command to clear Security, System, and Application event logs. And finally, it abuses a legitimate SysInternals tool called Sync (the equivalent of the native UNIX sync()) to manually flush the cache of filesystem data to disk.
update.bat will then call msrun.bat, passing the Meteor wiper executable as a parameter. That script will in turn set the stage for its execution.
msrun.bat preparing to execute the Meteor wiper
msrun.bat moves several components into place including a screen locker (mssetup.exe) and the encrypted configuration for the Meteor wiper (msconf.conf). The script also moves four additional files: mscap.bmp, mscap.jpg, mssetup.reg, msuser.reg. At the time of writing, we were unable to recover the .reg files and have no indication of what role they play. The image files are the background images that will replace the wallpaper on locked machines.
mscap.jpg lockscreen image
The same script then creates a scheduled task called mstask set to execute the Meteor wiper at five minutes to midnight.
update.bat calls the wiper and screen locker
The final portion of update.bat checks whether mssetup.exe and the Meteor wiper are running, taking appropriate actions like exiting the script or restarting the machine as necessary.
A Wiper Triad
There’s a strange level of fragmentation to the overall toolkit. Batch files spawn other batch files, different rar archives contain intermingled executables, and even the intended action is separated into three payloads: Meteor wipes the filesystem, mssetup.exe locks the user out, and nti.exe presumably corrupts the MBR. We have been able to identify two out of three components and detail their inner workings below.
Internal naming convention visible within the wiper binary
The main payload of this convoluted attack chain is an executable dropped under env.exe or msapp.exe. Internally, the coders refer to it as ‘Meteor’. While this particular instance of Meteor suffers from a crippling OPSEC failure (the inclusion of verbose debug strings presumably intended for internal testing), it’s an externally configurable wiper with an extensive set of features.
The Meteor wiper is executed as a scheduled task, called mstask and set to run at five minutes to midnight. It’s supplied with a single argument, an encrypted JSON configuration file, msconf.conf (68e95a3ccde3ea22b8eb8adcf0ad53c7993b2ea5316948e31d9eadd11b5151d7), that holds values for corresponding keys contained in cleartext within the binary:
At its most basic functionality, the Meteor wiper takes a set of paths from the encrypted config and walks these paths, wiping files. It also makes sure to delete shadow copies and removes the machine from the domain to avoid means of quick remediation. The wiper includes a wealth of additional functionality, most of which isn’t used in this particular attack, including:
Changing passwords for all users
Disabling screensavers
Process termination based on a list of target processes
Installing a screen locker
Disabling recovery mode
Changing boot policy error handling
Creating scheduled tasks
Logging off local sessions
Changing lock screen images for different Windows versions (XP, 7, 10)
Creating processes and executing commands
Meteor wiper attempts two different methods to remove victim machine from Domain
The developers resort to multiple redundant methods to accomplish each of their objectives. For example, Meteor will attempt to remove the machine from the domain via WinApi functions. If that fails it will then attempt to do the same via an equivalent WMI command.
Taking a step back to evaluate the development of Meteor and what it might tell us about the threat group involved, we must note that the composition of this binary is beset by contradictory practices.
First, the code is rife with sanity checks, error checking, and redundancy in accomplishing its goals. However, the operators clearly made a major mistake in compiling a binary with a wealth of debug strings meant for internal testing. The latter is an indication that despite whatever advanced practices the developers have in their arsenal, they lack a robust deployment pipeline that ensures such mistakes do not happen. Moreover, note that this sample was compiled six months before its deployment and the mistake was not caught.
Lock My PC 4 embedded within Meteor
Secondly, the code is a bizarre amalgam of custom code that wraps open-source components (cpp-httplib v0.2) and practically ancient abused software (FSProLabs’ Lock My PC 4). While that might suggest that the Meteor wiper was built to be disposable, or meant for a single operation, that’s juxtaposed with an externally configurable design that allows efficient reuse for different operations. Many of the available keys are not instantiated in this operation, like the ability to kill specific processes. Additionally, that external configuration is encrypted, presumably to limit analysis, but all of the configurable keys are hardcoded in plaintext within the main binary.
Meteor overwrites boot.ini with the same template as bcd.bat
Taking a step back to look at the entire toolkit deployed in this operation, there are also some overlaps between the functionality contained within Meteor and that of other components executed beforehand that suggest some operational segmentation between developers of different components and the operators themselves. Functionality carried out with batch scripts is also embedded within Meteor such as disabling network adapters and corrupting boot.ini. The wiper also includes a commercial screen locker and yet this functionality is redundantly instantiated through a separate binary, mssetup.exe.
The externally configurable nature of the wiper entails that it wasn’t created for this particular operation. However, at the time of writing, we’ve been unable to find other attacks or variants of the Meteor wiper. For that reason, we are supplying a very broad (but well tested) hunting YARA rule below.
‘mssetup.exe’ Screenlocker
mssetup.exe’s WinMain() function
The MeteorExpress operators drop a standalone screenlocker. Despite a wealth of C++ template and exception handling code, mssetup.exe is simple. Most of its functionality is pictured above. It blocks user input before creating a Window that fills the entire screen. If an image is available at the hardcoded path C:tempmscap.bmp (dropped by the msrun.bat script), then it’ll use this image to fill the screen. Otherwise, it’ll draw a black rectangle. It’ll then disable the cursor and effectively lock the user out entirely. It’s worth noting that though this binary was clearly developed by the same production pipeline, it doesn’t include any of the verbose debug strings nor overt logging functionality.
Finally, the Padvish security blog makes reference to an additional executable, nti.exe, that serves as an MBR corruptor. We’ve been unable to recover this at this time and suspect that the incident responders were unable to recover it themselves as their analysis centers on the corrupted MBRs rather than the binary.
Description of nti.exe Google translated from Farsi
One interesting claim in the Padvish blog is that the manner in which nti.exe corrupts the MBR is by overwriting the same sectors as the infamous NotPetya. While one’s first instinct might be to assume that the NotPetya operators were involved or that this is an attempt at a false flag operation, it’s important to remember that NotPetya’s MBR corrupting scheme was mostly cribbed from the original Petya used for criminal operations. An additional inconsistency from the Padvish blog is their claim that update.bat runs nti.exe. While they’re likely referring to a different version in their possession, our copy of update.bat makes no overt reference to nti.exe.
Conclusion
Conflict in cyberspace is overpopulated with increasingly brazen threat actors. Behind the artistry of this epic troll lies an uncomfortable reality where a previously unknown threat actor is willing to leverage wiper malware against public railways systems. The attacker is an intermediate level player whose different operational components sharply oscillate from clunky and rudimentary to slick and well-developed.
On the one hand, we have a new externally-configurable wiper packed full of interesting capabilities, involving a mature development process, and redundant means to accomplish their goals. Even their batch scripts include extensive error checking, a feature seldom encountered with deployment scripts. Their attack is designed to cripple the victim’s systems, leaving no recourse to simple remediation via domain administration or recovery of shadow copies.
On the other hand, we see an adversary that doesn’t yet have a handle on their deployment pipeline, using a sample of their malware that contains extensive debug features and burning functionality irrelevant to this particular operation. There’s feature redundancy between different attack components that suggests an uncoordinated division of responsibilities across teams. And files are dispensed in a clunky, verbose, and disorganized manner unbecoming of advanced attackers.
We cannot yet make out the shape of this adversary across the fog. Perhaps it’s an unscrupulous mercenary group. Or the latent effects of external training coming to bear on a region’s nascent operators. At this time, any form of attribution is pure speculation and threatens to oversimplify a raging conflict between multiple countries with vested interests, means, and motive.
Behind this epic troll/stunning provocation there’s a lot more to uncover in getting to know the actor behind MeteorExpress. We should keep in mind that the attackers were already familiar with the general setup of their target, features of the domain controller, and the target’s choice of backup system (Veeam). That implies a reconnaissance phase that flew entirely under the radar and a wealth of espionage tooling that we’ve yet to uncover.
Versions 4.2 and 4.3 of Cobalt Strike’s server contain multiple Denial of Service vulnerabilities (CVE-2021-36798).
The vulnerabilities can render existing Beacons unable to communicate with their C2 server, prevent new beacons from being installed, and have the potential to interfere with ongoing operations.
We have released a new Python library to help generically parse Beacon communication in order to help the research security community.
Introduction
Cobalt Strike is one of the most popular attack frameworks designed for Red Team operations. At the same time, many APTs and malicious actors also use it.
SentinelOne has seen numerous attacks involving Cobalt Strike Beacons across our customer base. SentinelOne detects Cobalt Strike Beacon and we are constantly rolling out new ways to detect modifications or novel ways to load Beacon in memory.
Given its rampant adoption by red teams and attackers alike, we wanted to better understand the operational security of Cobalt Strike. This led us to discover the vulnerabilities reported in CVE-2021-36798 and which we describe below.
Beacon Communications
To understand the vulnerabilities we found, we will briefly cover how Cobalt Strike Beacon communication works.
The first time the Cobalt Strike server runs, it creates randomly generated RSA keys, private and public, stored in a file named “.Cobalt Strike.beacon_keys”. Every Beacon stager has the public key embedded in it.
We can get the Beacon’s public RSA key by parsing its configuration
When a Beacon stager runs, it gathers information about the computer it is running on (CPU architecture, keyboard layout, internal IP, etc.), encrypts that info using the public key, and sends it to the server in an HTTP GET request. We will refer to that part as “Beacon registration”.
After the Beacon has registered with the server, the attacker can interact with the Beacon. From this point, the Beacon works by receiving and replying to “tasks”. Tasks can, for example, be used to get a process list, run a command, conduct lateral movement, and many other things of interest to the attacker.
Receiving tasks generally happens over HTTP GET requests and the Beacon replies with the task data over HTTP POST requests. Tasks are encrypted using an AES key sent by the Beacon in the registration request. The entire communication flow is explained in the official documentation, but the outline above should suffice for what follows.
One of the most famous features of Cobalt Strike is its Malleable C2. In short, this feature lets the attacker encode (“transform” in Cobalt’s language) all the beacon’s HTTP communications. The entire process described above is wrapped in the chosen Malleable profile’s transformation steps, which are also embedded in the stager itself.
Below is an example of a popular Malleable C2 profile that masquerades traffic as a normal request for the jquery code (source):
An example of a popular Malleable C2 profile
Vulnerabilities
First, it should be noted that there was already one known vulnerability in Cobalt Strike that was previously reported. A great write-up written by nccgroup is worth reading for a more in-depth understanding of Beacon’s communication internals. In practice, that vulnerability allowed for remote code execution on the server.
We’re not interested in remote code execution vulnerability here as it would be overkill for our purposes. Considering that the server’s code is written in Java and isn’t very large, it wasn’t too hard to find bugs there.
For example, in the Screenshot and Keylogger task replies, there’s an interesting behavior when reading the reply’s data:
public void process_beacon_callback_decrypted(final String beaconID, final byte[] responseBytes) {
...
// Sanity checks here
...
try {
final DataInputStream responeBytesStream = new DataInputStream(new ByteArrayInputStream(responseBytes));
cmd = responeBytesStream.readInt();
if (cmd == 0) {...}
...
else if (cmd == 3) {
final DataParser dp = new DataParser(CommonUtils.readAll(responeBytesStream));
dp.little();
final byte[] scData = dp.readCountedBytes(); // Bug #1 here
final int scDesktop = dp.readInt();
final String scTitle = this.getCharsets().process(beaconID, dp.readCountedBytes());
final String process6 = this.getCharsets().process(beaconID, dp.readCountedBytes());
if (scData.length == 0) {
output(BeaconOutput.Error(beaconID, "screenshot from desktop " + scDesktop + " is empty"));
return;
}
...
output(BeaconOutput.OutputB(beaconID, "received screenshot of " + scTitle + " from " + process6 + " (" + CommonUtils.formatSize(scData.length) + ")"));
...
}}}
In this example, we see the parsing of a screenshot task reply. To read the screenshot’s data, it calls the function readCountedBytes, which reads an integer from the first four bytes of the data and treats it as the screenshot’s size without any sanity checks.
Then, before reading the screenshot’s data, it allocates a buffer big enough to hold it:
byte[] array = new byte[ReplySize];
By manipulating the screenshot’s size we can make the server allocate an arbitrary size of memory, the size of which is totally controllable by us. However, in order to trigger this piece of code, we need to be able to talk to the server like a Beacon.
By combining all the knowledge of Beacon communication flow with our configuration parser, we have all we need to fake a Beacon.
We’ve published a POC python script that does just that: it parses a Beacon’s configuration and uses the information stored in it to register a new random Beacon on the server. After registering the Beacon, it’s pretty trivial to use the primitive found above to iteratively send fake task replies that squeeze every bit of available memory from the C2’s web server thread:
size = 1000000000
while True:
try:
if size
This leads to the crashing of the server’s web thread that handles HTTP stagers and Beacon communication:
Crashing the server's web thread
This would allow an attacker to cause memory exhaustion in the Cobalt Strike server (the “Teamserver”) making the server unresponsive until it's restarted. This means that live Beacons cannot communicate to their C2 until the operators restart the server.
Restarting, however, won’t be enough to defend against this vulnerability as it is possible to repeatedly target the server until it is patched or the Beacon’s configuration is changed.
Either of these will make the existing live Beacons obsolete as they’ll be unable to communicate with the server until they’re updated with the new configuration. Therefore, this vulnerability has the potential to severely interfere with ongoing operations.
Although used every day for malicious attacks, Cobalt Strike is ultimately a legitimate product, so we have disclosed these issues responsibly to HelpSystems and they have fixed the vulnerabilities in the last release.
Parsing of a Beacon’s embedded Malleable profile instructions
Parsing of a Beacon’s configuration directly from an active C2 (like the popular nmap script)
Basic code for communicating with a C2 as a fake Beacon
Other than registering a fake Beacon with the server, the code we are releasing makes it easier to parse captured Beacon communications in a generic way.
Let’s take, for example, a case of a captured unencrypted Beacon communication from malware-traffic-analysis and decode it using the new communication module:
from urllib import parse
from pcaper import PcapParser
from parse_beacon_config import *
from comm import *
conf = cobaltstrikeConfig(r"beacon.bin").parse_config()
pparser = PcapParser()
reqs = pparser.read_pcap({'input': r"2019-07-25-Hancitor-style-Amadey-with-Pony-and-Cobalt-Strike.pcap"})
t = Transform(conf['HttpPost_Metadata'])
for req in reqs:
if conf['HttpPostUri'] in req.uri:
params = {k: v[0] for k, v in parse.parse_qs(parse.urlsplit(req.uri).query).items()}
print('nnFound beacon reply:n', t.decode(req.body, req.headers, params)[1])
Output:
...
Found beacon reply:
♠r↓10.7.25.101:445 (platform: 500 version: 6.1 name: HIDDENROAD-PC domain: WORKGROUP)
Scanner module is complete
"))
Found beacon reply:
☺►[*] Wrote hijack DLL to 'C:UsersSARAH~1.RUTAppDataLocalTemp745f.dll'
[+] Privileged file copy success! C:WindowsSystem32sysprepCRYPTBASE.dll
[+] C:WindowsSystem32sysprepsysprep.exe ran and exited.
[*] Cleanup successful
...
It parses the Malleable C2 instructions embedded in the Beacon’s configuration and uses it to decode Beacon replies from the captured HTTP requests.
There’s a lot that can be done with this new communication library and it will be interesting to see what other researchers from the community will do with it.
Conclusion
Research into attack frameworks like Cobalt Strike and Cellebrite is still a niche area. We hope that this research and the tools we have released help to further encourage research into the robustness of attack frameworks and expand the range of available options when facing their consistent abuse.
Disclosure Timeline
We would like to thank HelpSystems for their approach to our disclosure and for remediating the vulnerabilities.
04/20/2021 - Initial contact with HelpSystems for issue disclosure. 04/22/2021 - Issue details disclosed to HelpSystems. 04/23/2021 - HelpSystems confirmed the issue and asked for an extension until August 3rd. 04/28/2021 - SentinelOne accepted the extension. 07/18/2021 - Submitted CVE request to MITRE. 07/19/2021 - CVE-2021-36798 was assigned and reserved for the specified issue. 08/02/2021 - SentinelOne shared the publication date and post for review. 08/02/2021 - HelpSystems reviewed and confirmed the post for publication. 08/04/2021 - HelpSystems released Cobalt Strike 4.4, which contains a fix for CVE-2021-36798.
AdLoad is one of several widespread adware and bundleware loaders currently afflicting macOS.
In late 2019, SentinelLabs described how AdLoad was continuing to adapt and evade detection.
This year we have seen over 150 unique samples that are part of a new campaign that remain undetected by Apple’s on-device malware scanner.
Some of these samples have been known to have also been blessed by Apple’s notarization service.
We describe the infection pattern and detail the indicators of compromise for the first time.
Introduction
AdLoad is one of several widespread adware and bundleware loaders currently afflicting macOS. AdLoad is certainly no newcomer to the macOS malware party. In late 2019, SentinelLabs described how AdLoad was continuing to adapt and evade detection, and this year we have seen another iteration that continues to impact Mac users who rely solely on Apple’s built-in security control XProtect for malware detection.
In this post, we detail one of several new AdLoad campaigns we are currently tracking that remain undetected by Apple’s macOS malware scanner. We describe the infection pattern and indicators of compromise for the first time and hope this information will help others to detect and remove this threat.
AdLoad | Staying One Step Ahead of Apple
AdLoad has been around since at least 2017, and when we previously reported on it in 2019, Apple had some partial protection against its earlier variants. Alas, at that time the 2019 variant was undetected by XProtect.
As of today, however, XProtect arguably has around 11 different signatures for AdLoad (it is ‘arguable’ because Apple uses non-industry standard names for its signature rules). As best as we can track Apple’s rule names to common vendor names, the following XProtect rules appear to be all partially or wholly related to AdLoad variants:
Signatures for AdLoad variants in XProtect
The good news for those without additional security protection is that the previous variant we reported in 2019 is now detected by XProtect, via rule 22d71e9.
An earlier AdLoad variant reported by SentinelLabs is now detected by XProtect
The bad news is the variant used in this new campaign is undetected by any of those rules. Let’s see what’s changed.
AdLoad 2021 Campaign | ‘System’ and ‘Service’
Both the 2019 and 2021 variants of AdLoad used persistence and executable names that followed a consistent pattern. In 2019, that pattern included some combination of the words “Search” , “Result” and “Daemon”, as in the example shown above: “ElementarySignalSearchDaemon”. Many other examples can be found here.
The 2021 variant uses a different pattern that primarily relies on a file extension that is either .system or .service. Which file extension is used depends on the location of the dropped persistence file and executable as described below, but typically both .system and .service files will be found on the same infected device if the user gave privileges to the installer.
With or without privileges, AdLoad will install a persistence agent in the user’s Library LaunchAgents folder with patterns such as:
To date, we have found around 50 unique label patterns, with each one having both a .service and a .system version. Based on our previous understanding of AdLoad, we expect there to be many more.
When the user logs in, the AdLoad persistence agent will execute a binary hidden in the same user’s ~/Library/Application Support/ folder. That binary follows another deterministic pattern, whereby the child folder in Application Support is prepended with a period and a random string of digits. Within that directory is another directory called /Services/, which in turn contains a minimal application bundle having the same name as the LaunchAgent label. That barebones bundle contains an executable with the same name but without the com. prefix. For example:
Indicators of compromise in the User’s Library Application Support folder
A hidden tracker file called .logg and containing only a UUID string is also dropped in the Application Support folder. Despite the location, if the dropper has also been granted privileges, then the tracker file is owned by root rather than the user.
The hidden tracker file in the User’s Library Application Support folder
Further, assuming the user supplied admin privileges as requested by the installer, another persistence mechanism is written to the domain /Library/LaunchDaemons/ folder. This plist file uses the file extension .system, and the corresponding folder in the hidden Application Support folder is also named /System/ instead of /Services/.
Indicators of compromise in the Domain Library Application Support folder
The LaunchDaemon is dropped with one of a number of pre-determined labels that mirrors the label used in the LaunchAgent, such as:
The persistence plists themselves pass different arguments to the executables they launch. For the system daemon, the first argument is -t and the second is the plist label. For the user persistence agent, the arguments -s and 6600 are passed to the first and second parameters, respectively.
AdLoad 2021 macOS persistence pattern
Interestingly, the droppers for this campaign share the same pattern as Bundlore/Shlayer droppers. They use a fake Player.app mounted in a DMG. Many are signed with a valid signature; in some cases, they have even been known to be notarized.
Like much other adware, AdLoad makes use of a fake Player.app to install malware
Typically, we observe that developer certificates used to sign the droppers are revoked by Apple within a matter of days (sometimes hours) of samples being observed on VirusTotal, offering some belated and temporary protection against further infections by those particular signed samples by means of Gatekeeper and OCSP signature checks. Also typically, we see new samples signed with fresh certificates appearing within a matter of hours and days. Truly, it is a game of whack-a-mole.
The droppers we have seen take the form of a lightly obfuscated Zsh script that decompresses a number of times before finally executing the malware out of the /tmp directory (for a discussion of how to deobfucscate such scripts see here).
The dropper executes a shell script obfuscated several times over
The final payload is not codesigned and isn’t known to the current version of Apple’s XProtect, v2149.
The malware executes out of /tmp/ and is neither codesigned nor known to XProtectOnce infection is complete, the adware pops the following page in the user’s default browser
How New Is This Variant of AdLoad?
In our investigation, we found over 220 samples of this adware variant on VirusTotal, in both packed and unpacked form. At least 150 of these are unique. Interestingly, a lone sample of this variant was documented by analysts at Confiant, who described the malware’s string decryption routine in a post published on June 3rd, 2021. According to these researchers, the sample they observed had been notarized by Apple.
We note that across our corpus, all samples from November 2020 to August 2021 use the same or similar string decryption routine as that described by Confiant. Similarly, the earlier researchers’ sample, “MapperState.system” conforms to the AdLoad naming pattern that we observed and described above. Both these indicators definitively link our findings with theirs.
AdLoad binaries use a great deal of obfuscation, including custom string encryptionThree different samples, all using a similar string encryption routine
Our research showed that samples began to appear at least as early as November 2020, with regular further occurrences across the first half of 2021. However, there appears to have been a sharp uptick throughout July and in particular the early weeks of August 2021.
It certainly seems possible that the malware developers are taking advantage of the gap in XProtect, which itself has not been updated since a few week’s after Confiant’s research over two months ago. At the time of writing, XProtect was last updated to version 2149 around June 15th – 18th.
Version 2149 is the most recent version of Apple’s XProtect as of August 11th
None of the samples we found are known to XProtect since they do not match any of the scanner’s current set of AdLoad rules.
Running XProtect v2149 against 221 known samples shows no detections
However, there is reasonably good detection across a variety of different vendor engines used by VirusTotal for all the same samples that XProtect doesn’t detect.
All the samples are detected by various VT vendor engines
On our test machine, we set the policy of the SentinelOne Agent to “Detect only” in order to allow the malware to execute and observe its behaviour. In the Management console, the behavioral detection is mapped to the relevant MITRE indicators.
Behavioral Indicators from the SentinelOne agent
Since AdLoad is a common adware threat whose behavior of hijacking search engine results and injecting advertisements into web pages has been widely documented in the past, we ended our observation at this juncture.
Conclusion
As Apple itself has noted and we described elsewhere, malware on macOS is a problem that the device manufacturer is struggling to cope with. The fact that hundreds of unique samples of a well-known adware variant have been circulating for at least 10 months and yet still remain undetected by Apple’s built-in malware scanner demonstrates the necessity of adding further endpoint security controls to Mac devices.
As we indicated at the beginning of this post, this is only one campaign related to AdLoad that we are currently tracking. Further publications related to these campaigns are in progress.
Indicators of Compromise
YARA Hunting Rule
private rule Macho
{
meta:
description = "private rule to match Mach-O binaries"
condition:
uint32(0) == 0xfeedface or uint32(0) == 0xcefaedfe or uint32(0) == 0xfeedfacf or uint32(0) == 0xcffaedfe or uint32(0) == 0xcafebabe or uint32(0) == 0xbebafeca
}
rule adload_2021_system_service
{
meta:
description = "rule to catch Adload .system .service variant"
author = "Phil Stokes, SentinelLabs"
version = "1.0"
last_modified = "2021-08-10"
reference = "https://s1.ai/adload"
strings:
$a = { 48 8D 35 ?? ?? 00 00 48 8D 5D B8 BA B8 00 00 00 48 89 DF E8 ?? ?? FB FF 48 8B 43 08 48 2B 03 66 48 0F 6E C0 66 0F 62 05 ?? ?? 00 00 66 0F 5C 05 ?? ?? 00 00 0F 57 C9 66 0F 7C C0 48 8D 7D A0 0F 29 0F F2 0F 59 05 }
condition:
Macho and all of them
}
ShadowPad is a privately sold modular malware platform –rather than an open attack framework– with plugins sold separately.
ShadowPad is still regularly updated with more advanced anti-detection and persistence techniques.
It’s used by at least four clusters of espionage activity. ShadowPad was the primary backdoor for espionage operations in multiple campaigns, including the CCleaner, NetSarang, and ASUS supply-chain attacks.
The adoption of ShadowPad significantly reduces the costs of development and maintenance for threat actors. We observed that some threat groups stopped developing their own backdoors after they gained access to ShadowPad.
As a byproduct of that shared tooling, any claim on attribution needs to be reviewed in a cautious way when a shared backdoor like ShadowPad is involved.
Instead of focusing on specific threat groups, we discuss local personas possibly involved in the development of ShadowPad as an iterative successor to PlugX.
ShadowPad emerged in 2015 as the successor to PlugX. However, it was not until several infamous supply-chain incidents occurred – CCleaner, NetSarang and ShadowHammer – that it started to receive widespread attention in the public domain. Unlike the publicly-sold PlugX, ShadowPad is privately shared among a limited set of users. Whilst collecting IoCs and connecting the dots, we asked ourselves: What threat actors are using ShadowPad in their operations? And ultimately, how does the emergence of ShadowPad impact the wider threat landscape from Chinese espionage actors?
To answer those questions, we conducted a comprehensive study on the origin, usage and ecosystem of ShadowPad. The full report provides:
a detailed overview of ShadowPad, including its history, technical details, and our assessment of its business model and ecosystem
a detailed description of four activity clusters where ShadowPad has been used
a discussion of how ShadowPad’s emergence changes the attacking strategies of some China-based threat actors
how ShadowPad affects the threat landscape of Chinese espionage attacks
In this blog post, we provide an abridged version of some of our key findings and discussions. Please see the full report for an extended discussion, full Indicators of Compromise and other technical indicators.
Technical Analysis
ShadowPad is a modular backdoor in shellcode format. On execution, a layer of an obfuscated shellcode loader is responsible for decrypting and loading a Root plugin. While the sequence of operation in the Root plugin decrypts, it loads other plugins embedded in the shellcode into memory. The plugins are kept and referenced through a linked list:
Along with the plugins embedded in the sample, additional plugins are allowed to be remotely uploaded from the C&C server, which allows users to dynamically add functionalities not included by default.
The architecture of ShadowPad backdoor
As luck would have it, the ShadowPad controller (version 1.0, 2015) was accidentally discovered during private research. All of the stakeholders involved agreed to our releasing screenshots but not the details of the actual file, so we are unable to provide hashes for this component at present.
Analysis of the controller allowed us to obtain a clear picture of how the builder generates the shellcodes, how the users manage the infected hosts, and the kinds of functions available on the controller.
Privately Shared Attack Framework or Privately Sold Modular Malware?
An intriguing question to address is whether ShadowPad is a privately shared attack framework or a privately developed modular malware platform for sale to specific groups. Its design allows the users to remotely deploy new plugins to a backdoor. In theory, anyone capable of producing a plugin that is encrypted and compressed in the correct format can add new functionalities to the backdoor freely.
However, the control interfaces of the plugins are hardcoded in the “Manager” page of the ShadowPad controller, and the controller itself does not include a feature to add a new control interface.
The interfaces to control the plugins are hardcoded and listed in the “Manager” page
In other words, it is unlikely that ShadowPad was created as a collaborative attacking framework. Only the plugins produced by the original developer could be included and used through the ShadowPad controller.
On the other hand, even if the control interface of a plugin is listed in the menu, not every available plugin is embedded in the ShadowPad samples built by the controller by default. There is no configuration in the builder to allow the user to choose which plugins are compiled into the generated sample, so this setting can only be managed by the developer of the controller.
If ShadowPad was not originally designed as an open framework, the following question is whether it is freely shared with or sold to its users. The possible author ‘whg’ – and one of his close affiliates, Rose – have been monetizing their malware development and hacking skills since the early 2000s. Both individuals sold self-developed malware, and Rose offered services such as software cracking, penetration testing and DDoS attacks. If ShadowPad was developed by them or their close affiliates, it is more likely to be sold to – rather than freely shared with – other users under this context.
Selling the Plugins Separately Rather than Giving a Full Bundle by Default
The available functionalities to ShadowPad users are highly controlled by the seller of ShadowPad. Looking deeply into the plugin numbers and the distribution of different plugins embedded in around a hundred samples, we assessed that the seller is likely selling each plugin separately instead of offering a full bundle with all of the currently available plugins.
The number of samples grouped by the number of plugins in each sample
The image above groups the samples by the number of the plugins embedded in them. Most of the samples contain less than nine plugins with the following plugins embedded: Root, Plugins, Config, Install, Online, TCP, HTTP, UDP and DNS. This set of plugins can only support the installation of backdoors and communications with C&C servers, without providing further functionality.
What Threat Actors Are Using Shadowpad?
ShadowPad is sold privately to a limited set of customers. SentinelOne has identified at least five activity clusters of ShadowPad users since 2017:
APT41
Tick & Tonto Team
Operation Redbonus
Operation Redkanku
Fishmonger
In the full report, we discuss each in turn. Here, we will limit our observations to the most interesting points related to APT41.
APT41 is the accepted naming convention for the activities conducted by two spinoffs of what was once referred to as ‘Winnti’, sub-groups – BARIUM (Tan Dailin aka Rose and Zhang Haoran) and LEAD (Chengdu 404 Network Technology Co., Ltd).
All of the individuals are based in Chengdu, Sichuan. Rose (aka “凋凌玫瑰”), Zhang Haoran, and Jiang Lizhi (aka “BlackFox”, one of the persons behind Chengdu 404) were coworkers between 2011 and 2017, while Rose and BlackFox knew each other since at least 2006.
Rose started his active collaboration on malware development with whg, the author of PlugX, when he was a member of the hacking group NCPH back in 2005. They developed “NCPH Remote Control Software” together until 2007. The executable of the controller was freely shared on NCPH websites, but they also declared that the source code was for sale.
NCPH 5.0 Remote Control Software, developed back in 2005, was powered by whg and RoseRose and his friends sold the source code of “NCPH remote control software” on NCPH forum
BARIUM (Rose and Zhang Haoran) were one of the earliest threat groups with access to ShadowPad. Aside from some smaller-scale attacks against the gaming industry, they were accountable for several supply chain attacks from 2017 to 2018. Some of their victims included NetSarang, ASUS, and allegedly, CCleaner.
Another subgroup, LEAD, also used ShadowPad along with other backdoors to attack victims for both financial and espionage purposes. They were reported to attack electronic providers and consumers, universities, telecommunication, NGO and foreign governments.
Considering the long-term affiliation relationship between Rose and whg, we suspect that Rose likely had high privilege access to – or was a co-developer of – ShadowPad, and other close affiliates in Chengdu were likely sharing resources. This could also explain why BARIUM was able to utilize a special version of ShadowPad in some of their attacks.
Conclusion
The emergence of ShadowPad, a privately sold, well-developed and functional backdoor, offers threat actors a good opportunity to move away from self-developed backdoors. While it is well-designed and highly likely to be produced by an experienced malware developer, both its functionalities and its anti-forensics capabilities are under active development. For these threat actors, using ShadowPad as the primary backdoor significantly reduces the costs of development.
For security researchers and analysts tracking China-based threat actors, the adoption of the “sold – or cracked – commercial backdoor” raises difficulties in ascertaining which threat actor they are investigating. More systematic ways – for instance, analysis on the relationship between indicators, long-term monitoring on the activities and campaigns – need to be developed in order to carry out analytically-sound attribution. Any claim made publicly on the attribution of ShadowPad users requires careful validation and strong evidentiary support so that it can help the community’s effort in identifying Chinese espionage.
Read the full report for an extended discussion, full Indicators of Compromise and other technical indicators.
Hive is a double-extortion ransomware group that first appeared in June 2021.
The group is notable in its undiscerning choice of targets, having no limits when it comes to healthcare providers and hospitals, as evidenced in a recent attack on Memorial Health System hospitals in Ohio.
Hive ransomware is written in Go to take advantage of the language’s concurrency features to encrypt files faster.
This report offers an overview of Hive TTPs as well as a reverse engineering deep dive into the ransomware payloads.
Hive remains active with as many as 30 victim companies listed on its Hive Leaks onion site at the time of writing.
Background
While many active ransomware groups have committed to forgoing attacks on medical targets in deference to the current global situation, Hive is not one of them. On August 15, 2021, news broke of a Hive campaign against Memorial Health System, an Ohio healthcare provider. As a result, the hospital was forced to advise some patients to seek treatment at separate facilities.
While some ransomware attacks hitting public health and critical infrastructure targets can be the result of a shotgun approach to targetting – mass phishing campaigns that execute malware blindly on victim devices without awareness of the victim environment – that is not the case with Hive. This is a human-operated ransomware attack designed to take input from the command line, indicating the attackers are both aware of the environment and tailoring their attacks for maximum impact.
Memorial Health Systems open statement on ransomware attack
Who is Hive?
Hive or “HiveLeaks” is a relatively new ransomware outfit that made its appearance on the scene in late June, 2021. Hive is yet another double extortion group, making their money off of a two-pronged attack: exfiltrating sensitive data before locking up the victims’ systems. This allows them to pressure the victim into paying greater sums than a conventional ransomware attack as they also face the threat of a mass leak of sensitive data. Hive’s schemes have proven successful so far as multiple leaks are currently posted on their victim blog. As of the time of writing, there are 30 companies currently named on the HiveLeaks site.
HiveLeaks site showing the timer before releasing victim files
We can’t put the toothpaste back in the tube for Memorial Health Systems, but we can at least contribute a breakdown of the Hive operators’ preferred techniques and a deep dive into their ransomware toolkit to help other potential victims.
Technical Analysis
Initial acces can vary. Cobalt Strike implants are most often the tool of choice. They are delivered via phishing or emails in order to establish initial access. These beacons maintain persistence and allow the operators to expand their reach within the compromised environment. They are also used to launch the Hive payloads.
Recent campaigns opt for the use of ConnectWise. ConnectWise is a legitimate commercial remote administration tool that has been abused by multiple ransomware operators in recent years. This allows for persistence and management of their malware in environments where Cobalt Strike hasn’t been successful.
Once inside, attackers will attempt to dump credentials by way of consvcs.dll (MinDump) though rundll32.exe:
Windowssystem32cmd.exe /C rundll32.exe
WindowsSystem32comsvcs.dll MinDump 752 lsass.dmp full
Additionally, WDigest may be manipulated to allow for the caching of cleartext credential data:
Additional tools like ADRecon may be used to further understand and traverse the compromised Active Directory (AD) environment. ADRecon is an open-source tool designed to do just that– to map, traverse and enumerate an AD environment.
The Hive Payload
While the tools, techniques, and procedures mentioned above are fairly standard for ransomware groups these days, Hive utilizes their own closed-source ransomware. The payloads are written in Go and packed with UPX. After unpacking, the ransomware itself is over 2MB in size owing to the way Go packages statically-link all dependencies to create a reliably portable executable.
The developers are taking advantage of some of the native benefits of Go, particularly the ability to implement easy and reliable concurrency. On the other hand, Go is known for enabling easy cross-compilation across different operating systems but the manner in which Hive implements its functionality makes it Windows-specific, at this time.
The ransomware is designed to take input from the command line, indicating that it’s meant to be run directly by an operator or a script containing the desired parameters. The available flags are as follows.
Flags used by Hive Ransomware
These flags are largely self-explanatory with the exception of the final option, no-cleanpollDesc. This refers to a final phase in the ransomware’s functionality that looks for a file named swap.tmp in all logical drives and deletes it before the ransomware exits. The developers refer to this as ‘cleaning space’. At this time we don’t know what this file does, whether it’s a component generated during their operations, a native Windows file, or perhaps a reference to incomplete cross-platform functionality intended for future builds.
Go malware is usually considered difficult to reverse engineer, primarily due to the wealth of tangentially-related imported code baked into every executable. It’s important to isolate the code contributed by the malware developers. In this case, Hive devs contributed four packages orchestrated by the main() function: encryptor, keys, winutils, and config.
Custom packages under ‘google.com’ parent directory
Cursory examination might miss these as they’re housed under a parent package named google.com, perhaps to give the appearance that these are standard packages.
The main function parses the flags provided by the operator and before initializing the ransomware functionality under encryptor.NewApp(). First it generates and exports the encryption keys and generates the ransom note. It directs the victim to a password-protected Onion domain:
The main functionally is housed under encryptor.(*App).Run(), which does the following:
App.ExportKeys() wraps around standard go crypto functions, which it uses to generate RSA keys. A key file is exported.
MountPoints() enumerates different types of drives and appends them to a slice (a dynamically-sized array in Go). This includes native logical drives, removable drives, and remote shares.
Based on the kill flag, the malware proceeds to kill processes matching the regex provided. If no custom value is provided, the following default is used:
"bmr|sql|oracle|postgres|redis|vss|backup|sstp"
Based on the stop flag, the malware connects to the Windows service control manager and proceeds to stop services matching the regex provided.
The malware creates a batch file to self-delete with the filename hive.bat, removing its own components from the disk via a new process.
timeout 1 || sleep 1
del "C:Usersadmin1Desktophmod4.exe"
if exist "C:Usersadmin1Desktophmod4.exe" goto Repeat
del "hive.bat"
It creates a batch file to delete shadow copies under the filename shadow.bat and executes it as a separate process.
vssadmin.exe delete shadows /all /quiet
del shadow.bat
In order to take advantage of Go’s concurrency features, the Hive devs run a Notify() function that is meant to watch the WaitGroup that keeps track of the parallel threads. As long as there are threads pending, this function will keep the program running.
Now onto the real business of ransomware. ScanFiles() will populate a list of absolute filepaths fed into a channel (a queue of sorts). EncryptFiles() will then spawn threads that each take a file from that queue and encrypt it. This concurrency feature is the main advantage of writing this ransomware in Go and allows for much faster file encryption.
Finally, the devs make sure to erase the encryption key from memory.
Ransom notes are deposited into each folder containing encrypted files (skipping the C:windows) directory.
The ‘HOW_TO_DECRYPT.TXT’ ransom note
The ransom note instructs victims to visit the Hive portal via TOR and login with their assigned unique ID to continue the payment process.
Hive Victim Portal
Each infection campaign is assigned unique credentials available in the ransom note. This portal leads the victim to the standard ransomware ‘support’ area where they can upload freebie test files, communicate with their attackers, and receive their decryptor should they choose to pay (which, in an ideal world, they shouldn’t).
Conclusion
As these attacks continue to escalate and become more egregious, the need for true attack ‘prevention’ is all the more critical. While well-maintained and tested backup strategies are a must, they are not enough in these double-extortion cases.
Once executed, most modern ransomware will go after backup and storage volumes in fairly smart ways. Many have even evolved to target specific NAS devices and platforms. Some groups will bypass the encryption phase altogether and opt for pilfering data to openly extort victims with. While the latter scenario may seem preferable due to a lack of disruption, the reputational damage, potential liability, and threat to business viability remains. Hence our emphasis on prevention.
We urge all defenders to explore and embrace modern endpoint protection technologies that go beyond static checks, basic signatures, and other outdated components. Contextual awareness and automated behavioral classification are among the most powerful weapons defenders should avail themselves of.
In our previous foray into macOS malware reverse engineering, we guided those new to the field through the basics of static and dynamic analysis using nothing other than native tools such as strings, otool and lldb. In this new series of posts, we move into intermediate and more advanced techniques, introducing you to further tools and covering a wide range of real-world malware samples from commodity adware to trojans, backdoors, and spyware used by APT actors such as Lazarus and OceanLotus. We’ll walk through problems such as beating anti-analysis and sandbox checks, reversing encrypted strings, intercepting C2 comms and more.
We kick off with a walk-through on how to rapidly triage a new sample. Analysts are busy people, and the majority of malware samples you have to deal with are neither that interesting nor that complicated. We don’t want to get stuck in the weeds reversing lots of unnecessary code only to find out that the sample really wasn’t worth that much effort!
Ideally, we want to get a sample “triaged” in just a few minutes, where “triage” means that we understand the basics of the malware’s behavior and objectives, collecting just enough data to be able to effectively hunt for related samples and detect them in our environments. For those rarer samples that pique our interest and look like they need deeper analysis, we want our triage session to give an overall profile of the sample and indicate areas for further investigation.
Why Use radare2 (r2) for macOS Malware Analysis?
For rapid triage, my preferred tool is radare2 (aka r2). There are many introductory blogs on installing and using r2, and I’m not going to cover that material here. Such posts will serve you well in terms of learning your way around the basics of installing and using the tool if it’s completely new to you.
However, most such posts are aimed at CTF/crackme readers and typically showcase simple ELF or PE binaries. Very few are aimed at malware analysts, and even fewer still are aimed at macOS malware analysts, so they are not much use to us from a practical point of view. I’m going to assume that you’ve read at least one or two basic intro r2 posts before starting on the material below. For a rare example of r2 introductory material using Mach-O samples (albeit not malware), I recommend having a look at these two helpful posts: 1, 2.
Before we dive in, I do want to say a little bit about why r2 is a good choice for macOS malware analysis, as I expect at least some readers are likely already familiar with other tools such as IDA, Ghidra and perhaps even Hopper, and may be asking that question from the outset.
Radare2 is an extremely powerful and customizable reversing platform, and – at least the way I use it – a great deal of that power comes from the very feature that puts some people off: it’s a command line tool rather than a GUI tool.
Because of that, r2 is very fast, lightweight, and stable. You can install and run it very quickly in a new VM without having to worry about dependencies or licensing (the latter, because it’s free) and it’s much less likely (in my experience) to crash on you or corrupt a file or refuse to start. And as we’ll see in the tips below, you can triage a binary with it very quickly indeed!
Moreover, because it’s a command line tool, it integrates very easily with other command line tools that you are likely familiar with, including things like grep, awk, diff and so on. Other tools typically require you to develop separate scripts in python or Java to do various tailored tasks, but with r2 you can often accomplish the same just by piping output through familiar command line tools (we’ll be looking at some examples of doing that below).
Finally, because r2 is free, multi-platform and runs on pretty much anything at all that can run a terminal emulator, learning how to reverse with r2 is a transferable skill you can take advantage of anywhere.
Enough of the hard sell, let’s get down to triaging some malware! For this post, we’re going to look at a malware sample called OSX.Calisto. Be sure to set up an isolated VM, download the sample from here (password:infect3d) and install r2.
Then, let’s get started!
1. Fun with Functions, Calls, XREFS and More
Our sample, OSX.Calisto, is a backdoor that tries to exfiltrate the user’s keychain, username and clear text copy of the login password. The first tip about using r2 quickly is to load your sample with the -AA option, like so:
% r2 -AA calisto
Load and analyse macOS malware sample with radare2
This performs the same analysis as loading the file and then running aaa from within r2. It’s not only faster to do it in one step, it also cuts out the possibility of forgetting to run the analysis command after loading the binary.
Now that our Calisto sample is loaded and analysed, the first thing that we should do is list all the functions in verbose mode with afll. What is particularly useful about this command is that it gives a great overview of the malware. Not only can we see all the function calls, we can see which are imports, which are dead code, which are making the most system calls, which take the most (or least) arguments, how many variables each declares and more. From here, we are in a very good position to see both what the malware does and where it does it.
List all functions, displaying stats for calls, locals, args, and xrefs for each
Even from just the top of that list, we can see that this malware makes a lot of calls to NSUserName. Typically, though, we will want to sort that table. Although r2 has an internal function for sorting the function table (aflt), I have not found the output to be reliable.
Fortunately, there is another way, which will introduce us to a more general “power feature” of r2. This is to pipe the output of afll through awk and sort. Say, for example, we would like to sort only select columns (we don’t want all that noisy data!):
Here we pipe the output through awk, selecting only the columns we want and then pipe and sort on the third column (number of calls). We add the -n option to make the sort numerical. We can reverse the sort with -r.
Function table sorted by calls
Note that we never left r2 throughout this whole process, making the whole thing extremely convenient. If we wanted to do the same and output the results to file, just do that as you would normally on the command line with a > <path_to_file>.
2. Quickly Dive Into a Function’s Calls
Having found something of interest, we will naturally want to take a quick look at it to see if our hunch is right. We can do that rapidly in a couple of ways as the next few tips will show.
Normally, from that function table, it would make sense to look for functions that have a particular profile such as lots of calls, args, and/or xrefs, and then look at those particular functions in more detail.
Back in our Calisto example, we noted there was one function that had a lot of calls: sym.func.100005620, but we don’t necessarily want to spend time looking at that function if those calls aren’t doing anything interesting.
We can get a look at what calls a function makes very quickly just by typing in a variant of the afll command, aflm. You might want to just punch that in and see what it outputs.
aflm
Yeah, useful, but overwhelming! As we noted in the previous section, we can easily filter things with command line tools while still in r2, so we could pipe that output to grep. But how many lines should we grep after the pattern? For example, if you try
aflm | grep -A 100 5620:
You’ll shoot way over target, because although there may be more calls in that function, aflm only lists each unique call. A better way is to pipe through sed and tell sed to stop piping when it hits another colon (signalling another function listing).
aflm | sed -n ‘/5620:/,/:/p’
The above command says “search for the pattern “/5620:/”, keep going (“/,/”) until you find the next “/:/”. The final “/p” tells sed to print all that it found.
You’ll get an output like this:
Sorting output from radare2
Awesome! Now we can see all the calls that this huge function makes. From that alone we can infer that this function appears to grab the User name, does some string searching, possibly builds an array out of what it finds, and then uploads some data to a remote server! And we haven’t even done any disassembly yet!
3. Strings on Steroids
At this point, we might want to go back to the function table and repeat the above steps on a few different functions, but we also have another option. Having seen that NSUserName is called on multiple occasions, we might want to look more closely at how the malware is interacting with the user. As we explained in our previous guide on reversing macOS malware, extracting strings from a binary can give you a very good insight into what the malware is up to, so much so that some malware authors take great efforts to obfuscate and encrypt the binary’s strings (something we’ll be looking at in a later post). Fortunately, the author of Calisto wasn’t one of those. Let’s see how we can use r2 to help us with string analysis.
The main command for dumping strings is
izz
However, that dump isn’t pretty and doesn’t make for easy analysis. Fortunately, there’s a much nicer way to look at and filter strings in radare2. Let’s try this instead:
izz~...
The tilde is r2’s internal “grep” command, but more importantly the three periods pipe the string dump into a “HUD” (Heads Up Display) from where we can type filter characters. For example, after issuing the above command, type a single “/” to reveal all strings (like paths and URLs, for example) containing a forward slash. Backspace to clear that and try other filters in turn like “http” and “user”. As the images below show, we quickly hit pay dirt!
Filtering strings in radare2
The first image above looks like a lead on the malware’s C2 addresses, while the second shows us what looks very much like a path the malware is going to write data to. Both of these are ideal for our IoCs and for hunting, subject to further confirmation.
4. Fast Seek and Disassembly
What we’ve found after just a few short commands and a couple of minutes of triaging our binary is very promising. Let’s see if we can dig a little deeper. Our output from the HUD gives us the addresses of all those strings. Let’s take a look at the address for what looks like uploading exfiltrated data to a C2:
http://40.87.56.192/calisto/upload.php?username="
From the output, we can see that this string is referenced at 0x1000128d0. Let’s go to that address and see what we have. First, double-click the address to select it then copy it with Cmd-C. To escape the HUD, hit ‘return’ so that you are returned to the r2 prompt.
Next, we’ll invoke the ‘seek’ command, which is simply the letter s, and paste the address after it. Hit ‘return’. Type pd (print disassembly) and scroll up in your Terminal window to get to the start of the disassembly.
Seeking in radare2
The disassembly shows us where the string is called via the xref at the top. Let’s again select and Cmd-C that address and do another seek. After the seek, this time we’ll do pdf.
Disassembling a function in radare2
The difference is that pdf will disassemble an entire function, no matter how long it is. On the other hand, pd will disassemble a given number of instructions. Thus, it’s good to know both. You can’t use pdf from an address that isn’t a function, and sometimes you want to just disassemble a limited number of instructions: this is where pd comes in handy. However, when what you want is a complete function’s disassembly, pdf is your friend.
The pdf command gives you exactly what you’d expect from a disassembler, and if you’ve done any reversing before or even just read some r2 intros as suggested above, you’ll recognize this output (as pretty much all r2 intros start with pdf!). In any case, from here you can get a pretty good overview of what the function does, and r2 is nicer than some other disassemblers in that things like stack strings are shown by default.
You might also like to experiment with pdc. This is a “not very good” pseudocode output. One of r2’s weakpoints, it has to be said, is the ability to render disassembly in good pseudocode, but pdc can sometimes be helpful for focus.
Finally, before we move on to the next tip, I’m just going to give you a variation on something we mentioned above that I often like to do with pdf, which is to grep the calls out of it. This is particularly useful for really big functions. In other words, try
pdf~call
for a quick look at the calls in a given function. You can also get r2 to give you a summary of a function with pds.
5. Rabin2 | Master of Binary Info Extraction
When we discussed strings, I mentioned the izz command, which is a child of the iz command, which in turn is a child of r2’s i command. As you might have guessed, i stands for information, and the various incantations of i are all very useful while you’re in the middle of analysis (if you happen to forget what file you are analyzing, i~file is your friend!).
Some of the useful variants of the i command are as follows:
get file metadata [i]
look at what libraries it imports [ii]
look at what strings it contains [iz]
look at what classes/functions/methods it contains [icc]
find the entrypoint [ie]
However, for rapid triage, there is a much better way to get a bird’s eye view of everything there is to know about a file. When you installed r2, you also installed a bunch of other utilities that r2 makes use of but which you can call independently. Perhaps the most useful of these is rabin2. In a new Terminal window, try man rabin2 to see its options.
While we can take advantage of rabin2’s power via the i command in r2, we can get more juice out of it by opening a separate Terminal window and calling rabin2 directly on our malware sample. For our purposes, focused as we are in this post on rapid triage, the only rabin2 option we need to know is:
% rabin2 -g <path_to_binary>
Triaging macOS malware with rabin2
The -g option outputs everything there is to know about the file, including strings, symbols, sections, imports, and such things like whether the file is stripped, what language it was written in, and so on. It is essentially all of the options of r2’s i command rolled into one (if it’s possible to make r2 punch out all of that in one command, I’m not aware of how).
Strangely, one of the best outputs from rabin2 is when its -g option outputs almost nothing at all! That tells you that you are almost certainly dealing with packed malware, and that in itself is a great guide on where to go next in your investigation (we’ll be looking at packed files in a later post).
Meanwhile, it’s time to introduce our last rapid analysis pro trick, Visual Graph mode!
6. Visual Graph Mode
For those of you used to a GUI disassembler, if you’ve followed this far you may well be thinking… “ahuh…but how do I get a function call graph from a command line tool?” A graph is often a make or break deal when trying to triage malware rapidly, and a tool that doesn’t have one is probably not going to win many friends. Fortunately, r2 has you covered!
Returning to our r2 prompt, type VV to enter visual graph mode.
radar2 graph mode
Visual graph mode is super useful for being able to trace logic paths through a malware sample and to see which paths are worth further investigation. I will readily admit that learning your way around the navigation options takes some practice. However, it is an extremely useful tool and one which I frequently return to with samples that attempt to obstruct analysis.
The options for using Visual Graph mode are nicely laid out in this post here. Once you learn your way around, it’s relatively simple and powerful, but it’s also easy to get lost when you’re first starting out. Like Vi and Vim, inexperienced users can sometimes find themselves trapped in an endless world of error beeps with r2’s Visual Graph mode. However, as with all things in r2, whenever you find yourself “stuck”, hit q on the keyboard (repeatedly, if needs be). If you find yourself needing help, hit ?.
I highly recommend that you experiment with the Calisto sample to familiarize yourself with how it works. In the next post, we’ll be looking in more detail at how Visual Graph mode can help us when we tackle anti-analysis measures, so give yourself a heads up by playing around with it in the meantime.
Conclusion
In this post, we’ve looked at how to use radare2 to quickly triage macOS malware samples, seen how it can easily be integrated with other command line tools most malware analysts are already familiar with, and caught a glimpse of its visual graph mode.
There’s much more to learn about radare2 and macOS malware, and while we hope you’ve enjoyed the tips we’ve shared here, there’s many more ways to use this amazing tool to achieve your aims in reversing macOS malware. We hope you’ll join us in the next post in this series as we continue our exploration of intermediate and advanced macOS malware analysis techniques.
EGoManiac is responsible for the previously reported ‘Octopus Brain’ campaign, where the operators interdicted the machines of OdaTV journalists to place malware and incriminating documents, effectively framing them before arrest.
Our research connects Octopus Brain to a toolkit called Rad, in development as early as 2010 and used until 2015.
Rad samples use hardcoded email addresses for exfiltration.
One of those email addresses is cited in connection to the prosecution of rogue members of the Turkish National Police along with executives of a company called ‘Datalink Analiz’. They refer to Rad as ‘HORTUM’.
Following the trail of ‘Datalink Analiz’, we suspect that EGoManiac activity includes the use of Hacking Team’s Remote Control System (RCS) contracted under this same front company with a series of irregularities as early as 2011.
In 2013, a report emerged on the use of RCS against a Turkish victim in the United States. The victim voiced an unverified suspicion that its use represented the unsanctioned interests of rogue Gülenist elements within the Turkish government.
In this post, we provide an abridged version of our in-depth investigation into the activities of this unscrupulous threat actor. The full report provides detailed technical breakdowns of malware samples, full IoCs and hunting rules along with further attribution details.
In the world of cyberespionage research, the human-interest element is often lost amidst a barrage of technical indicators. The absence of a human dimension can make our research seem overly technical and dry, something we write for defenders to block and other researchers to enjoy. When we can see the impact that some of these campaigns have on civil society and the weakening of public institutions, it invokes a certain doggedness that won’t let sleeping dogs lie. EGoManiac is one that’s been in the back of our heads for the past five years. The research involved multiple dead ends, false starts, and layers of conspiratorial mystery.
What we refer to as EGoManiac is a cluster of two notable campaigns starting as early as 2010. The first campaign came to be known in research circles as ‘Octopus Brain’, based on the Turkish strings Ahtapot and Bejin left in the malware. This original campaign used a combination of publicly available RATs (including Turkojan and Bandook) as well as the closed-source Ahtapot, with delivery methods ranging from malicious documents to personal visits by the attackers.
Our initial awareness of this case came from Turkish court documents surrounding arrests of journalists at OdaTV. Much greater detail came to light thanks to the excellent work of the folks at Arsenal Consulting. Their forensic investigation not only proved the presence of the malware and the physical interdiction of the victim systems, but also established the attacker’s access as the definitive source of the incriminating documents on those systems that were then used to justify arrests by the Turkish National Police. The journalists were ultimately acquitted by a court in 2017—six years after the attacks.
This scenario is one of the often-ignored dirty edge cases of ‘lawful intercept’ malware, stated plainly: what’s the expectation of evidential integrity when it comes to an infected device? This question is currently playing out further in the Bhima Koregaon case in India, where it appears malware was used to upload incriminating letters onto the victim’s machine.
While these particular operators resorted to physically tampering with the devices they were monitoring, there’s little keeping malware operators from placing incriminating or damaging files on systems infected with malware that has file download capabilities, as most rudimentary malware does.
In the face of such an unscrupulous actor, we are left to wonder if this activity is part of a cluster we already track, and if not, what else has this actor been up to in the shadows? Octopus Brain provided few answers. Despite finding a handful of Ahtapot modules, there were no newer samples nor connections to other toolkits. The trail went cold…until now.
Experiments in Innovative Pivoting
As threat hunting technology continued to improve, there were different attempts to once again pick up the scent of the attackers behind the Octopus Brain campaign. Code similarity analysis is one of the favorite tools in our research arsenal. However, initial attempts to cluster new samples based on shared unique code snippets were not fruitful.
We decided to take a different approach. Rather than focusing on unique code snippets, we can instead focus on a bulk of shared common code as a way of profiling the development environment that produced the samples and attempt to find other samples produced in the same way: same compiler, same optimizations, relying on the same statically-linked libraries, etc. Limited testing of this method has yielded positive results under specific circumstances—like allowing us to cluster a set of samples based off of the analysis of a single original sample and without needing to spend cycles conducting extensive goodware testing.
Ahtapot campaign components connect to newer Rad toolkit
To our surprise, applying this experimental approach to Octopus Brain yielded results. By generating a rule based off of the bulk of common code of Ahtapot components, we stumbled upon a set of samples we’ll call ‘Rad’, based on a persistent typo in symbol paths left within the binaries.
Expanding on this initial finding, we found a cluster of more than 50 samples and subcomponents for a modular espionage toolkit almost entirely undetected at the time of discovery.
EGoManiac’s ‘Rad’ Toolkit
Rad is a modular espionage malware toolkit built around the POCO C++ cross-platform development libraries. The design entails a form of organized development but not a particularly savvy or sophisticated one at that. POCO is doing most of the heavy lifting. Functionality is split into modules contained within a ‘RadApplicationInstaller’ and orchestrated by a ‘RadStarter’ module that takes its cues from an encrypted configuration XML file. All of the Rad samples we’ve found rely on email exfiltration with a hardcoded address belonging to either Gmail, Yandex, or Woxmail (defunct at the time of writing).
Rad toolkit components
The execution flow of the Rad toolkit is straightforward. wsms.exe (RadStarter) is the main module that runs from a registry key set by the installer. It, in turn, runs the other modules as separate processes. Each process represents different functionality, including a keylogger, hot mic recorder, browser information extractor, screen capture module, file search, and a communication module for exfiltration. More details are provided in the full report.
Who is EGoManiac?
Attribution based solely on technical indicators is complicated and inexact. Most technical indicators are subject to modification and require interpretation based on limited visibility. Lacking a greater understanding of local context and closed-source intelligence, it’s difficult to extend attribution beyond abstract entities (like an APT group name) to specific people or organizations.
On the surface, EGoManiac activity revolves around a Turkish nexus. The malware is riddled with Turkish language, lures are written in Turkish, victims are Turkish and relevant to local politics. The connection to Ahtapot and the OdaTV incident entails the actor’s ability to physically interdict systems within Turkey. Additionally, most PDB paths for Rad components have a root folder of ‘EGM’, from which we derived the name ‘EGoManiac’.
EGoManiac’s Rad toolkit relies on hardcoded email addresses for communication. Obfuscated logs and other exfiltrated materials are sent to the following emails across multiple service providers:
While email comms might usually lead to another research dead-end, the address [email protected] raised an interesting connection.
In 2016, Turkish websites reported sparse details of an ongoing attempt to prosecute members of the Turkish national police and executives of an IT company called ‘Datalink’ suspected of leaking information on active police operations. The leaks were reportedly used by FETO/Gülenist movement social media accounts to fuel conspiratorial elements in an ongoing power struggle within the country.
Reports cite the use of spyware called ‘HORTUM’ (roughly translated as ‘garden hose’) to siphon data from infected machines within public institutions in Turkey including the Intelligence department of the General Directorate of Security (EGM). Some of the reporting mistakenly conflates HORTUM with Hacking Team’s RCS. The siphoned data was sent to [email protected], and from there allegedly redistributed by Datalink. The capabilities of HORTUM and its communication methods match those of EGoManiac’s Rad, including the hardcoded Woxmail address.
We cannot independently verify the veracity of the initial reporting. An independent investigation to that effect was conducted by Kim Zetter, who obtained extensive details including a report by the prosecutor handling the case. Taking the information we have at face value, we uncover another possible facet of the EGoManiac story.
The Hacking Team Connection
As early as 2012, victims of Hacking Team’s Remote Control System (RCS) ‘Da Vinci’ began to show up in Turkey. In 2013, Wired reported that a woman in the United States was targeted with RCS. The victim suspected that she was targeted by Gülenist elements that had infiltrated the Turkish government. However, Hacking Team continued to assert that it only sells its tools to governments and did not confirm Turkey’s status as a customer. Now, in the aftermath of Phineas Fisher’s devastating hack-and-leak operation against Hacking Team, we can independently confirm that Turkey was in fact a customer of Hacking Team at the time, but who exactly was their customer in Turkey?
The leaked Hacking Team treasure trove contains communications with officials claiming to be a part of the Turkish National Police as early as 2011. Citing problems with their mail server, they proceed to use three Gmail accounts to plan their purchase of RCS. A Gmail account is also used for communication with the Hacking Team support portal.
Hacking Team officials note further irregularities as the first deal goes through. Though the purchase is intended under the umbrella of a UAE-based shell company (‘Foresys Information Technology-FZE’), Hacking Team receives payment from a company registered in Istanbul: ‘Datalink Analiz LTD’.
Prevalence of EGoManiac-related malware families by compilation timestamp
To be thorough, we chart the use of Hacking Team RCS by the Turkish National Police (see Appendix C in the full report) based on the company’s internal watermarking scheme used to track the origin of leaked samples among their customer base. The graphic above notes the coincidental cadence of the use of the different malware families related to the EGoManiac cluster. However, we can’t go as far as to equate the two clusters without resolving the murky allegiances of the operators involved.
The connection between the EGoManiac umbrella and this specific sub-cluster of Hacking Team RCS is built on the admittedly thin strand of the ‘Datalink Analiz’ shell company. That thread merited an investigation beyond the purely technical to straighten out an abundance of conspiratorial claims, alleged foreign money laundering, and ambiguous finger pointing.
Conclusion
The case of EGoManiac is far from straightforward. It involves difficult investigative connections that test the boundaries of our visibility, the efficacy of our research tools, and the limits of purely technical attribution. Beyond the technical exercise, it’s a profile of a threat actor willing to spy on both friend and foe and to use that access to malign and entrap journalists without compunction. While this particular intrusion set is outdated, the questions it raises speak to the friction between the unsupervised governmental use of malware and the integrity of public institutions, rule of law, and evidentiary standards. They are more relevant now than ever before.
ZLoader (also known as Terdot) was first discovered in 2016 and is a fork of the infamous Zeus banking trojan. It is still under active development. A multitude of different versions have appeared since December 2019, with an average frequency of 1-2 new versions released each week.
ZLoader is a typical banking trojan which implements web injection to steal cookies, passwords and any sensitive information. It attacks users of financial institutions all over the world and has also been used to deliver ransomware families like Egregor and Ryuk. It also provides backdoor capabilities and acts as a generic loader to deliver other forms of malware. Newer versions implement a VNC module which permits users to open a hidden channel that gives the operators remote access to victim systems. ZLoader relies primarily on dynamic data exchange (DDE) and macro obfuscation to deliver the final payload through crafted documents.
A recent evolution of the infection chain included the dynamic creation of agents, which download the payload from a remote server. The new infection chain observed by SentinelLabs demonstrates a higher level of stealth by disabling Windows Defender and relying on living-off-the-land binaries and scripts (LOLBAS) in order to evade detection. During our investigation, we were also able to map all the new ZLoader C2 infrastructure related to the ‘Tim’ botnet and identify the scope of the campaign and its objectives, which primarily involved stealing bank credentials from customers of European banks.
Overview of the ZLoader infection chain
Technical Analysis
The malware is downloaded from a Google advertisement published through Google Adwords. In this campaign, the attackers use an indirect way to compromise victims instead of using the classic approach of compromising the victims directly, such as by phishing.
We observed the following pattern of activity that leads to infection:
The user performs a search on www.google.com to find a website to download the required software from; in our case, we observed a search for “team viewer download”.
The user clicks on an advertisement shown by Google and is redirected to the fake TeamViewer site under the attacker’s control.
The user is tricked into downloading the fake software in a signed MSI format.
Once the user clicks on the advertisement, it will redirect through the aclk page. This redirect demonstrates the attackers usage of Google Adwords to gain traffic:
After further navigation (and redirects), the malicious Team-Viewer.msi is downloaded from the final URL hxxps://team-viewer.site/download/Team-Viewer.msi.
The downloaded file is a fake TeamViewer installer signed on 2021-08-23 10:07:00. It appears that the cybercriminals managed to obtain a valid certificate issued by Flyintellect Inc, a Software company in Brampton, Canada. The company was registered on 29th June 2021, suggesting that the threat actor possibly registered the company for the purpose of obtaining those certificates.
Pivoting from this certificate, we were able to spot other samples signed with the same certificate. These other samples suggest that the attackers had multiple campaigns ongoing beyond TeamViewer and which included fakes such as JavaPlug-in.mis, Zoom.mis, and discord.msi.
At the time of writing, these four samples have no detections on VirusTotal (a complete list of IoCs can be found in the full report).
New Zloader Infection Chain Bypass Defences
The .msi file is the first stage dropper which runs an installation wizard. It creates random legitimate files in the directory C:\Program Files (x86)\Sun Technology Network\Oracle Java SE. Once the folder has been created, it will drop the setup.bat file, triggering the initial infection chain by executing cmd.exe /c setup.bat.
This initiates the second stage of the infection chain, downloading the dropper updatescript.bat through the PowerShell cmdlet Invoke-WebRequest, from hxxps://websekir.com/g00glbat/index/processingSetRequestBat/?servername=msi. The dropper then executes the third stage with the command cmd /c updatescript.bat.
The third stage dropper contains most of the logic to impair the defenses of the machine. It also drops the fourth stage using a stealthy execution technique. At first, it disables all the Windows Defender modules through the PowerShell cmdlet Set-MpPreference. It then adds exclusions, such as regsvr32, *.exe, *.dll, with the cmdlet Add-MpPreference to hide all the components of the malware from Windows Defender.
At this point the fourth stage dropper is downloaded from the URL hxxps://pornofilmspremium.com/tim.EXE and saved as tim.exe. The execution of tim.exe is done through the LOLBAS command explorer.exe tim.exe. This allows the attacker to break the parent/child correlation often used by EDRs for detection.
The first part of the attack chain
The tim.exe binary is a backdoored version of the Windows utility wextract.exe. This backdoored version contains extra embedded resources with names like “RUNPROGRAM”, “REBOOT”, and “POSTRUNPROGRAM”, among others.
Resources embedded in the tim.exe binary (left) and legit wextract.exe(right)
This backdoored version contains additional code for creating a new malicious batch file with the name tim.bat. It is placed in a temporary directory retrieved with the Win32 function GetTempPath(). It retrieves the content of the resource “RUNPROGRAM” (containing the string value cmd /c tim.bat) and uses it as the command line parameter for the CreateProcess() Win32 function.
The tim.bat file is a very short script that downloads the final ZLoader DLL payload with the name tim.dll from the URL hxxps://pornofilmspremium.com/tim.dll and executes it through the LOLBAS command regsvr32 tim.dll. This allows the attackers to proxy the execution of the DLL through a signed binary by Microsoft.
This dropper downloads the script nsudo.bat from hxxps://pornofilmspremium.com/nsudo.bat and runs asynchronously in parallel with the execution of tim.dll. The script aims to further impair defenses of the machine.
Privilege Escalation and Defense Evasion
The nsudo.bat script performs multiple operations with the goal of elevating privileges on the system and impairing defenses.
At first, it checks if the current context of execution is privileged by verifying the access to the SYSTEM hive. This is done through %SYSTEMROOT%\system32\cacls.exe %SYSTEMROOT%\system32\config\system. If the process in which it runs has no access on that hive it will jump to the label :UACPrompt.
This part of the script implements an auto elevation VBScript that aims to run an elevated process in order to make system changes. The snippet of the script in charge of the UACPrompt feature is as follows:
:UACPrompt
echo Set UAC = CreateObject^("Shell.Application"^) > "%temp%\getadmin.vbs"
set params = %*:"="
echo UAC.ShellExecute "cmd.exe", "/c %~s0 %params%", "", "runas", 1 >> "%temp%\getadmin.vbs"
"%temp%\getadmin.vbs"
del "%temp%\getadmin.vbs"
exit /B
This snippet creates the VBScript getadmin.vbs, runs it and deletes it. Using a VBScript eases the interaction with COM objects. In this case, it instantiates a Shell.Application object and calls the function ShellExecute() to trigger the UAC elevation and the interaction with the AppInfo service.
Once the elevation occurs the script is run with elevated privileges. At this point, the script performs the steps to disable Windows Defender. It does this through a software utility called NSudo renamed as javase.exe, which is downloaded from the URL hxxps://pornofilmspremium.com/javase.exe. The attacker leverages this utility in order to spawn a process with “TrustedInstaller” privileges. This can be abused by the attacker to disable the Windows Defender service even if it runs as a Protected Process Light.
The script downloads the file autorun100.bat from and places it in the startup folder %USERPROFILE%\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup. This script ensures that the WinDefend service is deleted at the next boot through the utility NSudo.
The nsudo.bat script also completely disables UAC by setting the following registry key to 0:
In order to have these changes take effect, the computer is forced to restart. The nsudo.bat script does this with shutdown.exe /r /f /t 00. At this point, the attack chain of the script nsudo.bat is complete.
ZLoader Payload Execution Chain
The tim.dll is the main ZLoader payload that encapsulates the unpacking logic and adds persistence. It is executed through the system signed binary regsvr32.exe.
It first creates a directory with a random name inside %APPDATA% and then creates a copy of itself in the newly created directory. It then adds a new registry key in HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Run. The registry key value contains the command line of the malicious process to spawn on user logon. This ensures that the attacker’s implant survives machine reboots. The DLL execution also relies on the regsvr32 binary. This is an example of the registry key created on a single run of the sample:
Then it starts the unpacking by leveraging a process injection technique known as Thread Hijacking. It contains a small variation but essentially uses the same pattern of Win32 API calls used for Thread Hijacking:
It first creates a new process as a host for the unpacked DLL, and for this sample it uses a new instance of msiexec.exe. Then it allocates and writes 2 RWX memory regions inside the target process. One contains the unpacked version of the DLL XOR’ed with a key; the second, contains some shellcode to decrypt the DLL and jump to the entry point.
The unpacking routine
Once the memory is written in the remote process it sets the new thread context EIP to point to the unpacking routine shellcode and resumes the main thread of msiexec. This is how the hijacking of the main thread occurs. The unpacked DLL is extracted from the memory of msiexec.exe process by dumping the memory address used in the first WriteProcessMemory() call.
We have compared the unpacked DLL with the recent ZLoader payloads and found a similarity score of 92.62%.
Final part of the attack chain
Analyzing The New Zloader C2 Infrastructure
The analyzed sample belongs to the ‘Tim’ Botnet as defined in the malware configuration. Some of the embedded C2s (the full list can be found in the IoC section of the full report) are also shared by the googleaktualizacija ZLoader botnet.
One of the C2s dumped from the infected machine, mjwougyhwlgewbajxbnn[.]com, used to resolve to 194.58.108[.]89 until the 25th of August 2021. As of the 26th of August, however, it points to 195.24.66[.]70.
The IP 194.58.108[.]89 belongs to ASN 48287 – RU-CENTER and seems to deploy many different domains – 350 at the time of writing – forming the new ZLoader infrastructure. Some domains implement the gate.php component, which is a fingerprint of the ZLoader botnet. We noticed during our investigation that all the domains were registered from April to Aug 2021, and they switched to the new IP (195.24.66[.]70) on the 26th of August.
A Targeted Campaign: AU And DE Financial Institutions
The new ZLoader campaign is targeted. The final payload has a list of embedded AU and DE domains, and contains some strings with wildcards used by the malware to intercept specific users’ web requests to bank portals.
From our analysis of the communication patterns related to mjwougyhwlgewbajxbn[.]com, we were able to map most of the source traffic used by the operators of the botnet.
The pornofilmspremium[.]com domain delivers the tim.exe component. The domain was registered on 2021-07-19 (Location RU, ASN: REG RU 197695) and is associated by the community with ZLoader [1, 2]. The email address [email protected][.]com was used to register this domain and a number of others, as detailed in the full report.
Conclusion
The attack chain analyzed in this research shows how the complexity of the attack has grown in order to reach a higher level of stealthiness. The first stage dropper has been changed from the classic malicious document to a stealthy, signed MSI payload. It uses backdoored binaries and a series of LOLBAS to impair defenses and proxy the execution of their payloads.
This is the first time we have observed this attack chain in a ZLoader campaign. At the time of writing, we have no evidence that the delivery chain has been implemented by a specific affiliate or if it was provided by the main operator. SentinelLabs continues to monitor this threat in order to track further activity.
SentinelLabs has discovered a high severity flaw in an HP OMEN driver affecting millions of devices worldwide.
Attackers could exploit these vulnerabilities to locally escalate to kernel-mode privileges. With this level of access, attackers can disable security products, overwrite system components, corrupt the OS, or perform any malicious operations unimpeded.
SentinelLabs’ findings were proactively reported to HP on Feb 17, 2021 and the vulnerability is tracked as CVE-2021-3437, marked with CVSS Score 7.8.
HP has released a security update to its customers to address these vulnerabilities.
At this time, SentinelOne has not discovered evidence of in-the-wild abuse.
Introduction
HP OMEN Gaming Hub, previously known as HP OMEN Command Center, is a software product that comes preinstalled on HP OMEN desktops and laptops. This software can be used to control and optimize settings such as device GPU, fan speeds, CPU overclocking, memory and more. The same software is used to set and adjust lighting and other controls on gaming devices and accessories such as mouse and keyboard.
Following on from our previous research into other HP products, we discovered that this software utilizes a driver that contains vulnerabilities that could allow malicious actors to achieve a privilege escalation to kernel mode without needing administrator privileges.
CVE-2021-3437 essentially derives from the HP OMEN Gaming Hub software using vulnerable code partially copied from an open source driver. In this research paper, we present details explaining how the vulnerability occurs and how it can be mitigated. We suggest best practices for developers that would help reduce the attack surface provided by device drivers with exposed IOCTLs handlers to low-privileged users.
Technical Details
Under the hood of HP OMEN Gaming Hub lies the HpPortIox64.sys driver, C:\Windows\System32\drivers\HpPortIox64.sys. This driver is developed by HP as part of OMEN, but it is actually a partial copy of another problematic driver, WinRing0.sys, developed by OpenLibSys.
The link between the two drivers can readily be seen as on some signed HP versions the metadata information shows the original filename and product name:
File Version information from CFF Explorer
Unfortunately, issues with the WinRing0.sys driver are well-known. This driver enables user-mode applications to perform various privileged kernel-mode operations via IOCTLs interface.
The operations provided by the HpPortIox64.sys driver include read/write kernel memory, read/write PCI configurations, read/write IO ports, and MSRs. Developers may find it convenient to expose a generic interface of privileged operations to user mode for stability reasons by keeping as much code as possible from the kernel-module.
The IOCTL codes 0x9C4060CC, 0x9C4060D0, 0x9C4060D4, 0x9C40A0D8, 0x9C40A0DC and 0x9C40A0E0 allow user mode applications with low privileges to read/write 1/2/4 bytes to or from an IO port. This could be leveraged in several ways to ultimately run code with elevated privileges in a manner we have previously described here.
The following image highlights the vulnerable code that allows unauthorized access to IN/OUT instructions, with IN instructions marked in red and OUT instructions marked in blue:
The Vulnerable Code – unauthorized access to IN/OUT instructions
Since I/O privilege level (IOPL) equals the current privilege level (CPL), it is possible to interact with peripheral devices such as internal storage and GPU to either read/write directly to the disk or to invoke Direct Memory Access (DMA) operations. For example, we could communicate with ATA port IO for directly writing to the disk, then overwrite a binary that is loaded by a privileged process.
For the purposes of illustration, we wrote this sample driver to demonstrate the attack without pursuing an actual exploit:
This ATA PIO read/write is based on LearnOS. Running this driver will result in the following DebugView prints:
Debug logging from the driver in DbgView utility
Trying to restart this machine will result in an ‘Operating System not found’ error message because our demo driver destroyed the first sector of the disk (the MBR).
The machine fails to boot due to corrupted MBR
It’s worth mentioning that the impact of this vulnerability is platform dependent. It can potentially be used to attack device firmware or perform legacy PCI access by accessing ports 0xCF8/0xCFC. Some laptops may have embedded controllers which are reachable via IO port access.
Another interesting vulnerability in this driver is an arbitrary MSR read/write, accessible via IOCTLs 0x9C402084 and 0x9C402088. Model-Specific Registers (MSRs) are registers for querying or modifying CPU data. RDMSR and WRMSR are used to read and write to MSR accordingly. Documentation for WRMSR and RDMSR can be found on Intel(R) 64 and IA-32 Architecture Software Developer’s Manual Volume 2 Chapter 5.
In the following image, arbitrary MSR read is marked in green, MSR write in blue, and HLT is marked in red (accessible via IOCTL 0x9C402090, which allows executing the instruction in a privileged context).
Vulnerable code with unauthorized access to MSR registers
Most modern systems only use MSR_LSTAR during a system call transition from user-mode to kernel-mode:
MSR_LSTAR MSR register in WinDbg
It should be noted that on 64-bit KPTI enabled systems, LSTAR MSR points to nt!KiSystemCall64Shadow.
The entire transition process looks something like as follows:
The entire process of transition from the User Mode to Kernel mode
These vulnerabilities may allow malicious actors to execute code in kernel mode very easily, since the transition to kernel-mode is done via an MSR. This is basically an exposed WRMSR instruction (via IOCTL) that gives an attacker an arbitrary pointer overwrite primitive. We can overwrite the LSTAR MSR and achieve a privilege escalation to kernel mode without needing admin privileges to communicate with this device driver.
Using the DeviceTree tool from OSR, we can see that this driver accepts IOCTLs without ACLs enforcements (note: Some drivers handle access to devices independently in IRP_MJ_CREATE routines):
Using DeviceTree software to examine the security descriptor of the deviceThe function that handles IOCTLs to write to arbitrary MSRs
Weaponizing this kind of vulnerability is trivial as there’s no need to reinvent anything; we just took the msrexec project and armed it with our code to elevate our privileges.
Initially, HP developed a fix that verifies the initiator user-mode applications that communicate with the driver. They open the nt!_FILE_OBJECT of the callee, parsing its PE and validating the digital signature, all from kernel mode. While this in itself should be considered unsafe, their implementation (which also introduced several additional vulnerabilities) did not fix the original issue. It is very easy to bypass these mitigations using various techniques such as “Process Hollowing”. Consider the following program as an example:
int main() {
puts("Opening a handle to HpPortIO\r\n");
hDevice = CreateFileW(L"\\\\.\\HpPortIO", FILE_ANY_ACCESS, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
if (hDevice == INVALID_HANDLE_VALUE) {
printf("failed! getlasterror: %d\r\n", GetLastError());
return -1;
}
printf("succeeded! handle: %x\r\n", hDevice);
return -1;
}
Running this program against the fix without Process Hollowing will result in:
Opening a handle to HpPortIO failed!
getlasterror: 87
While running this with Process Hollowing will result in:
Opening a handle to HpPortIO succeeded!
handle: <HANDLE>
It’s worth mentioning that security mechanisms such as PatchGuard and security hypervisors should mitigate this exploit to a certain extent. However, PatchGuard can still be bypassed. Some of its protected structure/data are MSRs, but since PatchGuard samples these assets periodically, restoring the original values very quickly may enable you to bypass it.
Impact
An exploitable kernel driver vulnerability can lead an unprivileged user to SYSTEM, since the vulnerable driver is locally available to anyone.
This high severity flaw, if exploited, could allow any user on the computer, even without privileges, to escalate privileges and run code in kernel mode. Among the obvious abuses of such vulnerabilities are that they could be used to bypass security products.
An attacker with access to an organization’s network may also gain access to execute code on unpatched systems and use these vulnerabilities to gain local elevation of privileges. Attackers can then leverage other techniques to pivot to the broader network, like lateral movement.
Impacted products:
HP OMEN Gaming Hub prior to version 11.6.3.0 is affected
HP OMEN Gaming Hub SDK Package prior 1.0.44 is affected
Development Suggestions
To reduce the attack surface provided by device drivers with exposed IOCTLs handlers, developers should enforce strong ACLs on device objects, verify user input and not expose a generic interface to kernel mode operations.
Remediation
HP released a Security Advisory on September 14th to address this vulnerability. We recommend customers, both enterprise and consumer, review the HP Security Advisory for complete remediation details.
Conclusion
This high severity vulnerability affects millions of PCs and users worldwide. While we haven’t seen any indicators that these vulnerabilities have been exploited in the wild up till now, using any OMEN-branded PC with the vulnerable driver utilized by OMEN Gaming Hub makes the user potentially vulnerable. Therefore, we urge users of OMEN PCs to ensure they take appropriate mitigating measures without delay.
We would like to thank HP for their approach to our disclosure and for remediating the vulnerabilities quickly.
Disclosure Timeline
17, Feb, 2021 – Initial report
17, Feb, 2021 – HP requested more information
14, May, 2021 – HP sent us a fix for validation
16, May, 2021 – SentinelLabs notified HP that the fix was insufficient
07, Jun, 2021 – HP delivered another fix, this time disabling the whole feature
27, Jul, 2021 – HP released an update to the software on the Microsoft Store
14, Sep 2021 – HP released a security advisory for CVE-2021-3437
14, Sep 2021 – SentinelLabs’ research published
In this second post in our series on intermediate to advanced macOS malware reversing, we start our journey into tackling common challenges when dealing with macOS malware samples. Last time out, we took a look at how to use radare2 for rapid triage, and we’ll continue using r2 as we move through these various challenges. Along the way, we’ll pick up tips on both how to beat obstacles put in place by malware authors and how to use r2 more productively.
Although we can achieve a lot from static analysis, sometimes it can be more efficient to execute the malware in a controlled environment and conduct dynamic analysis. Malware authors, however, may have other ideas and can set up various roadblocks to stop us doing exactly that. Consequently, one of the first challenges we often have to overcome is working around these attempts to prevent execution in our safe environment.
In this post, we’ll look at how to circumvent the malware author’s control flow to avoid executing unwanted parts of their code, learning along the way how to take advantage of some nice features of the r2 debugger! We’ll be looking at a sample of EvilQuest (password: infect3d), so fire up your VM and download it before reading on.
A note for the unwary: if you’re using Safari in your VM to download the file and you see “decompression failed”, go to Safari Preferences and turn off the ‘Open “safe” files after downloading’ option in the General tab and try the download again.
Getting Started With the radare2 Debugger
Our sample hit the headlines in July 2020, largely because at first glance it appeared to be a rare example of macOS ransomware. SentinelLabs quickly analyzed it and produced a decryptor to help any potential victims, but it turned out the malware was not very effective in the wild.
It may well have been a PoC, or a project still in early development stages, as the code and functionality have the look and feel of someone experimenting with how to achieve various attacker objectives. However, that’s all good news for us, as EvilQuest implements several anti-analysis features that will serve us as good practice.
The first thing you will want to do is remove any extended attributes and codesigning if the sample has a revoked signature. In this case, the sample isn’t signed at all, but if it were we could use:
% sudo codesign --remove-signature <path to bundle or file>
If we need the sample to be codesigned for execution, we can also sign it (remember your VM needs to have installed the Xcode command line tools via xcode-select --install) with:
% sudo codesign -fs - <path to bundle or file> --deep
We’ll remove the extended attributes to bypass Gatekeeper and Notarization checks with
% xattr -rc <path to bundle or file>
And we’ll attempt to attach to the radare2 debugger by adding the -d switch to our initialization command:
% r2 -AA -d patch
Unfortunately, our first attempt doesn’t go well. We already removed the extended attributes and codesigning isn’t the issue here, but the radare2 debugger fails to attach.
Failing to attach the debugger.
That ptrace: Cannot Attach: Invalid argument looks ominous, but actually the error message is misleading. The problem is that we need elevated privileges to debug, so a simple sudo should get us past our current obstacle.
The debugger needs elevated privileges
Yay, attach success! Let’s take a look around before we start diving further into the debugger.
A Faster Way of Finding XREFS and Interesting Code
Let’s run afll as we did when analyzing OSX.Calisto previously, but this time we’ll output the function list to file so that we can sort it and search it more conveniently without having to keep running the command or scrolling up in the Terminal window.
> afll > functions.txt
Looking through our text file, we can see there are a number of function names that could be related to some kind of anti-analysis.
Some of EvilQuest’s suspected anti-analysis functions
We can see that some of these only have a single cross-reference, and if we dig into these using the axt commmand, we see the cross-reference (XREF) for the is_virtual_mchn function happens to be main(), so that looks a good place to start.
Getting help on radare2’s axt command
> axt sym._is_debugging
main 0x10000be5f [CALL] sys._is_virtual_mchn
Many commands in r2 support tab expansion
Here’s a useful powertrick for those already comfortable with r2. You can run any command on a for-each loop using @@. For example, with
axt @@f:<search term>
we can get the XREFS to any function containing the search term in one go.
In this case I tell r2 to give me the XREFS for every function that contains “_is_”. Then I do the same with “get”. Try @@? to see more examples of what you can do with @@.
Using a for-each in radare2
Since we see that is_virtual_mchn is called in main, we should start by disassembling the entire main function to see what’s going on, but first I’m going to change the r2 color theme to something a bit more reader-friendly with the eco command (try eco and hit the tab key to see a list of available themes).
eco focus
pdf @ main
Visual Graph Mode and Renaming Functions with Radare2
As we scroll back up to the beginning of the function, we can see the disassembly provides pretty interesting reading. At the beginning of main, we can see some unnamed functions are called. We’re going to jump into Visual Graph mode and start renaming code as this will give us a good idea of the malware’s execution flow and indicate what we need to do to beat the anti-analysis.
Hit VV to enter Visual Graph mode. I will try to walk you through the commands, but if you get lost at any point, don’t feel bad. It happens to us all and is part of the r2 learning curve! You can just quit out and start again if needs be (part of the beauty of r2’s speed; you can also save your project: type uppercase P? to see project options).
I prefer to view the graph as a horizontal, left-to-right flow; you can toggle between horizontal and vertical by pressing the @ key.
Viewing the sample’s visual graph horizontally
Here’s a quick summary of some useful commands (there are many more as you’ll see if you play around):
hjkl(arrow keys) – move the graph around
-/+0 – reduce, enlarge, return to default size
‘ – toggle graph comments
tab/shift-tab – move to next/previous function
dr – rename function
q – back to visual mode
t/f – follow the true/false execution chain
u – go back
? – help/available options
Hit ‘ once or twice make sure graph comments are on.
Use the tab key to move to the first function after main() (the border will be highlighted), where we can see an unnamed function and a reference in square brackets that begins with the letter ‘o’ (for example, [ob], though it may be different in your sample). Type the letters (without the square brackets) to go to that function. Type p to rotate between different display modes till you see something similar to the next image.
As we can see, this function call is actually a call to the standard C library function strcmp(), so let’s rename it.
Type dr and at the prompt type in the name you want to use and hit ‘enter’. Unsurprisingly, I’m going to call it strcmp.
To return to the main graph, type u and you should see that all references to that previously unnamed function now show strcmp, making things much clearer.
If you scroll through the graph (hjkl, remember) you will see many other unnamed functions that, once you explore them in the same way, are just relocations of standard C library calls such as exit, time, sleep, printf, malloc, srandom and more. I suggest you repeat the above exercise and rename as many as you can. This will both make the malware’s behaviour easier to understand and build up some valuable muscle-memory for working in r2!
Beating Anti-Analysis Without Patching
There are two approaches you can take to interrupt a program’s designed logic. One is to identify functions you want to avoid and patch the binary statically. This is fairly easy to do in r2 and there’s quite a few tutorials on how to patch binaries already out there. We’re not going to look at patching today because our entire objective is to run the sample dynamically, so we might as well interact with the program dynamically as well. Patching is really only worth considering if you need to create a sample for repeated use that avoids some kind of unwanted behaviour.
We basically have two easy options in terms of affecting control flow dynamically. We can either execute the function but manipulate the returned value (like put 0 in rax instead of 1) or skip execution of the function altogether.
We’ll see just how easy it is to do each of these, but we should first think about the different consequences of each choice based on the malware we’re dealing with.
If we NOP a function or skip over it, we’re going to lose any behaviour or memory states invoked by that function. If the function doesn’t do anything that affects the state of our program later on, this can be a good choice.
By the same token, if we execute the function but manipulate the value it returns, we may be allowing execution of code buried in that function that might trip us up. For example, if our function contains jumps to subroutines that do further anti-analysis tests, then we might get blocked before the parent function even returns, so this strategy wouldn’t help us. Clearly then, we need to take a look around the code to figure out which is the best strategy in each particular case.
Let’s take a look inside the _is_virtual_mchn function to see what it would do and work out our strategy.
If you’re still in Visual Graph mode, hit q to get back to the r2 prompt. Regardless of where you are, you can disassemble a function with pdf and the @ symbol and provide a flag or address. Remember, you can also use tab expansion to get a list of possible symbols.
It seems this function subtracts the sleep interval from the second timestamp, then compares it against the first timestamp. Jumping back out to how this result is consumed in main, it seems that if the result is not ‘0’, the malware calls exit() with ‘-1’.
The is_virtual_mchn function causes the malware to exit unless it returns ‘0’
The function appears to be somewhat misnamed as we don’t see the kind of tests that we would normally expect for VM detection. In fact, it looks like an attempt to evade automated sandboxes that patch the sleep function, and we’re not likely to fall foul of it just by executing in our VM. However, we can also see that the next function, user_info, also exits if it doesn’t return the expected value, so let’s practice both the techniques discussed above so that we can learn how to use the debugger whichever one we need to use.
Manipulating Execution with the radare2 Debugger
If you are at the command prompt, type Vp to go into radare2 visual mode (yup, this is another mode, and not the last!).
The Visual Debugger in radare2
Ooh, this is nice! We get registers at the top, and source code underneath. The current line where we’re stopped in the debugger is highlighted. If you don’t see that, hit uppercase S once (i.e., shift-s), which steps over one source line, and – in case you lose your way – also brings you back to the debugger view.
Let’s step smartly through the source with repeated uppercase S commands (by the way, in visual mode, lowercase ‘s’ steps in, whereas uppercase ‘S’ steps over). After a dozen or so rapid step overs, you should find yourself inside this familiar code, which is main().
main() in Visual Debugger mode
Note the highlighted dword, which is holding the value of argc. It should be ‘2’, but we can see from the register above that rdi is only 1. The code will jump over the next function call, which if you hit the ‘1’ key on the keyboard you can inspect (hit u to come back) and see this is a string comparison. Let’s continue stepping over and let the jump happen, as it doesn’t appear to block us. We’ll stop just short of the is_virtual_mchn function.
Seek and break locations are two different things!
We know from our earlier discussion what’s going to happen here, so let’s see how to take each of our options.
The first thing to note is that although the highlighted address is where the debugger is, that’s not where you are if you enter an r2 command prompt, unless it’s a debugger command. To see what I mean, hit the colon key to enter the command line.
From there, print out one line of disassembly with this command:
> pd 1
Note that the line printed out is r2’s current seek position, shown at the top of the visual view. This is good. It means you can move around the program, seek to other functions and run other r2 commands without disturbing the debugger.
On the other hand, if you execute a debugger command on the command line it will operate on the source code where the debugger is currently parked, not on the current seek at the top of your view (unless they happen to be the same).
OK, let’s entirely skip execution of the _is_virtual_mchn function by entering the command line with : and then:
> dss 2
Hit ‘return’ twice. As you can see, the dss command skips the number of source lines specified by the integer you gave it, making it a very easy way to bypass unwanted code execution!
Alternatively, if we want to execute the function then manipulate the register, stop the debugger on the line where the register is compared, and enter the command line again. This time, we can use dr to both inspect and write values to our chosen register.
> dr eax // see eax’s current value
> dr eax = 0 // set eax to 0
> drr // view all the registers
> dro // see the previous values of the registers
Viewing and changing register values
And that, pretty much, is all you need to defeat anti-analysis code in terms of manipulating execution. Of course, the fun part is finding the code you need to manipulate, which is why we spent some time learning how to move around in radare2 in both visual graph mode and visual mode. Remember that in either mode you can get back to the regular command prompt by hitting q. As a bonus, you might play around with hitting p and tab when in the visual modes.
At this point, what I suggest you do is go back to the list of functions we identified at the beginning of the post and see what they do, and whether it’s best to skip them or modify their return values (or whether either option will do). You might want to look up the built-in help for listing and setting breakpoints (from a command prompt, try db?) to move quickly through the code. By the time you’ve done this a few times, you’ll be feeling pretty comfortable about tackling other samples in radare2’s debugger.
Conclusion
If you’re starting to see the potential power of r2, I strongly suggest you read the free online radare2 book, which will be well worth investing the time in. By now you should be starting to get the feel of r2 and exploring more on your own with the help of the ? and other resources. As we go into further challenges, we’ll be spending less time going over the r2 basics and digging more into the actual malware code.
In the next part of our series, we’re going to start looking at one of the major challenges in reversing macOS malware that you are bound to face on a regular basis: dealing with encrypted and obfuscated strings. I hope you’ll join us there and practice your r2 skills in the meantime!
SentinelLabs has been tracking the activity of Agrius, a suspected Iranian threat actor operating in the Middle East, throughout 2020 and 2021 following a set of destructive attacks starting December 2020. Since we last reported on this threat actor in May 2020, Agrius lowered its profile and was not observed conducting destructive activity. This changed recently as the threat actor likely initiated a ransomware attack on the Israeli university Bar-Ilan utilizing the group’s custom Apostle ransomware.
Although the full technical details of the incident were not disclosed publicly, some information was released to the public, most notably the ransom demand text file dropped on victim machines. The .txt file matches that from a new version of Apostle compiled on August 15, 2021, the day of the attack.
The new version of Apostle is obfuscated, encrypted and compressed as a resource in a loader we call Jennlog, as it attempts to masquerade payload in resources as log files. Before executing the Apostle payload, Jennlog runs a set of tests to verify that it is not being executed in an analysis environment based on an embedded configuration. Following the analysis of the Jennlog loader, SentinelLabs retrieved an additional variant of Jennlog, used to load and run OrcusRAT.
Jennlog Analysis
Jennlog (5e5e526a69490399494dcd7195bb6c67) is a .NET loader that deobfuscates, decompresses and decrypts a .NET executable from a resource embedded within the file. The resources within the loader appear to look like log files, and it contains both the binary to run as well as a configuration for the malware’s execution.
Jennlog attempts to extract two different resources:
helloworld.pr.txt – stores Apostle payload and the configuration.
helloworld.Certificate.txt – contains None. If configured to do so, the malware compares the MD5 value of the system information (used as system fingerprint) to the contents of this resource.
The payload hidden in “helloworld.pr.txt” appears to look like a log file at first sight:
Contents of “helloworld.pr.txt” resource embedded within Jennlog
The payload is extracted from the resource by searching for a separator word – “Jennifer”. Splitting the contents of the resource results in an array of three strings:
Decoy string – Most likely there to make the log file look more authentic.
Configuration string – Used to determine the configuration of the malware execution.
Payload – An obfuscated, compressed and encrypted file.
Configuration
The configuration of Jennlog consists of 13 values, 12 of which are actually used in this version of the malware. In the variants we were able to retrieve, all of these flags are set to 0.
Jennlog configuration values
One of the most interesting flags found here is the certificate flag. If this flag is set, it will cause the malware to run only on a specific system. If this system does not match the configured MD5 fingerprint, the malware either stops operation or deletes itself utilizing the function ExecuteInstalledNodeAndDelete(), which creates and runs a BAT file as observed in other Agrius malware.
Jennlog ExecuteInstalledNodeAndDelete() function
Following all the configuration based-checks, Jennlog continues to unpack the main binary from within the resource “helloworld.pr.txt” by performing the following string manipulations in the function EditString() on the obfuscated payload:
Replace all “\nLog” with “A”.
Reverse the string.
Remove all whitespaces.
This manipulation will result in a long base64-encoded deflated content, which is inflated using the function stringCompressor.Unzip(). The inflated content highly resembles the contents of the original obfuscated payload, and it is deobfuscated again using the EditString() function.
The deobfuscation of the inflated content is carried out in a rather peculiar way, being run as a “catch” statement after attempting to turn a string containing a URL to int, which will always result in an error. The domain presented in the URL was never bought, and highly resembles other Agrius malware unpurchased domains, often used as “Super Relays”. Here, however, the domain is not actually contacted.
Execution of EditString() function as a catch statement
Following a second run of the EditString() function, Jennlog decodes the extracted content and decrypts it using an implementation of RC4 with a predefined key. The extracted content found in this sample is a new version of the Apostle ransomware, which is loaded into memory and ran using the parameters given to Jennlog at execution.
Apostle Ransomware Analysis
The new variant of Apostle (cbdbda089f7c7840d4daed22c34969fd876315b6) embedded within the Jennlog loader was compiled on August 15, 2021, the day the attack on Bar-Ilan university was carried out. Its execution flow is highly similar to the variant described in previous reports, and it even checks for the same Mutex as the previous ransomware variant.
The message embedded within it, however, is quite different:
Ooops, Your files are encrypted!!! Don't worry,You can return all your files!
If you want to restore theme, Send $10000 worth of Monero to following address :
43JuFUyzfcKQwTzCTHpQoA8uLGtbwFBLyeeXoYEEU5dZLhLT1cZJDk4cytjcgQT7kdjSerJqpEp2gUcH91bjLcoq2bqik3j
Then follow this Telegram ID : hxxps://t[.]me/x4ran
This is the exact same message that was released to the media in the context of the Bar-Ilan ransomware incident, as reported on ynet:
Ransom demand text file as seen in Bar-Ilan university
Other than the ransom demand note, the wallpaper picture used on affected machines was also changed, this time presenting an image of a clown:
New Apostle variant wallpaper image
OrcusRAT Jennlog Loader
An additional variant of Jennlog (43b810f918e357669be42030a1feb727) was uploaded to VirusTotal on July 14, 2021 from Iran. This variant is highly similar to the one used to load Apostle, and contains a similar configuration scheme (all set to 0). It is used to load a variant of OrcusRAT, which is extracted from the files resources in a similar manner.
The OrcusRAT variant (add7b6b60e746c36a66f5ec233873372) extracted from within it was submitted to VT on June 20, 2021 using the same submitter ID from Iran. It seems to connect to an internal IP address – 192.168.178.114, indicating it might have been used for testing. It also contained the following PDB path:
Agrius has shown a willingness to strategically wipe systems and has continued to evolve its toolkit to enable ransomware operations. At this time, we don’t know if the actor is committed to financially-motivated operations, but we do know the original intent was sabotage. We expect the sort of subterfuge seen here to be deployed in future Agrius operations. SentinelLabs continues to track the development of this nascent threat actor.
If you’ve been following this series so far, you’ll have a good idea how to use radare2 to quickly triage a Mach-O binary statically and how to move through it dynamically to beat anti-analysis attempts. But sometimes, no matter how much time you spend looking at disassembly or debugging, you’ll hit a roadblock trying to figure out your macOS malware sample’s most interesting behavior because much of the human-readable ‘strings’ have been rendered unintelligible by encryption and/or obfuscation.
That’s the bad news; the good news is that while encryption is most definitely hard, decryption is, at least in principle, somewhat easier. Whatever methods are used, at some point during execution the malware itself has to decrypt its code. This means that, although there are many different methods of encryption, most practical implementations are amenable to reverse engineering given the right conditions.
Sometimes, we can do our decryption statically, perhaps emulating the malware’s decryption method(s) by writing our own decryption logic(s). Other times, we may have to run the malware and extract the strings as they are decrypted in memory. We’ll take a practical look at using both of these techniques in today’s post through a series of short case studies of real macOS malware.
First, we’ll look at an example of AES 128 symmetric encryption used in the recent macOS.ZuRu malware and show you how to quickly decode it; then we’ll decrypt a Vigenère cipher used in the WizardUpdate/Silver Toucan malware; finally, we’ll see how to decode strings dynamically, in-memory while executing a sample of a notorious adware installer.
Although we cannot cover all the myriad possible encryption schemes or methods you might encounter in the wild, these case studies should give you a solid basis from which to tackle other encryption challenges. We’ll also point you to some further resources showcasing other macOS malware decryption strategies to help you expand your knowledge.
For our case studies, you can grab a copy of the malware samples we’ll be using from the following links:
Don’t forget to use an isolated VM for all this work: these are live malware samples and you do not want to infect your personal or work device!
Breaking AES Encryption in macOS.ZuRu
Let’s begin with a recent strain of new macOS malware dubbed ‘macOS.ZuRu’. This malware was distributed inside trojanized applications such as iTerm, MS Remote Desktop and others in September 2021. Inside the malware’s application bundle is a Frameworks folder containing the malicious libcrypto.2.dylib. The sample we’re going to look at has the following hash signatures:
Let’s load it into r2 in the usual way (if you haven’t read the earlier posts in this series, catch up here and here), and consider the simple sequence of reversing steps illustrated in the following images.
Getting started with our macOS.ZuRu sample
As shown in the image above, after loading the binary, we use ii to look at the imports, and see among them CCCrypt (note that I piped this to head for display purposes). We then do a case insensitive search on ‘crypt’ in the functions list with afll~+crypt.
If we add [0] to the end of that, it gives us just the first column of addresses. We can then do a for-each over those using backticks to pipe them into axt to grab the XREFS. The entire command is:
> axt @@=`afll~crypt[0]`
The result, as you can see in the lower section of the image above, shows us that the malware uses CCCrypt to call the AESDecrypt128 block cipher algorithm.
AES128 requires a 128-bit key, which is the equivalent of 16 bytes. Though there’s a number of ways that such a key could be encoded in malware, the first thing we should do is a simple check for any 16 byte strings in the binary.
To do that quickly, let’s pipe the binary’s strings through awk and filter on the len column for ‘16’: That’s the fourth column in r2’s iz output. We’ll also narrow down the output to just cstrings by grepping on ‘string’, so our command is:
> iz | awk ‘$4==16’ | grep string
We can see the output in the middle section of the following image.
Filtering the malware’s strings for possible AES 128 keys
We got lucky! There’s two occurrences of what is obviously not a plain text string. Of course, it could be anything, but if we check out the XREFS we can see that this string is provided as an argument to the AESDecrypt method, as illustrated in the lower section of the above image.
All that remains now is to find the strings that are being deciphered. If we get the function summary of AESDecrypt from the address shown in our last command, 0x348b, it reveals that the function is using base64 encoded strings.
> pds @ 0x348b
Grabbing a function summary in r2 with the pds command
A quick and dirty way to look for base64 encoded strings is to grep on the “=” sign. We’ll use r2’s own grep function, ~ and pipe the result of that through another filter for “str” to further refine the output.
> iz~=~str
A quick-and-dirty grep for possible base64 cipher strings
Our search returns three hits that look like good candidates, but the proof is in the pudding! What we have at this point is candidates for:
the encryption algorithm – AES128
the key – “quwi38ie87duy78u”
three ciphers – “oPp2nG8br7oIB+5wLoA6Bg==, …”
All we need to do now is to run our suspects through the appropriate decryption routine for that algorithm. There are online tools such as Cyber Chef that can do that for you, or you can find code for most popular algorithms for your favorite language from an online search. Here, we implemented our own rough-and-ready AES128 decryption algorithm in Go to test out our candidates:
A simple AES128 ECB decryption algorithm implemented in Go
We can pipe all the candidate ciphers to file from within r2 and then use a shell one-liner in a separate Terminal window to run each line through our Go decryption script with the candidate key.
Revealing the strings in clear text with our Go decrypter
And voila! With a few short commands in r2 and a bash one-liner, we’ve decrypted the strings in macOS.ZuRu and found a valuable IoC for detection and further investigation.
Decoding a Vigenère Cipher in WizardUpdate Malware
In our second case study, we’re going to take a look at the string encryption used in a recent sample of WizardUpdate malware. The sample we’ll look at has the following hash signatures:
We’ll follow the same procedure as last time, beginning with a case insensitive search of functions with “crypt” in the name, filtering the results of that down to addresses, and getting the XREFS for each of the addresses. This is what it looks like on our new sample:
Finding our way to the string encryption code from the function analysis
We can see that there are several calls from main to a decrypt function, and that function itself calls sym.decrypt_vigenere.
Vigenère is a well-known cipher algorithm which we will say a bit more about shortly, but for now, let’s see if we can find any strings that might be either keys or ciphers.
Since a lot of the action is happening in main, let’s do a quick pds summary on the main function.
Using pds to get a quick summary of a function
There are at least two strings of interest. Let’s take a better look by leveraging r2’s afns command, which lists all strings associated with the current function.
r2’s afns can help you isolate strings in a function
That gives us a few more interesting looking candidates. Given its length and form, my suspicion at this point is that the “LBZEWWERBC” string is likely the key.
We can isolate just the strings we want by successive filtering. First, we get just the rows we want:
> afns~:1..5
And then grab just the last column (ignoring the addresses):
> afns~:1..5[2]
Then using sed to remove the “str.” prefix and grep to remove the “{MAID}” string, we end up with:
Access to the shell in r2 makes it easy to isolate the strings of interest
As before, we can now pipe these out to a “ciphers” file.
Let’s next turn to the encryption algorithm. Vigenère has a fascinating history. Once thought to be unbreakable, it’s now considered highly insecure for cryptography. In fact, if you like puzzles, you can decrypt a Vigenère cipher with a manual table.
The Vigenère cipher was invented before computers and can be solved by hand
One of the Vigenère cipher’s weaknesses is that it’s possible to discern patterns in the ciphertext that can reveal the length of the key. That problem can be avoided by encrypting a base64 encoding of the plain text rather than the plain text itself.
Now, if we jump back into radare2, we’ll see that WizardUpdate does indeed decode the output of the Vigenère function with a base64 decoder.
WizardUpdate malware uses base64 encoding either side of encrypting/decrypting
There is one other thing we need to decipher a Vigenère cipher aside from the key and ciphertext. We also need the alphabet used in the table. Let’s use another r2 feature to see if it can help us find it. Radare2’s search function, /, has some crypto search functionality built in. Use /c? to view the help on this command.
Search for crypto materials with built-in r2 commands
The /ck search gives us a hit which looks like it could function as the Vigenère alphabet.
OK, it’s time to build our decoder. This time, I’m going to adapt a Python script from here, and then feed it our ciphers file just as before. The only differences are I’m going to hardcode the alphabet in the script and then run the output through base64. Let’s see how it looks.
Decoding the strings returns base64 as expected
So far so good. Let’s try running those through base64 -D (decode) and see if we get our plain text.
Our decoder returns gibberish after we try to decode the base64
Hmm. The script runs without error, but the final decoded base64 output is gibberish. That suggests that while our key and ciphers are correct, our alphabet might not be.
Returning to r2, let’s search more widely across the strings with iz~string
Finding cstrings in the TEXT section with r2’s ~ filter
The first hit actually looks similar to the one we tried, but with fewer characters and a different order, which will also affect the result in a Vigenère table. Let’s try again using this as the hardcoded alphabet.
Decoding the WizardUpdate’s encrypted strings back to plain text
Success! The first cipher turns out to be an encoding of the system_profiler command that returns the device’s serial number, while the second contains the attacker’s payload URL. The third downloads the payload and executes it on the victim’s device.
Reading Encrypted Strings In-Memory
Reverse engineering is a multi-faceted puzzle, and often the pieces drop into place in no particular order. When our triage of a malware sample suggests a known or readily identifiable encryption scheme has been used as we saw with macOS.ZuRu and WizardUpdate, decrypting those strings statically can be the first domino that makes the other pieces fall into place.
However, when faced with an incalcitrant sample on which the authors have clearly spent a great deal of time second-guessing possible reversing moves, a ‘cheaper’ option is to detonate the malware and observe the strings as they are decrypted in memory. Of course, to do that, you might need to defeat some anti-analysis and anti-debugging tricks first!
In our third case study, then, we’re going to take a look at a common adware installer. Adware is big business, employs lots of professional coders, and produces code that is every bit as crafty as any sophisticated malware you’re likely to come across. If you spend anytime dealing with infected Macs, coming across adware is inevitable, so knowing how to deal with it is essential.
Let’s dump this into r2 and see what a quick triage can tell us.
This sample is keeping its secrets
Well, not much! If we print the disassembly for the main function with pdf @main, we see a mass of obfuscated code.
Lots of obfuscated code in this adware installer
However, the only calls here are to system and remove, as we saw from the function list. Let’s quit and reopen in r2’s debugger mode (remember: you may need to chmod the sample and remove any code signature and extended attributes as explained here).
Let’s find the entrypoint with the ie command. We’ll set a breakpoint on that and then execute to that point.
Breaking on the entrypoint
Now that we’re at main, let’s break on the system call and take a look at the registers. To do that, first get the address of the system flag with
> f~system
Then set the breakpoint on the address returned with the db command. We can continue execution with dc.
Setting a breakpoint on the system call and continuing execution
Note that in the image above, our first attempt to continue execution results in a warning message and we actually hit our main breakpoint again. If this happens, repeating the dc command should get you past the warning. Now we can look at all the registers with drr.
Revealing the encoded strings in memory
At the rdi register, we can see the beginning of the decrypted string. Let’s see the rest of it.
The clear text is revealed in the rdi register
Ah, an encoded shell script, typical of Bundlore and Shlayer malware. One of my favorite things about r2 is how you can do a lot of otherwise complex things very easily thanks to the shell integration. Want to pretty-print that script? Just pipe the same command through sed from right within r2.
> ps 2048 @rdi | sed ‘s/;/\n/g’
We can easily format the output by piping it through the sed utility
More Examples of macOS String Decryption Techniques
WizardUpdate and macOS.ZuRu provided us with some real-world malware samples where we could use the same general technique: identify the encryption algorithm in the functions table, search for and isolate the key and ciphers in the strings, and then find or implement an appropriate decoding algorithm.
Some malware authors, however, will implement custom encryption and decryption schemes and you’ll have to look more closely at the code to see how the decryption routine works. Alternatively, where necessary, we can detonate the code, jump over any anti-analysis techniques and read the decrypted strings directly from memory.
If all this has piqued your interest in string encryption techniques used in macOS malware, then you might like to check out some or all of the following for further study.
EvilQuest, which we looked at in the previous post, is one example of malware that uses a custom encryption and decryption algorithm. SentinelLabs broke the encryption statically, and then created a tool based on the malware’s own decryption algorithm to decrypt any files locked by the malware. Fellow macOS researcher Scott Knight also published his Python decryption routine for EvilQuest, which is worth close study.
Adload is another malware that uses a custom encryption scheme, and for which researchers at Confiant also published decryption code.
Notorious adware dropper platforms Bundlore and Shlayer use a complex and varying set of shell obfuscation techniques which are simple enough to decode but interesting in their own right.
Likewise, XCodeSpy uses a simple but quite effective shell obfuscation trick to hide its strings from simple search tools and regex pattern matches.
Conclusion
In this post, we’ve looked at a variety of different encryption techniques used by macOS malware and how we can tackle these challenges both statically and dynamically. If you haven’t checked out the previous posts in this series, have a look Part 1 and Part 2. I hope you’ll join us for the next post in this series as we continue to look at common challenges facing macOS malware researchers.
Karma is a relatively new ransomware threat actor, having first been observed in June of 2021. The group has targeted numerous organizations across different industries. Reports of a group with the same name from 2016 are not related to the actors currently using the name. An initial technical analysis of a single sample related to Karma was published by researchers from Cyble in August.
In this post, we take a deeper dive, focusing on the evolution of Karma through multiple versions of the malware appearing through June 2021. In addition, we explore the links between Karma and other well known malware families such as NEMTY and JSWorm and offer an expanded list of technical indicators for threat hunters and defenders.
Initial Sample Analysis
Karma’s development has been fairly rapid and regular with updated variants and improvements, oftentimes building multiple versions on the same day. The first few Karma samples our team observed were:
Sample 1 was compiled on 18th, June 2021 and Samples 2 and 3 the following day on the 19th, a few minutes apart. Basic configuration between these samples is similar, though there are some slight differences such as PDB paths.
After Sample 1, we see more of the core features appear, including the writing of the ransom note. Upon execution, these payloads would enumerate all local drives (A to Z) , and encrypt files where possible.
Further hunting revealed a number of other related samples all compiled within a few days of each other. The following table illustrates compilation timestamps and payload size across versions of Karma compiled in a single week. Note how the payload size decreases as the authors’ iterate.
Ransom Note is not Created in Sample 1.
Also, the list of excluded extensions is somewhat larger in Sample 1 than in both Samples 2 and 3, and the list of extensions is further reduced from Sample 5 onwards to only exclude “.exe”, “.ini”, “.dll”, “.url” and “.lnk”.
The list of excluded extensions is reduced as the malware authors iterate
Encryption Details
From Sample 2 onwards, the malware calls CreateIoCompletionPort, which is used for communication between the main thread and a sub thread(s) handling the encryption process. This specific call is key in managing efficiency of the encryption process (parallelization in this case).
Individual files are encrypted by way of a random Chacha20 key. Once files are encrypted, the malware will encrypt the random Chacha20 key with the public ECC key and embed it in the encrypted file.
Chacha Encryption
Across Samples 2 to 5, the author removed the CreateIoCompletionPort call, instead opting to create a new thread to manage enumeration and encryption per drive. We also note the “KARMA” mutex created to prevent the malware from running more than once. Ransom note names have also been updated to “KARMA-ENCRYPTED.txt”.
Diving in deeper, some samples show that the ChaCha20 algorithm has been swapped out for Salsa20. The asymmetric algorithm (for ECC) has been swapped from Secp256k1 to Sect233r1. Some updates around execution began to appear during this time as well, such as support for command line parameters.
A few changes were noted in Samples 6 and 7. The main difference is the newly included background image. The file “background.jpg” is written to %TEMP% and set as the Desktop image/wallpaper for the logged in user.
Desktop image change and message
Malware Similarity Analysis
From our analysis, we see similarities between JSWorm and the associated permutations of that ransomware family such as NEMTY, Nefilim, and GangBang. Specifically, the Karma code analyzed bears close similarity to the GangBang or Milihpen variants that appeared around January 2021.
Some high-level similarities are visible in the configurations.
We can see deeper relationships when we conduct a bindiff on Karma and GangBang samples. The following image shows how similar the main() functions are:
The main() function & argument processing in Gangbang (left) and Karma
Victim Communication
The main body of the ransom note text hasn’t changed since the first sample and still contains mistakes. The ransom notes are base64-encoded in the binary and dropped on the victim machine with the filename “KARMA-AGREE.txt” or, in later samples, “KARMA-ENCRYPTED.txt”.
Your network has been breached by Karma ransomware group.
We have extracted valuable or sensitive data from your network and encrypted the data on your systems.
Decryption is only possible with a private key that only we posses.
Our group's only aim is to financially benefit from our brief acquaintance,this is a guarantee that we will do what we promise.
Scamming is just bad for business in this line of work.
Contact us to negotiate the terms of reversing the damage we have done and deleting the data we have downloaded.
We advise you not to use any data recovery tools without leaving copies of the initial encrypted file.
You are risking irreversibly damaging the file by doing this.
If we are not contacted or if we do not reach an agreement we will leak your data to journalists and publish it on our website.
http://3nvzqyo6l4wkrzumzu5aod7zbosq4ipgf7ifgj3hsvbcr5vcasordvqd.onion/
If a ransom is payed we will provide the decryption key and proof that we deleted you data.
When you contact us we will provide you proof that we can decrypt your files and that we have downloaded your data.
How to contact us:
{[email protected]}
{[email protected]}
{[email protected]}
Each sample observed offers three contact emails, one for each of the mail providers onionmail, tutanota, and protonmail. In each sample, the contact emails are unique, suggesting they are specific communication channels per victim. The notes contain no other unique ID or victim identifier as sometimes seen in notes used by other ransomware groups.
In common with other operators, however, the Karma ransom demand threatens to leak victim data if the victim does not pay. The address of a common leaks site where the data will be published is also given in the note. This website page appears to have been authored in May 2021 using WordPress.
The Karma Ransomware Group’s Onion Page
Conclusion
Karma is a young and hungry ransomware operation. They are aggressive in their targeting, and show no reluctance in following through with their threats. The apparent similarities to the JSWorm family are also highly notable as it could be an indicator of the group being more than they appear. The rapid iteration over recent months suggests the actor is investing in development and aims to be around for the foreseeable future. SentinelLabs continues to follow and analyze the development of Karma ransomware.
MITRE ATT&CK T1485 Data Destruction T1486 Data Encrypted for Impact T1012 Query Registry T1082 System Information Discovery T1120 Peripheral Device Discovery T1204 User Execution T1204.002 User Execution: Malicious File
The increasing popularity of Go as a language for malware development is forcing more reverse engineers to come to terms with the perceived difficulties of analyzing these gargantuan binaries. The language offers great benefits for malware developers: portability of statically-linked dependencies, speed of simple concurrency, and ease of cross-compilation. On the other hand, for analysts, it’s meant learning the inadequacies of our tooling and contending with a foreign programming paradigm. While our tooling has generally improved, the perception that Go binaries are difficult to analyze remains. In an attempt to further dispel that myth, we’ve set out to share a series of scripts that simplify the task of analyzing Go binaries using IDA Pro with a friendly methodology. Our hope is that members of the community will feel inspired to share additional resources to bolster our collective analysis powers.
A Quick Intro to the Woes of Go Binary Analysis
Go binaries present multiple peculiarities that make our lives a little harder. The most obvious is their size. Due to the approach of statically-linking dependencies, the simplest Go binary is multiple megabytes in size and one with proper functionality can figure in the 15-20mb range. The binaries are then easily stripped of debug symbols and can be UPX packed to mask their size quite effectively. That bulky size entails a maze of standard code to confuse reverse engineers down long unproductive rabbit holes, steering them away from the sparse user-generated code that implements the actual functionality.
To make things worse, Go doesn’t null-terminate strings. The linker places strings in incremental order of length and functions load these strings with a reference to a fixed length. That’s a much safer implementation but it means that even a cursory glance at a Go binary means dealing with giant blobs of unrelated strings.
“Hello World!” string lost in a sea of unrelated strings.
Even the better disassemblers and decompilers tend to display these unparsed string blobs confusing their intended purpose.
Autoanalysis output
Manually fixed disassembly
That’s without getting into the complexities of recovering structures, accurately portraying function types, interfaces, and channels, or properly tracing argument references.
The issue of arguments should prove disturbing for our dynamic analyst friends who were hoping that a debugger would spare them the trouble of manual analysis. While a debugger certainly helps determine arguments at runtime, it’s important to understand how indirect that runtime is. Go is peculiar in allocating a runtime stack owned by the caller function that will in turn handle arguments and allow for multiple return values. For us it translates into a mess of runtime function prologues before any meaningful functionality. Good luck navigating that without symbols.
Improving the Go Reversing Experience
With all those inadvertent obstacles in mind, it’s perfectly understandable that reversers dreaded analyzing Go binaries. However, the situation has changed in the past few years and we should reassess our collective abhorrence of Go malware. Different disassemblers and decompilers have stepped up their Go support. BinaryNinja and Cerbero have improved their native support for Go and there are now standalone frameworks like GoRE that offer good functionality depending on the Go compiler version and can even help support other projects like radare2.
We’ll be focusing on IDA Pro. With the advent of version 7.6, IDA’s native support of Go binaries is drastically better and we’ll be building features on top of this. For folks stuck on v7.5 or lower, we’ll also provide some help by way of scripts but the full functionality of AlphaGolang is unlocked with 7.6 due to some missing APIs (specifically the ida_dirtree API for the new folder tree view). This isn’t the first time brave souls in the community have attempted to improve the Go reversing experience for IDA. However, those projects were largely monolithic labors of love by their original authors and given the fickle nature of IDA’s APIs, once those authors got busy, the scripts fell into disrepair. We hope to improve that as well.
AlphaGolang
With AlphaGolang, we wanted to tackle two disparate problems simultaneously– the brittleness of IDApython scripts for Go reversing and the need for clear steps to analyze these binaries.
We can’t expect everyone to be an expert in Go in order to understand their way around a binary. Less so to have to fix the tooling they’re attempting to rely on. While engineers might be tempted to address the former by elaborating a complex framework, we figured we’d swing in the opposite direction and break up the requisite functionality into smaller scripts. Those smaller digestible scripts allow us to part out a relatable methodology in steps so that analysts can pick and choose what they need as they advance in their reversing journey.
Additionally, we hope the simplicity of the project and a forthrightness about its current limitations will inspire others to contribute new steps, fixes, and additional functionality.
At this time, the first five steps are as follows–
Step 0: Identifying Go Binaries
Go Build ID Regex
By popular request, we are including a simple YARA rule to help identify Go binaries. This is a quick and scrappy solution that checks for PE, ELF, and Mach-O file headers along with a regex for a single Go Build ID string.
Step 1: Recreate pcln Table
Recreating the Go pcln table
Dealing with stripped Golang binaries is particularly awful. Reversers unfamiliar with Go are disheartened to see that despite having all of the original function names within the binary, their disassembler is unable to connect those symbols as labels for their functions. While that might seem like a grand coup for malware developers attempting to confuse and frustrate analysts, it isn’t more than a temporary inconvenience. Despite being stripped, we have enough information available to reconstruct the Go pcln table and provide the disassembler with the information it needs.
Two noteworthy points before continuing:
The pcln table is documented as early as Go v1.12 and has been modified as of v1.16.
IDA Pro v7.6+ handles Go binaries very well and is unlikely to need this step. This script will prove particularly valuable for folks using IDA v7.5 and under when dealing with stripped Go binaries.
Depending on the file’s endianness, the script will locate the pcln table’s magic header, walk the table converting the data to DWORDs (or QWORDs depending on the bitness), and create a new segment with the appropriate ‘.gopclntab’ header effectively undoing the stripping process.
Step 2: Discover Missing Functions and Restore Function Names
Function discovery and renaming
Immediately after recreating the pcln table (or in unfortunate cases where automatic disassembly fails), function names are not automatically assigned and many functions may have eluded discovery. We are going to fix both of these issues in one simple go.
We know that the pcln table is pointing us at every function in the binary, even if IDA hasn’t recognized them. This script will walk the pcln table, check the offsets therein for an existing function, and instruct IDA to define a function wherever one is missing. The resulting number of functions can be drastically greater even in cases where disassembly seems perfect.
Additionally, we’ll borrow Tim Strazzere’s magic from the original Golang Loader Assist in order to label all of our functions. We ported the plugin to Python 3, made it compatible with the new IDA APIs, and refactored it. The functionality is now part of two separate steps (2 and 4) and easier to maintain. In this step, Strazzere’s magic will help us associate all of our functions with their original names in an IDA friendly format.
Step 3: Surface User-Generated Functions
Automatically categorizing functions
Having recovered and labeled all of our functions, we are now faced with the daunting proposition of sifting through thousands of functions. Most of these functions are part of Go’s standard packages or perhaps functionality imported from GitHub repositories. To belabor a metaphor, we now have street signs but no map. How do we fix this?
IDA v7.5 introduced the concept of Folder Views, an easily missed Godsend for the anal-retentive reverser. This feature has to be explicitly turned on via a right-click on the desired subview –whether functions, structures, imports, etc. IDA v7.6 takes this a step further by introducing a thus-far undocumented API to interact with these folder views (A heartfelt thank you to Milan Bohacek for his help in effectively wielding this API). That should enable us to automate some extraordinary time-saving functionality.
While our malware may have 5,000 functions, the majority of those functions were not written by the malware developers, they’re publicly documented, and we need nothing more than a nominal overview to know what they do.
By being clever about categorizing function packages, we can actually whittle down to a fraction of the overall functions that merit analyst time. Functions will be categorized by their package prefixes and further grouped as ‘Standard Go Packages’, unlabeled (‘sub_’), uncategorized (no package prefix), and Github imports. What remains are the ‘main’ package and any custom packages added by the malware developers. For a notable example, the NOBELIUM GoldMax (a.k.a. SunShuttle) malware can be reduced from a hulking 4,771 functions to a mere 22. This is the simplest and perhaps the most valuable step towards our goal of understanding the malware’s functionality.
Step 4: Fix String References
Accurately recasting strings by reference
Finally, there’s the issue of strings in Go. Unlike all C-derivative languages, strings in Go are not null terminated. Neither are they grouped together based on their source or functionality. Rather, the linker places all of the strings in order of incremental length, with no obvious demarcation as to where one string ends and the next begins. This works because whenever a function references a string, it does so by loading the string address and a hardcoded length. While this is a safer paradigm for handling strings, it makes for an unpleasant reversing experience.
So how do we overcome this hurdle? Here’s where another piece of Strazzere’s Golang Loader Assist can help us. His original plugin would check functions for certain string loading patterns and use these as a guide to fix the string assignments. We have once again (partially) refactored this functionality, made it compatible with Python 3, and IDA’s new APIs. We have also improved some of the logic for the string blobs in place (either by suspicious length or because a reference points to the middle of a blob) and added some sanity checks.
While this step is already a marked improvement, we are seeing new string loading patterns introduced with Go v1.17 that need adding and there’s definitely room for improved refactoring. We hope some of you will feel inclined to contribute.
Where Do We Go From Here?
Let’s take a step back and look at where we are after all of these steps. We have an IDB with all functions discovered, labeled, and categorized, and hopefully all of their string references are correctly annotated. This is an ideal we could seldom dream of with malware of a comparable size written in C or C++ without extensive analysis time and prior expertise.
Now that we have a clear view of the user-generated functionality, what more can we do? How else can we improve our hunting and analysis efforts?
The following are a series of ideas we’d recommend implementing in further steps–
How about auto-generating YARA rules for user-generated functions and their referred strings?
Want a better understanding of arguments as they’re passed between functions? How about automagically setting breakpoints at the runtime stack prologues and annotating the arguments back to our IDB?
For reversers that follow the Kwiatkowski school of rewriting the code to understand the program’s functionality, how about selectively exporting the Hex-Rays pseudocode solely for the user-generated functions?
How about reconstructing interfaces and type structs from runtime objects?
Got more ideas? Want to help? Head to our SentinelLabs GitHub repo for all of the scripts and contribute your own shortcuts and superpowers for analyzing Go malware!
We’d like to thank the following for their direct and indirect contributions–
Tim Strazzere for his original Golang Loader Assist script, which we refactored and made compatible with Python3 and the new IDA APIs.
Milan Bohacek (Avast Software s.r.o.) for his invaluable help figuring out the idatree API.
Joakim Kennedy (Intezer)
Ivan Kwiatkowski (Kaspersky GReAT) for making his Go reversing course available.
Igor Kuznetsov (Kaspersky GReAT) for his help understanding newer pcln tab versions.
Spook Ransomware is an emerging player first seen in late September 2021
The operators publish details of all victims regardless of whether they pay or not
Targets range across several industries with an emphasis on manufacturing
Analysis shows a significant degree of code sharing between Spook and the Prometheus and Thanos ransomware families
Overview
Spook ransomware emerged onto the scene in late September 2021 and follows the multi-pronged extortion model that is all too common these days. Victims are hit with the threat of data destruction as well as public data leakage and the associated fallout. In this report, we explore how the malware shares certain similarities with earlier ransomware families, and describe its main encryption and execution behaviour.
Spook and Prometheus
There is some indication that Spook is either linked to, or derived from, Prometheus ransomware. Prometheus is itself an evolution of Thanos ransomware. However, it is important to note that since Thanos ransomware had a builder which was leaked, any real attempts at attribution based solely on the malware’s code is somewhat futile. Even so, there are a few notable similarities between Spook, Prometheus, and ultimately Thanos.
The .NET binary in the following sample, first seen in VirusTotal on 02 October, provides a glimpse into some of these similarities, with artifacts from the Thanos builder also apparent. a63a5de26582af1438c9886cfb15c4baa08cce2e
Shared code block with Thanos
Our analysis suggests that there is an overlap of between 29-50% of shared code between Spook and Prometheus. Some of this overlap is related to construction of the ransom notes and key identifiers.
Ransom note similarity example (Prometheus vs Spook)
In addition to shared code artifacts, there are similarities with regards to the layout and structure of the Spook and Prometheus payment portals.
Below are the similarities between the leak data URLs hosted by both the groups
Spook, mirroring the manifestos of others, boasts “very strong (AES) encryption” along with the threat of leaking victim data to the public. The malware has the ability to encrypt target machines without requiring internet connectivity. Encryption of a full disk can occur within just a few minutes, at which point the ransom note is displayed on the desktop (RESTORE_FILES_INFO.HTA) along with numerous other system notifications.
The malware also makes a number of changes to ensure that the ransom notifications are displayed prominently after reboot (via Start Menu lnk, Reg).
WinLogon is modified (via registry) to display the Ransom Note text upon login:
Ransom notes are also displayed upon login via a Shortcut placed in the Startup directory
Startup Folder Shortcut
In addition, Spook will attempt to terminate processes and stop services of anything that may inhibit the encryption process.
Here again there is overlap between Spook, Prometheus, and Thanos with regards to process discovery and manipulation, especially with regards to checking for and killing the Raccine anti-ransomware process that some organizations deploy in an effort to protect shadow copies.
TASKILL.EXE is used to force the termination of the following processes if found:
The Raccine product is specifically targeted with regards to disabling the products’ UI components and update features. These are carried out via basic OS commands such as reg.exe and schtasks.exe.
In addition, sc.exe is used to disable specific services and components:
sc.exe config Dnscache start= auto
sc.exe config SQLTELEMETRY start= disabled
sc.exe config FDResPub start= auto
sc.exe config SSDPSRV start= auto
sc.exe config SQLTELEMETRY$ECWDB2 start= disabled
sc.exe config SstpSvc start= disabled
sc.exe config upnphost start= auto
sc.exe config SQLWriter start= disabled
With various processes out of the way and the system in an optimal state for encryption, the malware proceeds to enumerate local files and folders, along with accessible network resources.
Given the Thanos pedigree, specifics around encryption can vary. The samples analyzed employ a random string at runtime as the passphrase for file encryption (AES). The string is subsequently encrypted with the attacker’s public key and added into the generated ransom note(s). Recovery of encrypted data is, therefore, not possible without the corresponding private key.
Ransom Payment and Victimology
Upon infection, victims are instructed to proceed to Spook’s TOR-based payment portal.
Spook Ransom Demand
At the payment portal, the victim is able to interact with the attackers via chat to negotiate payment.
Spook Payment Portal
Spook has been leveraging attacks against high-value targets across the globe, with little to no discretion with regards to industry. Looking at the current cross-section of victims posted on the group’s web site, however, the majority are in the manufacturing sector.
The public blog went live in early October 2021. At the time of writing, there are 17 victims posted on the Spook site.
Some of the victims named on the Spook blog site
Spook actually lists all attacked companies, regardless of whether or not they pay the ransom demand. Those victims that pay have their entry updated to indicate that the company’s data is ‘not for sale’. Those that have not paid are listed as having data that is “For Sale”, while some victim entries, presumably the most recent or those that are in the process of negotiating, are listed as “Company Decides”.
Conclusion
As these attacks continue to escalate and become more egregious, the need for true attack prevention is all the more critical. Spook’s tactic of public outing victims even if they pay threatens reputational harm to any compromised company, even if they follow the attackers’ payment demands.
This only continues to illustrate the importance of preventing attacks in the first place. Ransomware operators have moved beyond worrying about companies detecting after-the-fact and attempting to recover encrypted data.
SentinelLabs has discovered a heap overflow vulnerability in the TIPC module of the Linux Kernel.
The vulnerability can be exploited either locally or remotely within a network to gain kernel privileges, allowing an attacker to compromise the entire system.
The TIPC module comes with all major Linux distributions but needs to be loaded in order to enable the protocol.
A patch has been released on the 29th of October and affects kernel versions between 5.10 and 5.15.
At this time, SentinelOne has not identified evidence of in-the-wild abuse.
Introduction and Methodology
As a researcher, it’s important to add new techniques and software to your bug hunting methodology. A year ago, I started using CodeQL for my own research on open source projects and decided to compile the Linux kernel with it and try my luck.
For those who haven’t come across it before, CodeQL is an analysis engine that allows you to run queries on code. From a security perspective, this can allow you to find vulnerabilities purely by describing what they look like. CodeQL will then go off and find all instances of that vulnerability.
I’d had a passing thought about overflows that I wanted to take a quick look at between research projects, namely, looking at locations in which a 16-bit variable was passed to kmalloc. My thinking was that 16-bits would be easier to realistically overflow than a 32-bit or 64-bit number.
The query itself is basic and isn’t aimed at finding actual overflows, just looking for interesting kmalloc calls as a starting point for a larger query:
import cpp
from FunctionCall fc // Select all Function Calls
where fc.getTarget().getName() = "kmalloc" // Where the target function is called kmalloc
and fc.getArgument(0).getType().getSize() = 2 // and the supplied size argument is a 16-bit int
select fc, fc.getLocation() // Select the call location and the string of the location to know what file it’s in
This returned 60 results. After briefly looking over a few, one result stood out above the rest:
What struck me as interesting is that this seems to be a function for parsing received data (1) and doesn’t appear to have any validation on the size (4) (5) obtained from the body of the message (2) until after it’s already copied (6). It also appears that the copied size could be different to the allocated size (3). This looked like a clear-cut kernel heap buffer overflow.
What is the Linux TIPC Protocol?
Transparent Inter-Process Communication (TIPC) is a protocol that allows nodes in a cluster to communicate with each other in a way that can optimally handle a large number of nodes remaining fault tolerant.
In order to keep this section brief, this post will focus on the key components. For a more detailed and high-level description of the actual TIPC protocol, including the various ways messaging is performed and how Service Tracking works, it’s best to refer to the official sourceforge page.
The protocol is implemented in a kernel module packaged with all major Linux distributions. When loaded by a user, it can be used as a socket and can be configured on an interface using netlink (or using the userspace tool tipc, which will perform these netlink calls) as an unprivileged user.
TIPC can be configured to operate on top of a bearer protocol such as Ethernet or UDP (in the latter case, the kernel listens on port 6118 for incoming messages from any machine). Since a low privileged user is unable to create raw ethernet frames, setting the bearer to UDP makes it easier to write a local exploit for.
Although TIPC is used on top of these protocols, it has a separate addressing scheme whereby nodes can choose their own addresses.
The TIPC protocol works in a way transparent to the user. All message construction and parsing is performed in the kernel. Each TIPC message has a common header format and some message-specific headers (hence the variable total size of the header).
The most important parts of the common header for this vulnerability are the ‘Header Size’ –the actual header size shifted to the right by two bits– and the ‘Message Size’ –the entire TIPC message taking into account the header size:
An example of a TIPC message header
These two sizes are validated by the tipc_msg_validate function.
The Message Size is correctly validated as greater than the Header Size, the payload size is validated against the maximum user message size, and the message size is validated against the actual received packet length.
Overview of the TIPC Vulnerability
In September 2020, a new user message type was introduced called MSG_CRYPTO, which allows peers to send cryptographic keys (at the moment, only AES GCM appears to be supported). This is part of the 2021 TIPC roadmap.
The body of the message has the following structure:
struct tipc_aead_key {
char alg_name[TIPC_AEAD_ALG_NAME];
unsigned int keylen; /* in bytes */
char key[];
};
Where TIPC_AEAD_ALG_NAME is a macro for 32. When this message is received, the TIPC kernel module needs to copy this information into storage for that node:
/* Allocate memory for the key */
skey = kmalloc(size, GFP_ATOMIC);
/* ... *//* Copy key from msg data */
skey->keylen = ntohl(*((__be32 *)(data + TIPC_AEAD_ALG_NAME)));
memcpy(skey->alg_name, data, TIPC_AEAD_ALG_NAME);
memcpy(skey->key, data + TIPC_AEAD_ALG_NAME + sizeof(__be32),
skey->keylen);
The size used to allocate is the same as the size of the message payload (calculated from the Header Size being subtracted from the Message Size). The name of the key algorithm is copied and the key itself is then copied as well.
As mentioned above, the Header Size and the Message Size are both validated against the actual packet size. So while these values are guaranteed to be within the range of the actual packet, there are no similar checks for either the keylen member of the MSG_CRYPTO message or the size of the key algorithm name itself (TIPC_AEAD_ALG_NAME) against the message size. This means that an attacker can create a packet with a small body size to allocate heap memory, and then use an arbitrary size in the keylen attribute to write outside the bounds of this location:
An example of a MSG_CRYPTO message that triggers the vulnerability
Exploitability of CVE-2021-43267
This vulnerability can be exploited both locally and remotely. While local exploitation is easier due to greater control over the objects allocated in the kernel heap, remote exploitation can be achieved thanks to the structures that TIPC supports.
As for the data being overwritten, at first glance it may look like the overflow will have uncontrolled data, since the actual message size used to allocate the heap location is verified. However, a second look at the message validation function shows that it only checks that the message size in the header is within the bounds of the actual packet. That means that an attacker could create a 20 byte packet and set the message size to 10 bytes without failing the check:
if (unlikely(skb->len
The Patch for CVE-2021-43267
In order to aid in fixing the issue quickly, I drafted a patch idea along with the report. After some very helpful discussion with one person from the Linux Foundation and one of the TIPC maintainers, the following patch was decided on:
This patch moves the size validation to take place before the copy has taken place instead of after it. I’ve also added a size overflow check along with additional checks for the minimum packet size and the supplied key size.
Remediation
As this vulnerability was discovered within a year of its introduction into the codebase, TIPC users should ensure that their Linux kernel version is not between 5.10-rc1 and 5.15.
Conclusion
The vulnerability research that SentinelLabs conducts allows us to protect users on a global scale by identifying and fixing vulnerabilities before malicious actors do. In the case of TIPC, the vulnerability was caught within a year of its introduction into the codebase. While TIPC itself isn’t loaded automatically by the system but by end users, the ability to configure it from an unprivileged local perspective and the possibility of remote exploitation makes this a dangerous vulnerability for those that use it in their networks. What is more concerning is that an attacker that exploits this vulnerability could execute arbitrary code within the kernel, leading to a complete compromise of the system.
Disclosure Timeline
19 Oct 2021 - SentinelLabs supplied the initial vulnerability report to the Kernel.org team
19 Oct 2021 - Greg K.H. responds and adds the TIPC maintainers to the email thread
21 Oct 2021 - The patch is finalised
25 Oct 2021 - The patch is added to lore.kernel.org
29 Oct 2021 - The patch is added to the mainline repository
31 Oct 2021 - The patch is now officially under 5.15
04 Nov 2021 - SentinelLabs publicly disclose details of the vulnerability