时时勤拂拭,勿使惹尘埃

TOC

Categories

漏洞分析(二)iOS kernel.backtrace信息泄漏漏洞


0x0 背景

Apple在iOS 10中引入内核的一些代码,这段代码为kernel.backtrace sysctl实现了一个新的sysctl处理程序,此sysctl用于检索当前线程的用户级别回溯。

0x1 kernel.backtrace

kernel.backtrace是一个相对较新的iOS内核功能,它可以让当前进程获取自己的用户级别回溯。虽然用户级别的回溯的逻辑隐藏在内核源代码的Mach部分中,但sysctl处理程序本身在文件/bsd/kern/kern_backtrace.c中实现。
处理程序的代码如下所示,首先验证传入的参数并限制可以检索的回溯的深度(第58-66行)。然后它将分配一个堆缓冲区来存储第67行中用户选择的深度的回溯,并使用外部辅助函数用用户级别回溯填充缓冲区(第72行)。然后将实际检索的回溯复制到用户区(第77行)并释放堆缓冲区(第84行):
48 static int
49 backtrace_sysctl SYSCTL_HANDLER_ARGS
50 {
51 #pragma unused(oidp, arg2)
52         uintptr_t *bt;
53         uint32_t bt_len, bt_filled;
54         uintptr_t type = (uintptr_t)arg1;
55         bool user_64;
56         int err = 0;
57
58         if (type != BACKTRACE_USER) {
59                 return EINVAL;
60         }
61
62         if (req->oldptr == USER_ADDR_NULL || req->oldlen == 0) {
63                 return EFAULT;
64         }
65
66         bt_len = req->oldlen > MAX_BACKTRACE ? MAX_BACKTRACE : req->oldlen;
67         bt = kalloc(sizeof(uintptr_t) * bt_len);
68         if (!bt) {
69                 return ENOBUFS;
70         }
71
72         err = backtrace_user(bt, bt_len, &bt_filled, &user_64);
73         if (err) {
74                 goto out;
75         }
76
77         err = copyout(bt, req->oldptr, bt_filled * sizeof(uint64_t));
78         if (err) {
79                 goto out;
80         }
81         req->oldidx = bt_filled;
82
83 out:
84         kfree(bt, sizeof(uintptr_t) * bt_len);
85         return err;
86 }

0x2 漏洞

这里是将回溯复制到用户区的代码,复制到用户区的字节数是bt_filled * sizeof(uint64_t),这是将填充的回溯条目的数量乘以8个字节:
77         err = copyout(bt, req->oldptr, bt_filled * sizeof(uint64_t));
78         if (err) {
79                 goto out;
80         }
那么再看堆缓冲区大小,堆缓冲区的大小由sizeof(uintptr_t)\ * bt_len确定,这是最大检索的回溯条目的数量乘以指针的大小。
67         bt = kalloc(sizeof(uintptr_t) * bt_len);
68         if (!bt) {
69                 return ENOBUFS;
70         }
在较新的64位设备上,指针的大小为8。而在较旧的iOS设备(iPhone 5c及更低版本)和较旧的Apple Watch(系列3)内部均为32位设备,因此只有4个字节的指针。这意味着在这些旧设备上,对copyout()的调用将允许从堆中复制两倍大小的字节,而不是缓冲区大小,这是一个经典的堆缓冲区溢出漏洞。

0x3 影响

虽然这是一个内核信息泄漏的0day漏洞,但依然有许多缓解因素:
  • 该漏洞仅影响32位iOS设备 - 苹果目前在32位版本中仍然支持的唯一设备是Apple Watch Series 3及更低版本
  • 该漏洞只能在应用程序沙箱之外触发 - 因此它只能用作漏洞链的一部分,而不能直接从应用程序中利用

0x4 iOS 12 copyin/copyout缓解

从iOS 12开始Apple向内核添加了缓解措施,如果内核的堆缓冲区具有继续操作所需的大小,则每次执行copyin()或copyout()时都会进行检查。如果攻击者试图在内核区域堆元素的边界上读取或写入,则内核将会发生混乱。
但是,Apple没有将此保护添加到32位内核,估计Apple已经放弃对32位设备的支持了。

0x5 漏洞赏金

Apple安全奖金计划眼中的这个安全漏洞的价格为0。这有三个原因:
  1. Apple只支付影响其最新设备的漏洞。
  2. Apple不支付影响MacOS/tvOS/WatchOS的漏洞,只为iOS设备付费。
  3. 尽管许多缓解措施依赖于内核信息,但Apple暂未支付信息泄露漏洞。

0x6 结论

使用两种不同的数据类型进行分配和复制是相当难以理解的,而且也非常明显。所以很难解释为什么每次添加新代码时,对新内核代码的安全审查都没有发现这一点。