Skip to content
how amos macos stealer avoids detection
Blog Security How AMOS m...

How AMOS macOS Stealer Avoids Detection

Atomic macOS Stealer (AMOS) was first spotted in early 2023. It's a powerful piece of malware that targets Apple users and tricks them into installing the software on their computers. The malware is sold via Telegram; as of January 20, 2024, the price was $3,000 a month. 

Once installed, Atomic Stealer can exfiltrate an extensive amount of data, including keychain passwords, user documents, system info, cookies, browser data, credit card information, cryptocurrency wallets, and more. 

AMOS ad translated_shadowAdversaries leveraging AMOS direct victims to a website (the URL of which changes constantly) to download an unsigned disk image (DMG). If a user were to go that URL accidentally, they'd see only a benign welcome message. 

AMOS welcome screen_shadowOn that website is a subdirectory where 12 DMG files are available for download: Six in the /name directory, another six in /name/files.

AMOS name directory_shadowAMOS name files directory_shadow

Each of these twelve DMG files gives you a different hash. This is a way to avoid hash-based detection.  (We'll explain how they do that in a bit.) Because of this, hash-based detection for this new version will not work, since every time someone downloads the sample, they’d need to look for a new hash. From our research, this is new: AMOS creators have not previously had a way to iterate small portions of code to change hashes.

If you did download one of these DMG files, a pop-up would appear that looks similar to any other application you download from outside the Apple store.

AMOS install window_shadowInside that DMG is a Mach-O that, if the user right-clicks and opens it, the malware will execute. 

XOR Encoding

Since it first appeared, AMOS has continued to evolve. A recent sample included XOR instructions to prevent detections of strings known to be associated with this stealer. The capabilities of the latest sample do not appear to have changed, but its obfuscation tactics differ from previously reported samples. 

In the example below, some of the strings are visible even with the XOR encoding. The XOR key for these is 0x90, so we can see how these are separated by null bytes. It appears that, for many of these, only half of the strings were XOR encoded. 

code sample1_shadow

In a more recent sample, we can see that no strings are visible in the const section. Something appears to have changed with how the encoding is completed. This further highlights how quickly and often the AMOS malware changes.  

code sample2_shadowLet’s dive into an example of how an XOR function is called, along with the arguments that are passed in the arm64 slice of the Universal binary. 

1  10000c7cc  28108052  mov   w8, #0x81
2  10000c7d0  e8430039  strb  w8, [sp, #0x10 {xorKey}]  {0x81}
3  10000c7d4  e8430091  add   x8, sp, #0x10 {xorKey}
(...)
4  10000c84c  29008052  mov   w9, #0x1
5  10000c850  e013813c  stur  q0, [sp, #0x11 {var_af}]
6  10000c854  3f0d02f1  cmp   x9, #0x83
7  10000c858  80000054  b.eq  0x10000c868
8  10000c85c  ea434039  ldrb  w10, [sp, #0x10 {xorKey}]
9  10000c860  b20d0094  bl    _OUTLINED_FUNCTION_1
10 10000c864  fcffff17  b     0x10000c854
11 10000c868  a0110094  bl    _system

Starting at line 1, the value 0x81 is moved to register W8. This will be the XOR key used later to decode the string stored on the stack. 

At line 2, the XOR key is stored on the stack at the address SP + 0x10

Line 3 indicates that a pointer to this offset on the stack is moved to register X8

Between line 3 and 4, hex values are moved into registers and stored on the stack. This will be the string to decode. 

Line 4 shows the value 0x1 is moved into W9. This will serve as the index used to iterate through the string. 

Line 5 indicates that the value stored at register Q0 is saved on the stack at SP + 0x11. This is one byte away from where the XOR key is stored. 

Line 6 compares the value stored at X9 and 0x83, which would be the length of the string.

Line 7 shows a branch if equals instruction, which would branch to the address 0x10000c82c when X9 = 0x83. The system() function is at 0x10000c82c, which indicates that, once the decoding is complete for 0x82 characters, the output is passed as the argument to the system() function.

Line 8 is a LDRB instruction that loads the byte value stored at SP +0x10 (XOR Key) to W10

Line 9 is the branch to the function called _OUTLINED_FUNCTION_1, where the XOR instructions occur. 

Line 10 indicates that once the XOR function is completed, a branch back to the comparison of the value stored in W9 is completed to continue this loop. 

Line 11 is the system() function call after the loop completes. This sets up the call to the system() function after decoding the string using an XOR function. 

Let’s now look at the XOR function called _OUTLINED_FUNCTION_1, which we branched to above. 

10000ff28  int64_t _OUTLINED_FUNCTION_1(char* arg1 @ x8, int64_t arg2 @ x9, 
10000ff28  char arg3 @ x10)
10000ff28  0b696938  ldrb  w11, [x8, x9]
10000ff2c  6a010a4a  eor  w10, w11, w10
10000ff30  0a692938  strb  w10, [x8, x9]
10000ff34  29050091  add  x9, x9, #0x1
10000ff38  c0035fd6  ret     

The function definition indicates that the values stored in X8, X9, and X10 are passed as arguments: X8 is pointing to the stack at SP +0x10; X9 is our index, which at the start of this loop was given the value 0x1; and X10 has the XOR key byte value 0x81.

Using the value in X9 as the index, the byte stored at the memory address X8 + X9 is loaded into W11. The encoded string starts at the address of SP +0x11, one byte away from what X8 is pointing to. This is why the loop starts with X9 having the value 0x1 as the index. 

The EOR (XOR) instruction then uses the key (0x81) stored in W10 to calculate the hex value stored back into W10. The STRB instruction then stores this byte back to the location at [X8, X9] which is the string pointed to by X8 with the index in X9

This example creates an osascript command that is common in AMOS samples and is passed to the system function to execute. 

"osascript -e 'display dialog "Some error occurred while running the application." buttons {"OK"} default button 1 with icon stop'"

This example is just to demonstrate how one string is decoded in this manner using the 0x81 key. There are many other examples in the binary that leverage similar functionality to decode the strings. To highlight the use of the XOR function described above, we can see that _OUTLINED_FUNCTION_1 is called 120 times by other functions in the binary, further highlighting the use of this encoding.

AMOS is an active malware that is constantly changing and frequently being updated. As we post this, the source website is still live and is being actively updated; on March 1, the site was updated between 2:00 and 8:00 AM PST. Kandji is actively tracking the group that is distributing this malware and will continue to monitor and update our security processes as needed. We suggest that all security devs/teams should do the same.

Indicators Of Compromise

DMG

SHA256

CrackSetup.dmg/dowload1.php: c988fd14753a8ee73d5c2747e4deeda8ef798deeb747435a112744d243cfb7ba

CrackSetup-2.dmg/dowload2.php: 400303250be2414d340675226aea7f78757ea8d1413af8dcd8f2c7a8d3ff8e21

CrackSetup-3.dmg/dowload3.php: 0e8aa909c1fefe12fd12f8f1e0073203ef4111f27fefb48568fd6ab02c13fb38

CrackSetup-4.dmg/dowload4.php: 9e52d52e53393ac49447a8fa1d0e98b4f7835346b7f7b85df1b9c891b554924c

CrackSetup-5.dmg/dowload5.php: 785e96c34123d1b2e30e9cc43972ab165f584815cdb323bd892b78fe7c9980e7

CrackSetup-6.dmg/dowload6.php: 64abdcd49e9f12f6f92457a006dc1876f04dc1ad9bcab5d1766f126a87a5c60a

CrackInstaller.dmg: ed46d8865491e81d14197eafc71165eb0358086680a604e4bf6fe9c6372e741c

hendro.dmg: 469a0176993e13c28b5b8f7f85a382e576f5e93310c8f5dec53a860783b97f0f

riesling.dmg: d7c7aad5899fa10e47cd1f7a224c62a44ea2858b1205762e183d69cdf356a6c2 

vera.dmg: cb6ebdc900d730f844f07d9d66ee4008aeedff1eac0662553f1713fffb29f058

whiskey.dmg: 596330886e915d9905d72284dcba6b663d8db95e84c00d25e1e19a3d79b01664

zinfandel.dmg: aebd77032f05029f0c6d614d40a5891926daec04fba4254a671b59709dfa625f

MD5

CrackSetup.dmg/dowload1.php: 086e6e79345098505e607e227e59e244

CrackSetup-2.dmg/dowload2.php: 672ab7261497112403b822425da1c7a0

CrackSetup-3.dmg/dowload3.php: 9cb409075d23bbc1e9abbc72a522ebd8

CrackSetup-4.dmg/dowload4.php: aaa426bb97124bfce12b729e83711f5d

CrackSetup-5.dmg/dowload5.php: e5e9ef4b446908dfe73abdc5e48bfd54

CrackSetup-6.dmg/dowload6.php: 6c58ea2f6e62cfbff58de2e9577f1c10

CrackInstaller.dmg: dab48ed8fb5d64b6004b15e42589a03b

hendro.dmg: 0378ecb18fd5e1dadab35b30f5f9cd34

riesling.dmg: 934443be5b69308026179ab65693957f

vera.dmg: aea42471d9474bcafc84ee3db755f6ae

whiskey.dmg: 9b4e5e7c9970ff911cc792a2fb372415

zinfandel.dmg: c8d890226e28a00b9f6c171c1d817de3