macOS High Sierra 10.13 中的 ctl_ctloutput 函数忽略了 sooptcopyin 调用的返回值,这打开了一个竞态窗口,可将未初始化的内核堆数据泄漏到用户空间。ctl_ctloutput-leak 是一个概念验证漏洞利用,试图触发此信息泄漏。利用需要 root 权限。
已确认该漏洞利用在 macOS High Sierra 10.13.1 Beta 17B25c 和 iOS 10.1.1 14B100(通过 mach_portal)上有效。
以下是 macOS High Sierra 10.13 上 ctl_ctloutput 的相关部分:
if (sopt->sopt_valsize && sopt->sopt_val) {
MALLOC(data, void *, sopt->sopt_valsize, M_TEMP, // (a) data 被分配
M_WAITOK); // 未使用 M_ZERO。
if (data == NULL)
return (ENOMEM);
/*
* 4108337 - 复制用户数据,以备
* 内核控制需要
*/
error = sooptcopyin(sopt, data, // (b) 调用 sooptcopyin()
sopt->sopt_valsize, sopt->sopt_valsize); // 填充缓冲区;
} // 返回值被忽略。
len = sopt->sopt_valsize; //
socket_unlock(so, 0);
error = (*kctl->getopt)(kctl->kctlref, kcb->unit, // (c) 调用 getsockopt()
kcb->userdata, sopt->sopt_name, // 的实现来处理
data, &len); // 缓冲区。
if (data != NULL && len > sopt->sopt_valsize) //
panic_plain("ctl_ctloutput: ctl %s returned "
"len (%lu) > sopt_valsize (%lu)\n",
kcb->kctl->name, len,
sopt->sopt_valsize);
socket_lock(so, 0);
if (error == 0) {
if (data != NULL)
error = sooptcopyout(sopt, data, len); // (d) 如果 (c) 成功,
else // 则将数据缓冲区
sopt->sopt_valsize = len; // 复制回用户空间。
} //
这段代码执行以下操作:
getsockopt 的 data 参数分配一个内核堆缓冲区,但未指定 M_ZERO 标志来清零所分配的字节。sooptcopyin 从用户空间复制 getsockopt 数据,填充刚分配的 data 缓冲区。此 copyin 本应完全覆盖已分配的数据,因此不需要 M_ZERO 标志。然而,sooptcopyin 的返回值未被检查,这意味着 copyin 可能失败,导致缓冲区中留下未初始化的数据。例如,如果程序向 getsockopt 传递了一个未映射的地址,copyin 可能失败。getsockopt 实现。该实现应处理输入缓冲区,可能修改并缩短它,并返回结果代码。然而,该实现可以假定提供的缓冲区已被初始化(因为理论上它来自用户空间),因此一些实现根本不修改缓冲区。例如,NECP 函数 necp_ctl_getopt 仅返回 0,完全不处理数据缓冲区。getsockopt 实现未返回错误,ctl_ctloutput 调用 将数据缓冲区复制回用户空间。因此,通过向 getsockopt 指定一个未映射的数据地址,我们可以分配一个受控大小的堆缓冲区,防止该缓冲区的内容被初始化,然后触发对 sooptcopyout 的调用,该调用试图将缓冲区写回未映射的地址。我们只需在 sooptcopyin 和 sooptcopyout 调用之间重新映射该地址,即可使 copyout 成功。如果能够做到,我们就能将未初始化的内核堆数据泄漏到用户空间。
事实证明,这是一个相当容易获胜的竞态。在我的 2015 款 Macbook Pro 上测试时,赢得竞态的平均尝试次数从未超过 600 次,中位数从未超过 5 次。在 iPhone 7 上的 iOS 10.1.1 中,竞态更容易获胜,通常不超过 2 次尝试。(此测试在关闭 DEBUG 的情况下进行,因为 printf 会显著拖慢漏洞利用速度。)
要构建,请运行 make。查看 Makefile 顶部了解各种构建选项。
通过在命令行中指定目标泄漏大小来运行漏洞利用:
$ sudo ./ctl_ctloutput-leak 128
000000: ef be ad de ef be ad de 00 00 00 00 00 00 00 00
000010: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
000020: 00 00 00 00 00 00 00 00 01 00 00 00 40 80 00 00
000030: de 28 45 00 04 00 00 00 a0 ff 4a 26 80 ff ff ff
000040: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
000050: 08 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
000060: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
000070: 00 00 00 00 00 00 00 00 ef be ad de ef be ad de
我于 2017 年 10 月 7 日向 Apple 报告了此问题。它被分配了 CVE-2017-13868。Apple 在 macOS 10.13.2 和 iOS 11.2 中修复了这些问题。
ctl_ctloutput-leak 代码已发布到公共领域。作为礼节,如果您引用或使用任何这些代码,我希望您注明出处。
sooptcopyout