JokerSpy | Unknown Adversary Targeting Organizations with Multi-Stage macOS Malware

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, 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[.] 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 – to the temp directory and executes the latter, which now opens the reverse shell from the victim to the attacker.

The files opens a reverse shell to the attacker

Shared.dat & | Cross-Platform Python Backdoors

In the intrusions seen to date, researchers identified two Python backdoors, shared.dat and 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/

The 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 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
Domain name
OS version
Python version
Path to

According to researchers at Elastic, the 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

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
CandidateCDHash sha256=89706d1258b6f1c165ff8d1d6d13346e02b48e22
CandidateCDHashFull sha256=89706d1258b6f1c165ff8d1d6d13346e02b48e22d1a741ff451d1cb6ba81bab2
Hash choices=sha256
Launch Constraints:
Info.plist=not bound
TeamIdentifier=not set
Sealed Resources=none
# 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

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



Files (SHA1)
1ed2c5ee95ab77f8e1c1f5e2bd246589526c6362 – xcc
1f99081affd7bef83d44e0072eb860d515893698 – SwiftBelt
21ffda8a6a05a007ef92088f99ab54485cfe473d – xcc
2234c9fc3c3d340f0367c49c6599379b96544b5a –
370a0bb4177eeebb2a75651a8addb0477b7d610b – xcc
76b790eb3bed4a625250b961a5dda86ca5cd3a11 – xcc
937a9811b3e5482eb8f96832454723d59229f945 – shared.dat
bd8626420ecfd1ab5f4576d83be35edecd8fa70e –
c304aef96a783a39aedf1af30de5d5f1c33c68ca – QRLog
c7d6ede0f6ac9f060ae53bb1db40a4fbe96f9ceb – shared.dat



Leave a Comment

Your email address will not be published. Required fields are marked *