Sinter: macOS用户模式安全执行新方案
简单、开源且基于Swift
Sinter是我们为macOS 10.15及以上版本开发的新开源端点安全执行代理,完全使用Swift编写。我们从零开始构建了这款100%用户模式代理,利用新的EndpointSecurity API从macOS内核接收一组安全相关事件类型的授权回调。Sinter通过简单规则控制事件允许或拒绝,且不使用传统防病毒解决方案中昂贵的全系统扫描或基于签名的检测。
立即获取测试版安装程序并试用!
目前,Sinter允许您编写一组规则来阻止或允许进程执行事件,并通过Santa兼容的同步服务器或本地配置文件向代理提供规则(这是一个演示显式允许的示例规则)。然而,我们计划开发更复杂的规则语法,并为API支持的许多其他类型事件添加阻止能力,这也将意味着Santa规则兼容性的结束。
追求100%用户模式安全代理
实现端点安全解决方案(例如防病毒、反恶意软件)需要实时拦截和授权OS级事件。历史上,这意味着使用内核模式回调API或在未提供适当API时挂钩内核模式操作系统代码。操作系统开发人员早就知道,像这样的第三方内核模式代码是系统不稳定和不安全的主要来源,因为内核代码中的任何小错误往往都会产生严重后果。
于是macOS EndpointSecurity API应运而生。2019年底,Apple宣布在macOS中弃用对所有第三方内核扩展的支持,并将引入用户模式API和框架来替代第三方产品所需的功能。所有安全供应商都接到通知:在明年内弃用现有内核模式解决方案,并在下一版macOS(macOS 11 Big Sur)发布前迁移到EndpointSecurity API。这对许多团队来说显然不是一件有趣的事情,公告发布后不久,一位客户就委托我们开发一种用户模式解决方案,以减轻迁移痛苦。
什么是EndpointSecurity API?
EndpointSecurity是一个API,它在特定事件即将发生时实时实现从macOS内核的回调。EndpointSecurity客户端订阅一种或多种事件类型,这些类型要么是NOTIFY类型,要么是AUTH(授权)类型。Notify顾名思义,对于在主机上捕获简单活动日志非常有用。授权回调则强大得多;它允许客户端进程决定允许或拒绝事件发生。
EndpointSecurity取代了macOS上实时事件授权的内核模式等效项(Kauth KPI和其他不受支持的内核方法)以及只读事件监控OpenBSM审计跟踪。任何用于macOS的实时监控或保护产品都必须重写以使用EndpointSecurity for macOS 11 Big Sur。
请注意,EndpointSecurity API中没有网络相关事件(除了UNIX域套接字)。所有这些都在网络扩展框架中。您可以从一个系统扩展中结合使用这两种API,但这里我们特别关注EndpointSecurity API。
使用此API,FireEye的Stephen Davis和Objective-See的Patrick Wardle迅速发布了事件监控应用程序,可以实时显示例如进程相关和文件相关的事件。但追随Process Monitor(“ProcMon”)脚步的只读监控工具,虽然有用,但仅使用了EndpointSecurity API功能的一半(监控能力)。Google的Santa是一个用Objective-C编写的开源macOS进程允许/拒绝解决方案,展示了使用EndpointSecurity授权事件的能力:其代理现在接收并做出来自EndpointSecurity的进程事件的允许/拒绝决策。
我们看到掌握EndpointSecurity API至关重要,因为许多团队需要在其现有macOS安全应用程序中迁移到它。通过Sinter的开发,我们深入研究了EndpointSecurity,从经验中吸取了一些教训,并为我们遇到的各种挑战提供了解决方案——这样您就不必再经历这些。Sinter还演示了用Swift编程语言实现EndpointSecurity客户端,这承诺比Objective-C具有更好的内存安全性和性能,同时保持与所有其他新macOS API的兼容性。
开发Sinter:不适合胆小者
实现事件授权代理比实现只读事件订阅者困难一个数量级。我们还通过艰难的方式了解到EndpointSecurity API的某些缺点。以下是我们在Sinter开发过程中所做的一些更重要的繁重工作。
1. 实时决策而不影响系统
实现安全事件授权代理最困难的部分可能是授权决策必须实时做出。您不能永远阻塞以做出决策,EndpointSecurity对每个授权消息强制执行截止时间:如果您的客户端超过截止时间,EndpointSecurity将终止您的客户端进程以保持系统正常运行。
决策不应同步做出;Sinter使用es_copy_message从EndpointSecurity出队消息,并允许其调度程序立即向您发送下一条消息。决策应在单独的线程中做出,尽可能异步响应。有些决策处理时间比其他决策长,但通常执行签名检查所需的API无法中断。
使用Sinter时,当涉及大型程序的快速爆发执行事件导致Sinter锁定机器时,我们遇到了这个问题。我们通过实现高效的队列系统解决了这个问题,一个队列用于小型程序,另一个队列用于大型程序,因此事件永远不会在队列中等待卡住。大型程序队列在进程外工作,因此可以在必要时中止长时间运行的验证。这种新方法在我们所有的测试中都表现可靠。
2. 缓解实时安全决策中的TOCTOU风险
TOCTOU(检查时间,使用时间)竞态条件漏洞模式在做出安全决策时常见。任何执行检查的安全代理都不允许在检查和操作批准之间修改已检查的资源。
当授权macOS执行事件时,被检查的资源是可执行文件,它在执行前被映射到内存中。这是一个TOCTOU攻击场景:
恶意行为者执行Bad.app。坏的可执行文件被映射到内存中,EndpointSecurity发出执行授权事件。但随后攻击者立即替换或修改可执行文件以使其成为Good.app。EndpointSecurity客户端获取事件,验证捆绑包及其文件看起来都良好,并允许执行。
这个问题并非EndpointSecurity独有,并且始终是之前KAuth框架的风险(例如,不久前在Santa中提出了关于此TOCTOU的问题)。对于任何想要授权事件的代理来说,这仍然是一个必须解决的挑战。如前所述,Sinter尝试监控文件事件以捕获TOCTOU攻击。如果Apple在EndpointSecurity API本身内部处理此责任(作为开发者反馈建议FB8352031提交给Apple;参见OpenRadar),那将会容易得多。
3. macOS可执行文件存在于应用程序捆绑包中
执行事件发生在单个可执行文件的上下文中,但大多数macOS可执行文件存在于应用程序捆绑包内,这是在macOS Finder中显示为单个“.app”文件的目录状结构。捆绑包本身是代码签名的,代码签名验证必须在捆绑包级别完成。这意味着捕获执行事件的安全代理必须发现可执行文件是否包含应用程序捆绑包,然后验证整个捆绑包上的代码签名——这些不是由EndpointSecurity本身执行的任务。像Apple的Xcode.app这样的一些捆绑包大小高达千兆字节,实时处理验证是不可能的。执行事件必须首先被拒绝,直到验证完成。
EndpointSecurity确实提供了一个内置缓存机制,一个由所有EndpointSecurity客户端共享的单一缓存。然而,作为客户端,您不能使此缓存中的单个条目无效;您只能一次清除整个缓存。如果相关文件被更改/删除等,EndpointSecurity将自动使缓存项无效,但这是基于每个文件进行的,而不是基于每个应用程序捆绑包。目前,Sinter使用两个缓存:一个由EndpointSecurity管理,另一个包含应用程序捆绑包代码签名验证结果的自定义缓存。
理论上,恶意软件可以被添加到应用程序捆绑包中,如果捆绑包中先前批准的可执行文件没有更改,EndpointSecurity不会通过清除缓存的批准决策来做出反应。EndpointSecurity客户端必须自行监控此情况,并响应使整个缓存无效。这并不理想,我们希望Apple改进此缓存机制。在短期内,EndpointSecurity客户端可能必须在应用程序捆绑包上实现自己的完整性监控,以避免以这种方式被绕过。Sinter尝试自己的捆绑包文件完整性监控能力,以检测何时应清除此自定义缓存。
4. 将代理安装为系统扩展的优势
“系统扩展”是Apple对“扩展系统的用户模式组件”的称呼,是取代现已弃用的第三方内核扩展的总称。EndpointSecurity是此总称下的一个API;DriverKit和网络扩展是其他几个。系统扩展也是一种新型的macOS托管插件包,您可以通过它安装可执行文件。
不需要将EndpointSecurity客户端安装为系统扩展——您可以从任何类型的可执行文件(甚至是基本命令行应用程序)实现所有EndpointSecurity功能——但强烈鼓励这样做。当代理安装为系统扩展时,有额外的好处和系统强制保护。系统扩展可以选择在启动时在所有其他第三方应用程序之前加载。Apple还宣布macOS将SIP(系统完整性保护)扩展到覆盖系统扩展,这意味着它甚至防止根用户卸载您的安全代理。历史上,这只有在您开发自己的内核模式防篡改逻辑时才可能,但将代理安装为系统扩展使您免于重新发明此轮子。Sinter目前是一个后台守护进程,但现在Apple已经记录了将代理安装为系统扩展的防篡改保护好处,我们将把Sinter转换为此格式。
5. 掌握权利、签名和公证工作流程
EndpointSecurity API仅可由Apple批准的开发者(如Trail of Bits)在代码签名和公证的应用程序中使用。换句话说,该API由特殊权利门控。与大多数权利不同,此权利需要手动申请和Apple批准,之后您将获得带有EndpointSecurity权利的代码签名证书。在我们的案例中,批准时间是六个日历周,但您的经历可能不同。Apple显然对此权利很谨慎,因为行为不当或恶意的EndpointSecurity客户端可能会停止主机系统上的一切。
Apple的代码签名和公证步骤在失败时难以排除故障,因此尽早设置和自动化流程至关重要,这样您将在它们中断时立即注意到并轻松缩小中断更改的范围。对于Sinter,我们创建了自己的CMake驱动方法,自动化了Apple的公证、打包、包签名和包公证步骤的工作流程。所有这些现在都完美集成到我们的CI中,麻烦最少。
EndpointSecurity代理需要的最后一个权利与用户隐私相关。因为大多数代理将检查文件(无论是在文件事件的上下文中还是进程事件的可执行文件中),它们需要用户的许可来访问文件系统。在您的应用程序首次运行或之前,用户必须手动转到系统偏好设置中的隐私设置,并启用“完全磁盘访问”。有MDM有效负载可以自动启用权限并绕过此手动用户批准步骤。
这些是我们在编写Sinter时解决的更棘手挑战,当然还有更多杂项陷阱和经验教训(例如,确定文件是否为二进制文件、签名验证和多个EndpointSecurity客户端)。随着开发的继续,我们将更新最引人注目的细节——请保持关注。
结果
随着内核扩展的弃用,Apple正在为端点保护代理创造公平竞争环境:每个人都必须使用相同的用户模式API。这将通过改进系统稳定性和减少攻击面使每个人受益,但现有安全产品开发人员首先必须用用户模式方法替换其内核扩展。在用户模式中,他们现在可以使用任何语言工作,而不仅仅是C/C++。
因此,与其仅从C示例代码从头开始,我们希望组织帮助我们构建并依赖Swift中的开源平台,这是作为Apple Objective-C继承者的长期投资的前瞻性选择。
参与Sinter
Sinter的测试版现已可用。这是一个重要的第一步,以下是我们正在努力的一些较大项目的预览:
- 扩展阻止规则的标准,在规则语法中添加更多灵活性:问题4、17、24、25
- 基于EndpointSecurity中提供的文件事件实现强大的文件完整性监控能力
- 类似地,通过检查来自EndpointSecurity的mmap和相关事件来防止内存中代码注入攻击
- 合并NetworkExtension框架,以监控和授权网络事件,如网络流和DNS请求
我们邀请您与我们合作赞助Sinter的持续开发,或讨论将基于EndpointSecurity的能力集成到您现有代理中——只需联系我们开始。
也欢迎贡献者!在GitHub上给我们反馈,或加入Empire Hacking Slack上的#sinter频道。