Two macOS persistence tricks abusing plugins

Nov 21, 2019 · 4 min read

This blog does not involve any vulnerability, but I hope the readers can find these tricks useful for red teaming and anti-malware.

Since Mojave (10.14), Hardened Runtime has been introduced to bring global Library Validation enforcement, which prohibits dynamic libraries without valid code signature from the same developer or Apple from being loaded.

Some entitlements can mark an executable as an exception.

Image for post
Image for post
macOS AMFI.kext!platformHardenFlags
Image for post
Image for post
macOS AMFI!library_validation_failure
Image for post
Image for post
iOS AMFI!library_validation_failure
Image for post
Image for post
AMFI for macOS

So when an executable has any one of these entitlements, it implies that this process is designed to load third-party libraries. This leads me to these interesting persistence vectors on macOS.

Image for post
Image for post

This daemon (/usr/libexec/dspluginhelperd) is for loading 3rd-party DirectoryService plugins. It launches on each system boot, and automatically reloads after crash.


In function CPluginHandler::LoadPlugins, it scans bundles that match /Library/DirectoryServices/PlugIns/*.dsplug and execute them. Since this process has root privilege and no sandbox at all, it is a powerful place to put persistence payload.

/Library/DirectoryServices/Plugins requires root privilege to write. The malware must either trick user to input administrator password or exploit a privilege escalation bug.

ObjectiveSee’s KnockKnock v2.1 has introduced detection for this persistence vector.

Image for post
Image for post

You must be familiar with it because this service has just been exploited in this year’s TyphoonPwn for userspace sandbox escape on iOS.

Image for post
Image for post

It loads plugins (recursively search for child directories) from three locations:

  • /System/Library/Extensions/**/*.plugin
  • /Library/Audio/MIDI Drivers/**/*.plugin
  • ~/Library/Audio/MIDI Drivers/**/*.plugin
Image for post
Image for post

This agent does not set RunAtLoad to YES, and system built-in LaunchAgents is readonly, so we need to write a new launch agent entry in ~/Library/LaunchAgents/whatever.plist. Since launchd also supports setting environment string in the plist, we can use an alternative path for HOME directory. This vector does not require root privilege.

It can not hide itself from persistence detection softwares like KnockKnock. But after all, this executable is 100% valid signed by Apple.

Image for post
Image for post

Since we’ve talked about the recent bug reports for MIDIServer:

Could this be a persistence method on iOS?

Unfortunately, not.

AMFI on iOS is slightly different than macOS. Actually, iOS does not care about at all :(

On the other hand, Apple has introduced a new sandbox profile on iOS 13 to reduce its attack surface. But on macOS, sandbox profile named MIDIServer is nowhere to be found. Then it is like:

Image for post
Image for post

So this executable has one entitlement that does not work on iOS, and another one does not work on macOS 😢

On 10.15.1 there are about 30+ executables possessing this entitlement. I only analyzed some of them and found these two nice example.

You may also refer to other vectors like QuickLook plugins:

Less ideal because they are sandboxed.


I write random stuff

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch

Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore

Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store