Fragments of Cross-Platform Backdoor Hint at Larger Mac OS Attack

Promo Protect all your devices, without slowing them down.
Free 30-day trial
Fragments of Cross-Platform Backdoor Hint at Larger Mac OS Attack

During routine detection maintenance, our Mac researchers stumbled upon a small set of files with backdoor capabilities that seem to form part of a more complex malware toolkit. The following analysis is incomplete, as we are trying to identify the puzzle pieces that are still missing.

As of now, these samples are still largely undetected and very little information is available about any of them. The earliest mention we could find is an anonymous April 18 upload on VirusTotal (IoC A),  as well as the three samples (B through D)  that seem to have been uploaded by the victim we worked with in the investigation.

IoC A seems very similar to IoC B and shares the same feature set. The only difference is the order of two Python imported libraries.

Two of the three isolated samples are generic backdoors written in Python that  seem to target Mac OS, Windows and Linux-based operating systems.

To identify the victim’s operating system, the shared.dat backdoor has a routine that checks the OS, then returns 0 for Windows, 1 for macOS and 2 for Linux.

shared.dat – a generic Python backdoor

The first file identified is called shared.dat and uses rot13 substitution to hide the values of specific file paths and strings. It also generates a unique device identifier for later use in requests to the command and control center.

Upon execution, the backdoor first generates the UID mentioned above. This UID is then used  to name a temporary file (<uid>.dat) The malware then enters a while True loop  where it attempts to communicate with a remote server using a custom packet format. These packets start with either GITHUB_RES or GITHUB_REQ + the UID generated in the previous step. The backdoor supports four commands sent out as cmdType numbers by the remote server:

Code 501: Extract basic info

When the backdoor receives a cmdType 501 command, it extracts details such as Current Time, Username, Hostname, OS Version, as well as the results of the following commands:

  • ifconfig -a and ps -ef when executed on a Unix system;
  • ipconfig /all and tasklist /svc when executed on a Windows machine.

These details are also written to a file received as an argument – in our case, the file is called called b.dat

Code 502:  CmdExec

The cmdExec function is apparently used to run a specific command provided as an argument using the subprocess.Popen function. The request contains the command encoded as base64 and the results are encoded using the same method.

Code 503: DownExec

The DownExec routine behaves differently, based on the victim’s operating system. For MacOS devices, the function writes a file to /Users/Shared/AppleAccount.tgz. The content that is written to the archive is also encoded as base64 when received from server.  It unpacks the archive to  the /Users/Shared  folder, then opens the /Users/Shared/TempUser/ application.

On Linux systems, the malware calls a dist_name function that checks the /etc/os-release to validate whether the victim distro is Debian, Fedora or anything else. The function writes some C code received from the C2 to a temporary file named tmp.c, that is later compiled to a file named /tmp/.ICE-unix/git using the cc command on Fedora and gcc on Debian. After compilation, the file is executed in background with two parameters, also received from C2.

Code 504: KillSelf

This function is self-explanatory and is used to just exit the script. – a more potent backdoor counterpart

The (SHA-1 bd8626420ecfd1ab5f4576d83be35edecd8fa70e) backdoor is also written in Python and has cross-platform capabilities. It stores its configuration options in the ~/Public/Safari/sar.dat file after it encodes them to base64. The settings stored in this file include details such as UID, SleepCycleMin, as well as two possible Server URLs. Much like the shared.dat backdoor, also generates and saves a UID (albeit a 9-digit one).

Unlike the shared.dat backdoor, this one does not come with  a  hardcoded value  for the  C&C  server. Instead,  the  value for  the  C&C  can  be  provided as a parameter when running the script or can be loaded from the settings file.

In its main function, the script communicates with the remote server using a while loop and handles specific commands using the  `process_command' function that can receive any of the following values as argument:

  • "l" -> files listing (path, size, access times, permissions)
  • "c" -> output of a command executed on the system
  • "cd" -> change directory
  • "xs", "xsi" -> execute command using exec. Unlike xs, xsi executes a command which is initially encoded as base64
  • "r" -> remove a file/directory
  • "e" -> execute a file from a specific path  (also with parameters)
  • "u" -> writes the data received from server to a file after base64-decoding it
  • "d" -> reads data from a file and sends it to the server in batches, encoded as base64
  • "g" -> calls the load_setting() function and sends the current configuration to the server (from sar.dat file), also encoded as base64
  • "w" -> sets the value for particular options, such as SleepCycleMin or ServerUrl (2 elements array) using the save_settings function

The backdoor also includes a 'get_basic_information' function that extracts specific details about the system, such as hostname, username, OS version, Python version and much more. When executed, the script attempts to connect to one of the two server URLs  in order to send these details over. If the first URL is not available, it then attempts to contact the second one.

xcc – a precursor to spyware

Unlike the previous two files, this third component  is a FAT binary (a multi-architecture file) that contains Mach-O files for 2 architectures (x86 Intel and ARM M1). The xcc binary is written in Swift and targets MacOS version 12 and newer. Its primary purpose is apparently to check permissions before using a potential spyware component (probably to capture the screen)  but does not include the spyware component itself. This leads us to believe that these files are part of a more complex attack and that several files are missing from the system we investigated.

In addition to the xcc file uploaded on Jun 1st, we were able to isolate a second one dated June 6 (sample E in our IoC list). Unlike the “original” xcc file, this one only contains a Mach-O binary for the x86 architecture.

The xcc files have an ad-hoc signature, meaning that they are not associated with a recognized Apple Developer. The identifier of the file contains the keyword XProtectCheck, as well as a path identified inside the file content, /Users/joker/Downloads/Spy/XProtectCheck/ which hints at the project’s purpose, as well as at the role of this component.

xcc checks for permissions managed by Apple's TCC (Transparency, Consent and Control), such as Full Disk Access, Screen Recording and Accessibility. The latter is verified using "AXIsProcessTrusted" function, part of macOS Accessibility API, while the permission to capture the screen is obtained by calling CGPreflightScreenCaptureAccess(). Also, the function getTopWindowApp identifies the active app that the user currently interacts with using Apple's frontmostApplication property.

Command and Control

The command-and-control server is hardcoded in the share.dat Python backdoor.  The first reference to this domain dates back to February 10 2023,  roughly around the same time it was mentioned in a series of Tweets related to an infected MacOS QR code reader (QRLog).


Bitdefender products flag the Python components as Trojan.Python.JokerSpy. The Mach-O  binaries are detected as  Trojan.MAC.JokerSpy.

Indicators of Compromise


ID File Name SHA-1 Discovery Date Detection Name
A shared.dat 937a9811b3e5482eb8f96832454723d59229f945 Apr 18, 2023 Trojan.Python.JokerSpy.B
B shared.dat c7d6ede0f6ac9f060ae53bb1db40a4fbe96f9ceb Jun 1, 2023 Trojan.Python.JokerSpy.C
C bd8626420ecfd1ab5f4576d83be35edecd8fa70e Jun 1, 2023 Trojan.Python.JokerSpy.A
D xcc (Mach-O) 370a0bb4177eeebb2a75651a8addb0477b7d610b Jun 1, 2023 Trojan.MAC.JokerSpy.B
E xcc (FAT binary) 1ed2c5ee95ab77f8e1c1f5e2bd246589526c6362 Jun. 6, 2023 Trojan.MAC.JokerSpy.A
F xcc (Mach-O) 76b790eb3bed4a625250b961a5dda86ca5cd3a11 Jun. 1, 2023 Trojan.MAC.JokerSpy.C





I'm a Security Researcher at Bitdefender, always trying to be one step ahead of cybercriminals targeting Unix. I am passionate about exploring the world and staying active through sports.

View all posts

Information security professional. Living my second childhood at @Bitdefender as director of threat research.

View all posts

You might also like