Inside XCSSET: Evolution, Tradecraft, and Threat Hunts

A newly discovered variant of the XCSSET malware has surfaced in a series of targeted attacks, showcasing improved obfuscation techniques, revamped persistence methods, and inventive infection tactics. This latest campaign marks a notable escalation, with a clear focus on compromising Apple Xcode projects and their developers. Reports indicate that the malware is designed to steal and exfiltrate sensitive user and system data, including digital wallet information and personal notes. This blog post explores the malware’s evolution, its current capabilities, and practical steps defenders can take to mitigate the threat.

The Evolution of a Developer’s Nightmare

XCSSET was first documented by Trend Micro in August 2020, distinguishing itself early on by infecting Apple Xcode projects. Its initial iterations were notable for leveraging AppleScript and even Python scripts to obfuscate intentions and handle bash commands.

Since its discovery, XCSSET has continually adapted:

The evolution of XCSSET malware
  • Early Capabilities (2020-2021): The malware rapidly evolved to compromise newer macOS versions and even Apple’s M1 chipsets. By mid-2021, it had expanded its data exfiltration capabilities, targeting information from a wide array of applications, including Google Chrome, Telegram, Evernote, Opera, Skype, WeChat, and Apple’s own Contacts and Notes.
  • Zero-Day Exploitation: Around mid-2021, Jamf reported XCSSET’s ability to exploit CVE-2021-30713, a Transparency, Consent, and Control (TCC) framework bypass bug. This critical zero-day allowed the malware to take screenshots of a victim’s desktop without requiring additional permissions, by piggybacking on the permissions of legitimate “donor” applications like Zoom.
  • Adapting to macOS Monterey and Python Changes (2022): Over a year later, the malware was updated to support macOS Monterey. Threat actors behind XCSSET were also noted to be preparing for Python’s eventual deprecation, updating their AppleScripts to account for Python 3 and macOS Monterey 12.3 and above. Obfuscation techniques diversified, moving from primarily xxd (hexdump) to also incorporate Base64 for encoding. The locations of its fake applications for hiding the primary executable shifted from a fake Xcode.app to Mail.app, and then to a fake Notes.app, often residing in random locations within the user’s Library folder. Furthermore, elements like curl’s –max-time and the phaseName variable in Xcode infection scripts became randomised to hinder static detection. Persistence files also transitioned from ~/Library/Caches/GeoServices/ to ~/Library/Caches/GitServices/

New Capabilities and Techniques: A Closer Look

The latest XCSSET variant, the first major revision since 2022, boasts significant advancements aimed at challenging analysis and ensuring persistent malicious operations.

Enhanced Obfuscation

The new variant heavily relies on sophisticated obfuscation:

  • Obfuscated Module Names: Module names are obfuscated, making static analysis extremely difficult to determine their intent.
  • Randomised Payload Generation: Payloads used to infect Xcode projects and for encoding are generated using a randomised approach.
  • Diverse Encoding: The malware now incorporates Base64 encoding in addition to the previously used xxd (hexdump), with a random number of encoding iterations (between 2 and 5).

Updated Persistence Mechanisms

The malware employs three distinct persistence techniques to ensure its payload is launched reliably:

Persistence with .zshrc

The malware modifies the ~/.zshrc file and creates a ~/.zshrc_aliases file. This ensures that the malicious payload is executed every time a new shell session is initiated.

Microsoft identified persistence with .zshrc file

Persistence with Dock

XCSSET downloads a legitimate, signed dockutil utility from its command-and-control (C2) server. It then creates a fake Launchpad application (often located in ~/Library/Caches/com.apple.finder) and replaces the legitimate Launchpad’s path entry in the user’s Dock with this fake one. This clever trick ensures that whenever the Launchpad is started from the Dock, both the legitimate Launchpad and the malicious payload are executed.

Microsoft identified persistence with dock modification
Microsoft identified persistence with dock modification

Persistence with Git Pre-commit hooks

For developers, a particularly insidious method involves incorporating the malware’s payload into the pre-commit hooks within Git repositories’ .git directories. This means the payload will execute automatically upon each git commit action within an infected project.

Microsoft identified persistence with git pre-commit hooks

New Infection Strategies

The infection chain is typically a four-stage process, culminating in the execution of various sub-modules:

  • First Stage: A shell payload is executed when a user unknowingly builds an infected Xcode project. This payload is heavily obfuscated, passing through multiple iterations of a hex decoder before making a curl request to a C2 server (https[:]//bulknames[.]ru/a) to download the next stage.
Microsoft identified obfuscated first-stage shell payload
  • Second Stage: The downloaded obfuscated shell command collects the affected user’s device operating system information, sends it to the C2 server, and fetches an additional shell script payload.
Microsoft identified the second-stage command sent to the C2 server.
  • Third Stage: This shell script checks the device’s XProtect version (ensuring it’s less than 5287), stops certain processes, and then creates an AppleScript-compiled application using osacompile. It modifies the Info.plist file of this application to enable the LSUIElement key, effectively hiding the application from the Dock and making it a background process.
Microsoft identified third-stage shell script
  • Fourth Stage: The hidden AppleScript application executes a shell command that decodes a Base64-encoded blob to obtain the final script. This script collects system information (macOS version, Safari version, user locale, firewall/SIP status, CPU information) and sends it to the C2 server. Crucially, it then launches various sub-modules.
Microsfot identified base64-encoded fourth-stage AppleScript payload

These sub-modules are a hallmark of XCSSET’s modular design, each serving a specific malicious purpose:

  • seizecj: Steals system information, including application lists, user-level LaunchAgents, and versions of XProtect and Malware Removal Tool (MRT).
  • fpzfcieoci: Discovers and lists web browser extensions installed across popular browsers like Brave, Google Chrome (including Canary and Beta), Microsoft Edge, Mozilla Firefox, and Opera.
  • hxasoxtfd: Acts as a downloader, constantly requesting and launching additional modules from the C2 server.
  • txzx_vostfdi: Steals data from digital wallet browser extensions, including MetaMask, TokenPocket, TronLink, BNB Chain Wallet, and Phantom Wallet, by searching for their specific identifiers.
  • hfdieiz: Responsible for establishing the zshrc and Dock persistence methods detailed above.
  • cozfi_xhh: Exfiltrates notes from the Apple Notes application.
  • vectfd_xhh: A common launcher module used to launch other modules. It checks for the presence of Xcode or Git and creates fake versions of these or defaults to Finder.app or Terminal.app to launch the main malicious module.
  • dfhsebxzod: Designed specifically to infect Xcode projects on the target device.
  • jez: Implements the Git commit persistence mechanism, infecting .git/hooks/pre-commit files.
  • Under Development Modules: Reports suggests threat actors are actively refining their tools. Several modules, including uhsoxtfd_vostfd (directory uploader), fpfb (directory listing), and vectfd (specific file exfiltration), appear to be under active development. These indicate future expansion of data exfiltration capabilities.

MITRE ATT&CK Techniques

XCSSET activity has been observed aligning with numerous MITRE ATT&CK techniques:

  • T1195.001: Supply Chain Compromise: Compromise Software Dependencies and Development Tools
  • T1059.002: Command and Scripting Interpreter: AppleScript
  • T1059.007: Command and Scripting Interpreter: JavaScript
  • T1059.004: Command and Scripting Interpreter: Unix Shell
  • T1546.004: Event Triggered Execution: Unix Shell Configuration Modification
  • T1560: Archive Collected Data
  • T1005: Data from Local System
  • T1041: Exfiltration Over C2 Channel
  • T1083: File and Directory Discovery
  • T1222.002: File and Directory Permissions Modification: Linux and Mac File and Directory Permissions Modification
  • T1564.001: Hide Artifacts: Hidden Files and Directories
  • T1105: Ingress Tool Transfer
  • T1036.005: Masquerading: Match Legitimate Name or Location
  • T1647: Plist File Modification
  • T1518: Software Discovery
  • T1082: System Information Discovery
  • T1614.001: System Location Discovery: System Language Discovery
  • T1548.006: Abuse Elevation Control Mechanism: TCC Manipulation
  • T1140: Deobfuscate/Decode Files or Information
  • T1564.003: Hide Artifacts: Hidden Window
  • T1070.004: Indicator Removal: File Deletion
  • T1027.004: Obfuscated Files or Information: Compile After Delivery
  • T1027.013: Obfuscated Files or Information: Encrypted/Encoded File
  • T1217: Browser Information Discovery
  • T1518.001: Software Discovery: Security Software Discovery
  • T1033: System Owner/User Discovery

Hunting for XCSSET in your environment

Behavioural Hunts

  • Persistence with Dock modification / Fake Launchpad
    let timeRange = ago(7d);
    let dockutilIndicators = dynamic(["dockutil", "plutil"]);
    DeviceProcessEvents
    | where Timestamp > timeRange
    | where ProcessCommandLine has_any (dockutilIndicators)
    | project DeviceName, Timestamp, InitiatingProcessFileName, InitiatingProcessAccountName, ProcessCommandLine
    
    let timeRange = ago(7d);
    let suspiciousDir = "Library/Caches/com.apple.finder";
    DeviceFileEvents
    | where Timestamp > timeRange
    | where FolderPath has suspiciousDir
    
    let timeRange = ago(7d);
    DeviceProcessEvents
    | where Timestamp > timeRange
    | where ProcessCommandLine contains "plutil"
    | where ProcessCommandLine contains "Info.plist"
    | project DeviceName, Timestamp, InitiatingProcessFileName, ProcessCommandLine
    
  • Persistence with .zshrc moodification
    // Detect creation or modification of .zshrc_aliases and changes to .zshrc referencing it
    let timeRange = ago(7d);
    let zshrcFile = ".zshrc";
    let aliasesFile = ".zshrc_aliases";
    DeviceFileEvents
    | where Timestamp > timeRange
    | where FileName endswith aliasesFile
    | extend FullPath = strcat(FolderPath, FileName)
    | summarize AliasesFileModified=count(), FirstSeen=min(Timestamp), LastSeen=max(Timestamp) by DeviceName, InitiatingProcessFileName, FullPath, InitiatingProcessAccountName
    | union (
      DeviceProcessEvents
      | where Timestamp > timeRange
      | where ProcessCommandLine contains ".zshrc"
      | where ProcessCommandLine has ".zshrc_aliases"
      | project DeviceName, Timestamp, InitiatingProcessAccountName, InitiatingProcessFileName, ProcessCommandLine
    )
    
  • Persistence with Git pre-commit hook infection
    let timeRange = ago(7d);
    DeviceFileEvents
    | where Timestamp > timeRange
    | where FolderPath has "/.git/hooks" and FileName == "pre-commit"
    | summarize PreCommitHookTouched=count(), FirstSeen=min(Timestamp), LastSeen=max(Timestamp) 
            by DeviceName, FileName, FolderPath, InitiatingProcessFileName, InitiatingProcessAccountName
    
  • Fake Reminders App for stealing contents from Apple Notes
    let timeRange = ago(7d);
    let suspiciousAppPath = "/Applications/Reminders.app/Notes";
    DeviceFileEvents
    | where Timestamp > timeRange
    | where FolderPath has suspiciousAppPath
    | summarize FileCreated=count(), FirstSeen=min(Timestamp), LastSeen=max(Timestamp)
          by DeviceName, FolderPath, FileName, InitiatingProcessFileName, InitiatingProcessAccountName
    

Indicator based hunts

  • Hunting for C2 servers:
    DnsEvents
    | where Name in (
      "bulknames.ru",
      "castlenet.ru",
      "chaoping.ru",
      "devapple.ru",
      "gigacells.ru",
      "gizmodoc.ru",
      "trixmate.ru",
      "itoyads.ru",
      "rigglejoy.ru",
      "rutornet.ru",
      "sigmate.ru",
      "vivatads.ru",
      "figmasol.ru"
    )
    

References

  • https://www.trendmicro.com/en_in/research/20/h/xcsset-mac-malware–infects-xcode-projects–uses-0-days.html
  • https://www.trendmicro.com/en_in/research/21/d/xcsset-quickly-adapts-to-macos-11-and-m1-based-macs.html
  • https://www.jamf.com/blog/zero-day-tcc-bypass-discovered-in-xcsset-malware/
  • https://www.microsoft.com/en-us/security/blog/2025/03/11/new-xcsset-malware-adds-new-obfuscation-persistence-techniques-to-infect-xcode-projects/



    Enjoy Reading This Article?

    Here are some more articles you might like to read next:

  • Cyber Kill Chain - Explained