信息来源:红黑联盟
这次iOS 9.2.1最新更新中苹果修复了一个代码执行漏洞,是由Zimperium zLabs的两位研究员Nikias Bassen和Joshua J. Drake在syslogd里的发现的。
在这篇文章里,我们将分享如何确定该漏洞以及漏洞背后可以让攻击者以Root权限在6.0到9.2版本的iOS设备上执行代码的技术细节。
起初,我们在检查过程中发现模糊测试器跳过了syslog的代码,但一当我们调查测试崩溃的问题时,它就对syslogd的开源部分进行了全面审查。因此我们才从中确定了syslogd代码的bug并确实成功地触发了崩溃。然后我们通知苹果公司并与其安全团队共同合作来解决问题。苹果公司迅速作出反应。 完整的安全公告是如下:
受影响的组件:
受影响的平台和版本:
1
2
|
iOS 6.0 到 9.2
OS X 10.9 到 10.11.2
|
供应商
CVE:
最新公开的源代码版本:
1
|
syslog-267 (OS X 10.10.5)
|
披露时间
1
2
3
|
错误发现并证实:2015年10月26日
通知:2015年11月25日
漏洞修补:2016年1月14日与iOS 9.2.1
|
概要
内存重新分配过程中大小计算错误导致在建立多个客户端连接的时候syslogd的堆缓冲区溢出。
影响
本地权限提升,远程代码执行(WiFi下信任的设备)或DoS攻击
介绍
Syslogd进程是通过iOS的root权限运行的。我们发现一个堆溢出漏洞会导致内存受到破坏,并且在某些情况下可能会导致任意代码的执行。超出堆缓冲区边界的数据会被文件描述符的值覆盖。而且有一些操作也会允许对写入值的控制。不过想要利用这个漏洞可不简单。
背景
在设备上运行的软件可以以USB接口的方式或者通过将设备设置在相同WiFi网络上远程连接到syslog中继服务(com.apple.syslog_relay)来访问该设备的系统日志(syslog)。不过前提是无论使用哪个媒体,设备必须被配置为信任机才可以连接到服务。也就是说,必须要先“配对”。
由于密码锁定的设备只能在解锁的情况下才与一台电脑配对,那么在不受信任的设备上利用这个漏洞就需要密码。而且设备上运行的第三方应用程序也没有办法直接与syslogd连接。
细节
这个漏洞的根本原因在于源文件syslogd.tproj / dbserver.c的add_lockdown_session函数中。在执行这个函数的时候,程序员需要给数组重新分配内存大小。下图是代码有问题的一部分:
第170行是问题的根源。传递给reallocf函数的大小算错了。这个代码应该是为每一个新的连接生成四个字节的时域文件描述符数组,但从运算符优先级规则角度看计算出错了。代码应改为:
原代码里是把整型数据字节数增加到时域数量,而上面代码行是在时域数量后先加了1,然后再乘以sizeof(int)。这样意义就不一样了。
由于新版iOS和OS X的源代码还无法获得。我们需要通过二进制文件的反汇编来证明出现在最新版本中的这个问题。而且随着运算符优先级规则的应用和代码在操作上进行的优化,反汇编让问题变得更加显而易见了。下图是iOS 9.0.2的syslogd二进制反汇编:
同样的问题也在OS X的syslogd二进制文件里和iOS 9.1里发现,iOS 9.1甚至不需要多次连接到com.apple.syslog_relay服务来检查二进制文件就可以确认了。只要堆内重要的数据被破坏了,syslogd就会崩溃。下图是OS X 10.11.1的syslogd二进制反汇编:
请注意一下编译器在这两个版本中如何将“1 *sizeof(int)”简化为“sizeof(int)”,这使漏洞更加明显。
当一个新的客户端连接时,函数被第一次调用,执行时这个函数给初始缓冲器分配了第一个用于连接的文件描述符。因为sizeof(int)的值一直是定值4,第一次 global.lockdown_session_count值为0,计算出的大小是正确的4个字节。但是当第二个新的客户端连接时,global.lockdown_session_count值变成了1,缓冲区大小就会被错误地计算为5个字节。以此类推,第三个连接的大小是6个字节。
如果开发商把新的连接的文件描述符存储在global.lockdown_session_fds数组里,随着字节数越来越大就会发生实际的堆溢出。下图是syslogd中的syslogd.tproj / dbserver.c源文件代码摘录:
第179行的代码,同时也是上面反汇编的最后一行,表示了代码在没有足够内存分配的缓冲器里溢出写入4个字节。由于内存分配通常会四舍五入为8个字节,所以建立两个连接不会破坏内存。而创建第三个连接后,文件描述符就会写出界然后可能导致堆内存的损坏。
当然,有时内存损坏发生可能需要不止三个连接才能使syslogd崩溃。但是,这取决于堆块后的数据在利用后是否或如何被破坏。在我们的测试中syslogd中止执行是因为remove_lockdown_session函数中堆损坏。