Recent reports from researchers at BitDefender and Elastic have exposed an active adversary deploying novel spyware, cross-platform backdoors and an open-source reconnaissance tool to compromise organizations with macOS devices in their fleets. Although the number of known victims at this time is small, the nature of the tooling suggests that the threat actors have likely targeted other organizations.
In this post, we review the key components and indicators used in the campaign to help raise awareness and aid security teams and threat hunters.
QRLog | Suspected Infection Vector
There is little information about how initial compromise was achieved in the known compromises, but analysis of the known components provide a strong link to a trojanized QR code generator discovered in the wild in February 2023.
According to researcher Mauro Eldritch, QRLog is a trojanized QR code generator written in Java that opens a reverse shell on the host device, allowing the attacker privileged access. The malicious code is hidden inside a file QRCodeWriter.java, buried in an otherwise legitimate open source QR code project.
Base64 blob is an encoded Java file
After determining the host device’s operating system, QRLog decodes an embedded base64 blob and writes it out to a temporary directory and executes it.
QRLog changes the path separator to suit Windows or Posix-compatible systems like Linux and macOS
The decoded blob is a .java file that reaches out to a C2 at hxxps[:]//www[.]git-hub.me/view.php. This is the same C2 as used in the compromise reported by BitDefender (see below).
QRLog uses the same C2 later seen in an ITW JokerSpy intrusion
If the QRLog malware receives the right response from the C2, it then writes two further files – p.dat and a Java executable prefTmp.java – to the temp directory and executes the latter, which now opens the reverse shell from the victim to the attacker.
The prefTmp.java files opens a reverse shell to the attacker
Shared.dat & sh.py | Cross-Platform Python Backdoors
In the intrusions seen to date, researchers identified two Python backdoors, shared.dat and sh.py. The former uses a simple rot13 string obfuscation technique.
Deobfuscated strings found in shared.dat backdoor
The script’s behavior depends on the response from the server, whose address is hardcoded in plain text. In the intrusion seen by BitDefender, the C2 matched that seen in the QRLog malware. shared.dat also uses the same strings found in QRLog to identify packets sent and received from the C2, namely “GITHUB_RES” and “GITHUB_REQ”.
C2 in shared.dat is the same as noted in QRLog malware
A simple conditional parses the responses.
Parsing commands from the C2 in the shared.dat backdoor
If the device is identified as a macOS device, the malware downloads and executes the next stage to /Users/Shared/AppleAccount.tgz, which in turn decodes a further stage to /Users/Shared/TempUser/AppleAccountAssistant.app.
The sh.py backdoor is also multi-platform and requires a separate configuration file stored at ~/Public/Safari/sar.dat, likely containing the C2 as well as other parameters. The C2 observed by Elastic in an attack on an unnamed Japanese cryptocurrency exchange was app.influmarket[.]org.
The backdoor is capable of surveilling the host device and executing commands, exfiltrating data and deleting files.
The sh.py script is a cross-platform backdoor
Depending on the value received from the configuration file, the backdoor will beacon out to the C2 at regular intervals, the default being 5 seconds. Information sent to the attacker includes:
Current working directory
Path to sh.py
According to researchers at Elastic, the sh.py script was seen dropping the open-source macOS red-teaming tool SwiftBelt to the file path /Users/shared/sb and writing out to a file sb.log in the same directory.
/bin/sh -c /users/shared/sb > /users/shared/sb.log 2>&1
JokerSpy | macOS Spyware Stager
In both intrusions seen to date, a further macOS only component was observed. The file, named “xcc”, attempts to hide as an XProtect component, and uses the Launch Services identifier com.apple.xprotectcheck.
Format=Mach-O universal (x86_64 arm64)
CodeDirectory v=20400 size=911 flags=0x2(adhoc) hashes=17+7 location=embedded
Hash type=sha256 size=32
# designated => cdhash H”89706d1258b6f1c165ff8d1d6d13346e02b48e22″ or cdhash H”9860c28299d58e71540c64e56c709aa619cfac27″
The binary is ad hoc signed and is built for both Apple silicon and Intel architectures. On execution, it runs through several functions with the purpose of determining:
Device Idle Time
Active (Frontmost) App
Screen status (locked or unlocked)
Full Disk Access of the active app
Screen Recording permissions of the active app
Accessibility (e.g., control other apps) permission of the active app
Output to stdout from executing xcc binary
Threat hunters should note that these are somewhat noisily printed out to stdout, so will appear in system logs.
The inclusion of the SystemIdleTime() function is interesting and not something commonly seen in macOS malware. This may indicate the threat actor intends to establish a pattern of behavior as to when the user is inactive in order to time attacks. The function itself uses Apple’s IOServiceMatching() api and the now deprecated IOHIDSystem class to query for the HIDIdleTime value, a timer which tracks the last time the user interacted with the mouse, trackpad or keyboard, among other things.
Calls made by the SystemIdleTime() function
At the time of writing, it’s not clear how the xcc binary relates to the other components, other than that they have been observed together in both instances. xcc itself provides functionality for system discovery and it is likely there are further associated spyware and backdoor components that remain to be discovered.
Elastic observed xcc being executed by three different processes:
/Applications/Visual Studio Code.app/Contents/MacOS/Electron
The researchers suggest that initial access may have been provided via a malicious plugin or dependency that may have been trojanized in a similar way to QRLog mentioned above.
SentinelOne Detects JokerSpy
The SentinelOne agent protects customers from JokerSpy, QRLog and other malicious components identified in these attacks.
Security teams not protected by SentinelOne are advised to refer to the list of indicators below for threat hunting and detection.
The JokerSpy intrusions reveal a threat actor with the ability to write functional malware across several different languages – Python, Java, and Swift – and target multiple operating systems platforms. The relative sophistication of the multiple components suggests one or more developers devoting considerable effort to the project, and we can only surmise that further victims are out there.
There are still several pieces of the puzzle missing, but the intrusion into a large cryptocurrency exchange indicates a financially-motivated threat actor. SentinelOne continues to track this threat actor and will provide updates to this post as they become available.
Indicators of Compromise
1ed2c5ee95ab77f8e1c1f5e2bd246589526c6362 – xcc
1f99081affd7bef83d44e0072eb860d515893698 – SwiftBelt
21ffda8a6a05a007ef92088f99ab54485cfe473d – xcc
2234c9fc3c3d340f0367c49c6599379b96544b5a – QRCodeWriter.java
370a0bb4177eeebb2a75651a8addb0477b7d610b – xcc
76b790eb3bed4a625250b961a5dda86ca5cd3a11 – xcc
937a9811b3e5482eb8f96832454723d59229f945 – shared.dat
bd8626420ecfd1ab5f4576d83be35edecd8fa70e – sh.py
c304aef96a783a39aedf1af30de5d5f1c33c68ca – QRLog sample.zip
c7d6ede0f6ac9f060ae53bb1db40a4fbe96f9ceb – shared.dat