Two macOS persistence tricks abusing plugins

codecolorist
Nov 21 · 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.

macOS AMFI.kext!platformHardenFlags
macOS AMFI!library_validation_failure
iOS AMFI!library_validation_failure
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.


dspluginhelperd

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.


MIDIServer

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

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

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.

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 com.apple.security.cs.disable-library-validation 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:

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: https://github.com/theevilbit/macos/tree/master/PersistentQL

Less ideal because they are sandboxed.

CodeColorist

I write random stuff

codecolorist

Written by

just a script kiddy

CodeColorist

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