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.
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_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
OS Version, as well as the results of the following commands:
ps -efwhen executed on a Unix system;
tasklist /svcwhen 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
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
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.
The sh.py (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
sh.py also generates and saves a UID (albeit a 9-digit one).
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.
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:
xsiexecutes a command which is initially encoded as base64
load_setting()function and sends the current configuration to the server (from
sar.datfile), also encoded as base64
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
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.
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
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
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
Mach-O binaries are detected as
|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||sh.py||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|