上次介绍了PEGASUS IOS内核漏洞之后,又发现了几个新的问题:
1. Apple公司早在今年5月份就为了UAF漏洞修补过一次OSUnserializeBinary(),但是这个补丁是用来修补CVE-2016-1828漏洞的。 2. 我们描述的UAF触发方法在iOS9和 OSX 10.11之前版本的代码中并不存在。
获知这两个细节后,我们决定对我们的分析做一个小的更新。
退后一步看看iOS 9和OS X 10.11之前版本的OSUnserializeBinary()代码。
从上述代码中可以看出,iOS 9.0 和OS 10.11之前版本的字典键必须是OSSymbol对象。并且OSString代码路径还没有添加。这意味着我们分析中提到的UAF方法在老版本的代码中无法实现。此外,仔细查看这段代码并与我们的分析比较后,可以发现老版本中调用setObject()时只有2个参数,而不是3个。这是因为上述代码在CVE-2016-1828的补丁之前就存在。
现在仔细地看一下上述代码,并找出触发UAF的代码路径。
在这段代码中,触发UAF的第一种方法就是利用CVE-2016-1828:
1. 398行代码会设置字典键k1为对象o1 2. 这会使o1和k1的引用计数器加1(变成2) 3. 399行处对象o1被释放,它的引用计数器减小到1 4. 400行处对象k1(sym)被释放,它的引用计数器减小到1 5. 此时由于字典保持了对两个对象的引用,没有任何问题 6. 当下一个对象o2被逆序列化并插入字典后,重用相同的键k1时,398行的setObject()方法会在字典中用o2替换o1。在替换的时候o1的引用计数器从0减少到1 7. 此时o1被从内存中释放。如果此时逆序列器重新尝试产生一个指向这个对象的引用,就会造成UAF。
在这段代码中,触发UAF的第二种方法可能就是PEGASUS(CVE-2016-4656)真正用到的方法:
1. 如果我们正在插入的对象o是对dict本身的一个引用,389行代码则不会调用setObject() 2. 如果setObject()没有被执行,o和sym的引用计数器永远不会增加 3. 399行代码将会减小o的引用计数器到大于或等于1的值(因为它是对dict的引用) 4. 400行代码最多将会减小sym的引用计数器到0(如果symbol是奇怪的字符串) 5. 此时,OSSymbol对象sym被销毁 6. 此后创建任何指向对象sym的引用都是UAF
可以看到,在iOS 9 和 OS X 10.11之前版本的代码中早已存在两个单独但又相关的UAF触发代码路径。这意味着加上Part1中描述的,在仅仅20行代码中就已经有3个UAF触发代码路径。
Apple公司5月份发布的CVE-2016-1828漏洞补丁中对这段代码唯一的改变是增加了调用setObject()时的第3个参数ture
if (o != dict) ok = dict->setObject(sym, o, true);
当代码中出现错误时,就会重写已经设定的字典键,因此Brandon Azad提到的UAF情形便不能再被触发。不幸的是Apple公司认为这样已经足够好了,所以他们没有及时对OSUnserializeBinary()进行代码安全检查。不然的话,一个熟练的代码审计员一定会意识到代码中有大量对release()的直接调用。这可以被用来从objsArray中释放对象,从而触发UAF。这种仅仅给setObject添加第三个参数ture不是一个完美的修复方法。
Apple没有对他们的安全补丁进行安全审计,又或者说相关责任人没有查看函数中的其它release()调用。因此这20行代码中的其它两种UAF触发方法仍然存在,并能控制内核执行渗透系统的操作。PEGASUS被发现后,Apple花了很多精力去修复这20行代码。他们废除CVE-2016-1828的补丁,并重写函数使其不再调用任何release()(除了对错误和临时变量的清理),从而在函数的最后、返回结果之前,所有的对象从objsArray中得到释放。这样反序列化时引用计数器永远不会下降到0。
在过去的两周内,许多安全专家都称赞苹果对PEGASUS的快速反应。这一赞扬是因为有关方面不对独立的第三方研究人员开放样本,并且没有透露该内核漏洞的详细信息给公众。没有这些信息,公众只是简单地认为PEGASUS使用的是全新的内核漏洞来控制iOS设备。然而逆向PEGASUS使用的内核漏洞后,发现了一个完全不同的画面:
仅仅因为2016年5月Apple修复CVE-2016-1828漏洞时没有对问题代码进行安全审计,导致CVE-2016-4656内核漏洞仍然存在。仅仅20行代码中就存在3条代码路径可以触发UAF。尽管这些release()方法离得很近,但是Apple只修复了其中一条路径。此外,从对PEGASUS补丁的分析可以看出,只需要稍微修改一下代码就能够同时修复这3个问题。这可以被认为是一个非常严重的疏忽:修补工作并有在BrandonAzad提出UAF后就立刻展开。如果Apple公司用不同于之前的方式修补CVE-2016-4656漏洞,那么CVE-2016-1828漏洞将不会被滥用。
还没有评论,来说两句吧...