Black Hat USA 2026:MIE与macOS内核利用
原创
Max Luo
Max Luo
白帽子罗棋琛
2026年10月13日 08:01
中国香港
在小说阅读器读本章
去阅读
在公众号小说中沉浸阅读
苹果 MIE 如何重塑 macOS 内核利用
Black Hat USA 2026 议题笔记:Apple macOS Kernel Exploitation with MIE: Building on the Ashes of 100 Vulnerabilities
Apple 在新一代芯片上把内存安全从单项缓解措施推进为一套组合防线:Enhanced Memory Tagging Extension(EMTE)负责检查内存标签,类型化分配器限制不同对象之间的复用,指针认证、只读对象与 Secure Page Table Monitor(SPTM)继续压缩内核读写之后的利用空间。
Dion Blazakis、Bruce Dang 与 Josh Maine 的公开课件讨论了这套防线下的一条 macOS 内核利用链。真正值得安全工程师关注的,不是“某个技巧又绕过了所有防护”,而是漏洞价值发生了迁移:普通越界和释放后使用越来越难转化为稳定利用,类型混淆、未初始化数据泄露、对象内部错误和语义合法但上下文错误的指针,反而进入优先队列。
图 1:研究对象是启用 MIE 的新一代 macOS 平台,课件把工作拆为信息泄露、受限破坏、目标定位、内核读写、跨进程读写和提权六个阶段
本文依据公开课件与 Apple 的技术说明整理。为避免把研究材料改写成可直接复现的本地提权指南,文中不提供内核对象偏移、堆布局参数、目标指针、套接字调用序列以及 launchd 内存补丁载荷;代码全部用于防守侧审计和回归测试。
1、MIE 不是一项开关,而是一组相互补位的约束
把 MIE 理解成“更强的 MTE”会低估它的设计。Apple 对 MIE 的公开描述包括 EMTE、Secure Typed Allocators、标签保密以及与指针认证、内核完整性保护的协同。它试图同时回答四个问题:一次访问是否命中仍然存活的内存、访问者持有的标签是否匹配、分配槽是否允许容纳这种对象、攻击者是否能够预测或伪造验证所需的元数据。
图 2:旧利用链依赖可重复的堆布局和跨类型复用;MIE 把这类操作从“稳定原语”降为高概率崩溃
这些约束解决的是不同层次的问题:
- EMTE 主要发现越界访问与生命周期错误;
- 类型化分配减少攻击者用 A 类型释放槽承载 B 类型对象的机会;
- PAC 约束控制流指针及部分敏感数据指针;
- SPTM 把部分关键内核状态放进普通内核写能力也难以修改的区域;
- 只读对象、所有者检查与反向指针继续限制已有内核读写原语。
因此,判断一条链是否“绕过 MIE”不能只看最后是否获得 root。课件中的路径没有关闭整套 MIE,而是在每一层寻找仍被系统视为语义合法的操作,再把多个受限原语串起来。
2、利用链被拆成 Opening、Midgame 和 Endgame
课件用三个阶段描述新的内核利用工程:Opening 从初始漏洞获得泄露或受限破坏;Midgame 在标签和指针认证约束下找到可操作对象;Endgame 面对 SPTM 和只读内核对象,把已有能力转成最终安全影响。
图 3:防护没有让所有漏洞消失,而是让“漏洞到原语”和“原语到影响”都需要额外转换
这个模型对漏洞管理很有用。传统评分常把“内核越界写”直接映射为高危,却忽略写入宽度、可控位数、对象类型、标签域和可达目标。启用 MIE 后,至少应记录以下属性:
yaml
kernel_bug_triage:disclosure:bytes:bounded_or_unboundedcrosses_allocation:true_or_falsereveals_pointer:true_or_falsepreserves_tag_confidentiality:true_or_falsecorruption:width_bits:0direction:relative_or_absolutetarget_type_known:true_or_falseattacker_controls_value:true_or_falsesemantic_gap:discriminator_checked:true_or_falsepointer_is_authenticated:true_or_falseobject_owner_checked:true_or_falseendgame:writable_under_sptm:true_or_falsecrosses_process_boundary:true_or_false
这份记录比“有 OOB/UAF”更接近真实可利用性,也方便修复团队判断哪一个看似普通的信息泄露会补全另一条破坏链。
3、第一个入口:WebDAV 返回长度与实际写入长度不一致
课件中的第一个问题位于 WebDAV 数据路径。调用方申请 N 字节缓冲区,远端只返回 M 字节,后续逻辑却可能按 N 字节处理结果。未被本次响应覆盖的尾部来自此前使用过的页面,于是形成陈旧堆数据泄露。
图 4:研究使用一个非破坏型信息泄露和一个类型混淆,二者分别承担地址发现与受限破坏
图 5:协议声明长度、缓冲区容量和实际接收长度是三个不同变量,任何两者混用都会留下尾部数据
这类缺陷容易被忽略,因为它没有跨边界写,也不一定触发标签错误。MIE 能阻止很多非法访问,却无法知道“合法映射页中的旧内容不应返回给当前请求”。课件还指出,大块分配与小对象路径在清零和类型隔离上存在差异,使问题落在硬件标签检查的语义盲区。
图 6:访问地址和标签均合法时,硬件无法替软件判断其中的数据是否已完成初始化
网络文件系统、压缩、RPC 和设备 I/O 代码应采用“先清零、再写入、只返回实际长度”的固定模式:
c
typedefstruct {uint8_t *data; size_t capacity; size_t initialized; } receive_buffer_t; intreceive_exactly_bounded(receive_buffer_t *dst, transport_t *io) { if (dst == NULL || dst->data == NULL || dst->capacity > MAX_FRAME_SIZE) { return EINVAL; } explicit_bzero(dst->data, dst->capacity); ssize_t n = transport_read(io, dst->data, dst->capacity); if (n < 0 || (size_t)n > dst->capacity) { dst->initialized = 0; return EIO; } dst->initialized = (size_t)n; return0; } // 上层只能消费 initialized,不得消费 capacity。span_tresponse_view(constreceive_buffer_t *buf) { return (span_t){ .ptr = buf->data, .len = buf->initialized }; }
代码评审时应追踪“初始化长度”,而不只是缓冲区大小。只要 API 仍返回裸指针和容量,调用者就很容易重新引入同类问题。
4、第二个入口:SMBFS 把合法字段解释成了错误类型
第二个问题来自 SMB2 响应中的 create context。解析器依据上下文类型解释同一段载荷;若类型判定、长度验证和联合体访问不同步,一个由远端控制的数值字段便可能在后续路径中被当成另一种对象或句柄使用。
图 7:输入本身仍符合某种消息布局,错误发生在状态机选择了不匹配的解释路径
MIE 对普通“数据覆盖指针”很强,但这里的困难在于系统可能正在使用一个格式上合法的值。标签可以回答地址属于哪块内存,不能回答协议解析器为什么把 A 消息当成 B 消息。类似问题也常见于 TLV、Netlink、IOKit selector、XPC 字典和带判别字段的 C 联合体。
防守侧应把判别字段、载荷长度和解码结果绑定为一个不可分割的类型状态:
c
typedefenum { CTX_DURABLE_HANDLE, CTX_LEASE, CTX_QUERY_ON_DISK_ID } context_kind_t; typedefstruct {context_kind_t kind; union {durable_handle_t durable; lease_t lease; disk_id_t disk_id; } value; } parsed_context_t; boolparse_context(byte_span_t in, parsed_context_t *out) { context_header_t h; if (!decode_header(in, &h) || h.payload_len > in.len - sizeof(h)) { returnfalse; } switch (h.name) { case NAME_DURABLE: out->kind = CTX_DURABLE_HANDLE; return h.payload_len == sizeof(durable_wire_t) && decode_durable(payload(in, h), &out->value.durable); case NAME_LEASE: out->kind = CTX_LEASE; return h.payload_len == sizeof(lease_wire_t) && decode_lease(payload(in, h), &out->value.lease); case NAME_DISK_ID: out->kind = CTX_QUERY_ON_DISK_ID; return h.payload_len == sizeof(disk_id_wire_t) && decode_disk_id(payload(in, h), &out->value.disk_id); default: returnfalse; } }
不要让业务层重新读取原始判别字段,也不要暴露可被任意成员访问的通用联合体。解析函数应一次性完成合法性证明,后续函数只接收具体类型。
5、受限漏洞需要先被“驯化”,才能拼成稳定原语
课件没有把两个入口直接等同于内核读写。泄露可能只能看到某类旧页面,破坏也可能只有有限位宽和有限方向。研究的核心工作是把不稳定、带副作用的“奇怪机器”变成可重复调用的接口。
图 8:现代利用的主要成本从触发 bug 转向约束输入、识别状态并管理每次调用的副作用
图 9:对象必须同时满足可定位、仍存活、类型域允许和操作后可恢复,目标选择因此比旧式堆喷更苛刻
课件随后使用内核网络对象完成转换,但具体对象偏移与调用顺序具有直接武器化价值,本文不复述。工程上更重要的观察是:攻击者不一定需要构造伪造指针;如果能让一个真实对象错误引用另一个真实对象,标签和 PAC 都可能继续认为该指针有效。
这说明 MIE 时代的审计重点应从“哪里能写指针”扩展到:
- 哪些引用可在不同对象实例间错配;
- 所有者、生命周期和命名空间是否随引用一起验证;
- 回调或过滤器是否只验证指针真实性,却不验证业务身份;
- 一个有限宽度写能否修改长度、索引、状态枚举或对象间关系;
- 错误路径是否把仍存活对象暴露给另一个协议状态机。
6、获得内核读写,也不再等于可以随意改凭据
过去的 macOS 内核利用常把最终目标放在进程凭据、任务结构或代码签名状态。课件列出的新防护显示,这些目标已分别受到只读对象、所有者检查、SPTM 保护区和已认证指针约束。
图 10:内核读写仍然严重,但 SPTM 让若干经典提权字段不再是普通写原语可以直接修改的目标
因此研究转向 data-only 路径:先从内核能力建立对另一个用户态进程地址空间的受控访问,再寻找该高权限进程中会影响后续决策的可写状态。其价值在于不必破坏 SPTM 保护的内核对象,也不必注入新的内核控制流。
这里的防守启示不止适用于 Apple。只读凭据并不能阻断所有提权:只要高权限服务把“将执行什么”“为谁执行”“配置来自哪里”保存在普通可写内存,跨进程写仍可能改变授权结果。守护进程应尽量让关键决策具备以下属性:
text
请求内容 --解析--> 不可变命令描述 --授权服务签名--> 一次性执行票据 | +-- 绑定调用者身份、代码签名、参数摘要和过期时间 执行器:验证票据 -> 从只读/重新映射的数据构造 argv -> 执行 -> 销毁票据
如果执行器只相信自身堆中的可变字符串,内核级攻击者不需要修改凭据结构,也能改变下一次高权限动作的含义。
7、不要把“成功提权”误读为防护失败
课件的第一条结论是缓解措施很强。表面看似矛盾:既然最后仍得到 root,为什么还说防护有效?因为防护的目标不是数学上消灭所有缺陷,而是提高链条长度、降低通用性、增加平台相关成本,并让大量普通 bug 无法达到安全影响。
图 11:一条精心挑选的漏洞链可以跨越多层防护,不代表每个内存破坏都具备同样能力
更准确的评估方法是观察攻击预算发生了什么变化:
- 需要一个额外的信息泄露来补足地址与对象状态;
- 需要类型语义成立,而不是任意跨类型堆复用;
- 需要两个或更多长期存活、关系可控的真实对象;
- 需要在内核读写之后继续寻找 SPTM 之外的 data-only 目标;
- 链条与操作系统版本、对象生命周期和硬件实现强绑定。
这些成本不会让高价值目标自动安全,但会消灭大批低成本、可规模复制的利用。企业风险沟通应避免“又被绕过,所以防护无用”的二元叙事。
8、漏洞研究的优先级正在迁移
课件把类型混淆、非破坏型信息泄露和对象内部内存安全列为价值上升的方向。
图 12:当跨对象破坏被标签和类型化分配压缩后,硬件无法理解的业务语义错误更值得优先审计
对内核与驱动团队,可把审计清单改成下面这组问题:
- 返回给低权限调用者的每个字节是否都在当前请求中初始化?
- 长度字段表达的是容量、网络声明值、已接收值还是已验证值?
- 联合体成员是否由同一个判别字段选择并在单点验证?
- 句柄是否同时绑定类型、所有者、命名空间和代际编号?
- 一个合法指针是否可能被移植到不属于它的对象实例?
- 对象内索引、计数和状态位损坏后,是否会越过字段级边界?
- 高权限服务的关键决策是否只存在于普通可写堆内存?
静态扫描可以先筛出“容量与实际长度混用”的候选位置,随后必须人工做数据流确认:
yaml
rules:-id:copy-capacity-after-short-readlanguages: [c] severity:WARNINGmessage:短读后仍按缓冲区容量复制,检查是否泄露未初始化尾部patterns:-pattern-inside:| $N = $READ($FD, $BUF, $CAP, ...); ... -pattern-either:-pattern:$COPY(...,$BUF,$CAP)-pattern:$SEND(...,$BUF,$CAP,...)-pattern-not-inside:| $N = $READ($FD, $BUF, $CAP, ...); ... if ($N != $CAP) { ... } ...
规则故意只做候选发现,不假设所有短读都危险。真正的修复应落在 API 设计和状态类型上,而不是长期维护例外列表。
9、把同类问题放进模糊测试与终端遥测
针对 WebDAV/SMBFS 这类协议状态机,单纯随机字节变异很难到达深层分支。更有效的是生成合法外壳,再独立变异声明长度、实际载荷、上下文顺序、重复字段和分片边界。
cpp
extern\"C\"intLLVMFuzzerTestOneInput(constuint8_t *data, size_t size){ fuzz_cursor_t f = fuzz_cursor(data, size); smb2_message_t msg = make_minimal_valid_response(); msg.context_name = choose_known_or_unknown_name(&f); msg.declared_length = take_u16(&f); msg.actual_payload = take_remaining_bytes(&f, MAX_CONTEXT_SIZE); msg.flags = take_u32(&f); parser_result_t r = parse_smb2_context(serialize(msg)); if (r.ok) { assert(r.bytes_consumed <= serialized_size(msg)); assert(result_kind_matches_context_name(r, msg.context_name)); assert(no_uninitialized_bytes_exposed(r)); } return0; }
建议至少建立四类断言:解析器永不读取实际载荷之外的数据;成功结果的类型与判别字段一致;失败路径不返回半初始化对象;同一输入在启用地址、未初始化和标签检测后语义一致。
企业终端侧无法靠 EDR 判断某个崩溃就是这条公开研究链,但可以建立版本与异常基线。osquery 盘点示例:
sql
SELECT host_identifier, version, build, major, minor, patch FROM os_version WHERE platform ='darwin';
同时收集受管设备的 panic 摘要、异常重启、网络文件系统相关进程崩溃以及 MTE/tag fault 类错误。日志字段会随系统版本和采集产品变化,不应在规则里硬编码未经验证的 subsystem;先把原始样本送入实验环境,再建立字段映射与阈值:
yaml
macos_kernel_memory_watch:inventory:group_by: [hardware_model, os_build] alert_if_build_not_approved:truecrash_signals:-kernel_panic-memory_tag_fault-repeated_network_filesystem_crashcorrelation_window:30mescalate_when:-tag_fault_and_network_fs_activity-same_signature_on_multiple_hosts-panic_after_untrusted_share_mountretain:-panic_excerpt-os_build-hardware_model-mounted_share_metadata-preceding_process_tree
这不是一个声称能检测特定利用的规则,而是一条可验证的取证起点。生产环境最重要的动作仍是及时升级 Apple 已修复版本,并减少非必要的 WebDAV/SMB 暴露面。
10、AI 的作用是缩短筛选时间,不是替代利用工程
课件最后提出“AI helps Apple”:当大部分普通内存错误被 MIE 降为崩溃,AI 可以帮助研究人员从大量候选中优先找出仍有组合价值的缺陷。这里更合理的理解是辅助归类、代码导航、差异分析和假设生成,而不是模型自动完成了一条内核利用。
图 13:让大多数 bug 无法利用之后,自动化筛选更容易把有限研究资源集中在剩余高价值语义缺陷上
一条可靠链仍需要理解分配器、标签域、协议状态机、对象生命周期、PAC、SPTM 和高权限用户态服务。AI 可以把“翻完一百个候选”压缩成“深入验证几个候选”,却不能替代真机实验、崩溃归因、可重复性验证与负责任披露。
对防守方而言,MIE 带来的最重要变化也不是可以停止找内存漏洞,而是需要换一种找法:继续修复传统越界和 UAF,同时把更多资源投向未初始化数据、类型状态、对象内部边界和 data-only 决策状态。硬件负责判断一次访问是否合法,软件仍必须证明这次访问在当前协议、当前对象和当前身份下是正确的。
资料来源
- Black Hat 官方 Session 页面
- Apple Security Research:Memory Integrity Enforcement
- Apple Security Research:Towards the next generation of XNU memory safety
- Apple Platform Security:Operating system integrity
原始会议材料(仓库内)
- 演讲课件 PDF
开源资料与原始议题 PDF
本文对应的 Markdown 原稿、Black Hat 原始议题 PDF 与配图已整理到 GitHub,可按文章编号查找和下载。
https://github.com/cybermaxluo/black-hat-usa-2026-talks
也可以点击文末“阅读原文”进入仓库。欢迎 Star、提交 Issue 或参与勘误。
版权声明
本站仅做备份收录,仅供研究与教学参考之用。
读者将信息用于其他用途的,全部法律及连带责任由读者自行承担,本站不承担任何责任。










评论