Xu Hao、Chen Xiaobo:自己手动发现iOS内核缺陷

http://www.csdn.net/article/a/2012-12-14/2812881

12月13-14日,SyScan首次登陆北京,携手奇虎360合力举办SyScan360国际前瞻信息安全会议。此次活动为期两天,将涵盖Windows、Android、iOS、浏览器、软件漏洞挖掘攻击等各个方向互联网安全话题,为国内外从事互联网安全领域的研究同行们提供良好的交流与合作的平台。同时本次大会还将汇集国际安全领域最新的研究成果,共享最新的安全技术,众多国际重量级的演讲嘉宾将汇聚一堂,分享最前端的信息安全的理念。

独立安全研究员Xu Hao和McAfee研究科学家Chen Xiaobo在安全及iOS领域都有丰富的经验,他们在本议题中讨论了他们如何编写针对iOS内核的Fuzz工具以及如何分析真实的iOS内核漏洞。Xu Hao表示,其实iOS漏洞挖掘并没有那么难,致使大家没有进行深入挖掘。

独立安全研究员 Xu Hao和McAfee研究科学家 Chen Xiaobo

议题内容主要包括iOS内核的基础知识、如何编写一个基于Hook技术的Fuzz工具、主动Fuzz所有的IOKit驱动的方法等。

以下是演讲速记:

徐浩:大家好,今天我和Chen Xiaobo跟大家说一下,可能在座搞iOS安全的不是很多,其实iOS如果深入去做一些安全的检测或者说漏洞的挖掘,会发现它其实没有你想想那么困难。我们希望通过今天这个议题可以让大家我们自己可以发行一些这样的漏洞,写一些漏洞的利用。因为时间有限,我们稍微快一点,第一个我们讲一些iOS内的基本学习点。我们学习iOS有一个参照物,我们昨天讨论的是,我们可以通过OSX来学习iOS,因为OSX是部分开源,并且是一个开放的操作系统。我们这边大概看一下OSX内核基本概念,最早是从FreeBSD内核基础演变过来,苹果命名为XNU。

总的来说,有三个部分要说。第一个就是MACH,它是一个低程序的抽象,跟硬件结合比较紧密的部分。BSD是高层的抽象,IOKIT是内核扩展基础框架,为了方便支持第三方内核扩展。

BSD如果大家学习过Linux会比较熟悉,它是一套最基本的、可以和用户及内核态进行交互的接口。在内核中数组是存在Syent数组里面,如果大家感兴趣可以在这个文件找到每一个函数。

重点了解一下Iokit,苹果开发这套Iokit框架,是为了更好支持内核的扩展,所以我们看到如果是BSD那一套,你的内存代码只能用C面向过程来写,导致它的一个扩展性、可读都不是很好。为了解决这个问题,苹果用了C++子级,来实现面向对象的特性,从而使开发者更好做到抽象和代码管理。

从这边看一个基本结构是这样的,所有的类有一个Root,这里称之为OS object,OS object主要工作是承载了new操作符来分配内存,通过Init方法来初始化对象自身。这个OSMetaClass是运行时对象类型检查,可以通过一些函数,比如OSMetaClass,通过这个函数可以进行安全转换。

IOKit下面还有一个重要的类,大家看到IOSErvice,苹果对它使出的抽象定义的接口。在右边图这边我们可以看到,大概来说,是这样一个过程:通过Init初始化以后,可以attach上去,这个硬件跟我的契合度,我给出一个打分。比如说一个硬件有三个驱动,系统挑一个打分最高,也就是契合度最紧密的硬件。

Attach以后就可以,硬件控制必须要有跟底层有一个通信,就有Open Close接口帮助他做一些事情。这里有一些命令是桌面系统自带,如果在手机上大家可以去下载这个工具,叫IOreg,已经注册在系统内,打开它就可以通信了。

我们了解了一下,如果自己写一个驱动扩展会需要什么步骤,其实像这样的过程可以帮助我们更深入理解它的一个框架。第一步就是说我们必须要实现自己的Server,我的驱动必须向上层提供服务,很简单我们从IOService做继承,我可以通过程序比如说实施一些参数或者获取一些信息。接下来最后一步有了这两个部分的代码之后,你还要去修改Plist,让它知道是哪一个驱动。

这个是Kernelcache,iOS系统内核为了提高加载的速度,所以把所有的内核,包括所有的扩展全部存储在一个Coche文件中,这样加载的时候,不需要把所有的文件进行解析,这样可以加快启动的速度。

Cernelcache在硬件上软件的位置在这边列出,但是这个文件是加密我们会提到,对于老的设备也就是A4芯片的话,因为有Bootroom漏洞,通过漏洞可以经过硬件的指令进行解密,所以对于老的A4芯片,就是iPHONE和iPAD1可以从网上查到它的KeY,通过这个工具,可以解密固件中的Cache,从而可以把kernelcache进行静态逆向分析。对于新的CPU来说,因为Root被修改,没有办法调用硬件进行解密,没有IV和key,目前没有办法做这个工作。如果大家想看一些A5的内核的话,这里我尝试了一些方法,最简单方法就是我们可以当运行中内核的内存,这边其实dump的话非常简单,能做这个事情前提是你已经在这个手机上具备Root权限才能做这个事情。

当你Dump内核代码之后,根据Magic Number就可以知道这些开始的文件,放到IDA是会出错的。如果大家发生一些Crash,你可以写一些iOS下的一些工具,这个工具可以帮你列出当前iOS设备中所加载所有的驱动,以及驱动ID和起始位置,以及大小。写这个其实很简单,因为这个工具是在OSX平台上也有,并且是开源,你要做是稍微修改一下,移植到移动手机上。

再提一些IDA Pro 2.6的文件,我们从Kernel cache文件,当逆向iOKit的时候,我们要确定构造函数哪里?一般有这么几步,都是比较类似,首先用OSObject new,然后调用一个统一的接口就是Init,就是初始化,初始化之后进行OsMetaClass。

  • 如果各位想调试iOS内核的话,可能比想象还要更困难一些,这边给了一点提示。首先KDP代码,通过某些手段我们还是可以让它支持内核条施。
  • 这边还要介绍一个工具叫SerialkDPProxy,它的KDP只支持一个UDP五协议,手机上出来之后没有办法当做一根网线使用,没有一个协议,最多可以通过一个窗口协议。然后我们在电脑上把窗口协议跟UDP协议作为一个Proxy,来达到欺骗GDB的意思。
  • 最后一步要设置启动参数。对于A5来说还没有看到比较公开好的方法,有篇文章提到了A5内核调试的一个问题,他观点是需要一个内核漏洞欺骗内核,告诉内核我开始有调试参数,然后还要自己去进行一些调试相关的函数的初始化的工作。

接下来Chen Xiaobo给我们介绍一下目前已经知道一些比较有名的内核漏洞。首先我会简单列一下iOS内核的基础。还有一个就是刚才我们之前提到的iOKit,因为在苹果里面大部分是开源的,比如说USB和芯片里面这些接口都是闭源的,相对于开源的文件系统这些处理代码都是在开源的,我们可以直接下代码,闭源代码主要在iOKit代码里面。

Chen Xiaobo:我这里介绍两个iOKit比较有名的漏洞,一个是CVE—2010—2973整数溢出的漏洞,iOKit漏洞你可以以很低的权限就可以触发,这样可以让iOKit漏洞提前的目的。比如说我们可以用Library来触发这个漏洞,可以结合客户端的漏洞,来达到远程溢出和提权的目的,这个漏洞其中有几个字段,把这些字段设计畸形整数值,会导致整数溢出,这样创造分配内存会比较小,这样返回来做数据拷贝的时候会存在内核溢出这么一个漏洞,来达到补助内核。

这个列表大家可以看一下,这里设置了比较大的整数值,导致这个整数溢出。具体完整的快的代码现在已经开源了,大家可以去查找这个溢出的代码。

还有一个比较有名的,在2011年,里面有用到IOMobileFrameBuffer类型转换的问题,这个漏洞我个人分析它其实就因为在类型转换的时候,没有很好地利用类型检测,这样导致类型转换的问题。这样它直接调用数据里面一个指针,会直接跳过去执行,这个漏洞相比之前会更加简单、更加容易被利用。这里就是我根据这个漏洞的原理,用C写了一个简单的触发的过程,大家有兴趣可以自己去写一下,就是按照我这个上面所钩到的数据,就可以把内核里面的寄存器改成那个值。

iOKit漏洞可以作为提权的漏洞来使用,这样可以一键下载。内键功能的例子,大部分都是闭源的,基本上这一块没有太多安全人员在上面看。可以看到之前我们就是目前已知的有两个漏洞,同时也是一个非常好的方向来挖掘这个iOS内核的漏洞。

下面我会介绍被动挖掘iOKit漏洞的方法。

刚开始研究iOKit漏洞的时候,首先就是说在研究完就是说如何编写iOKit客户端之后,因为iOKit大部分接口是因为你需要逆向Cache,每个逆向你都要调用它的结构的话,这是一个比较大的工程,最开始一个idea,通过Fuzzer的方式进行漏洞的挖掘。如果大家看看iOKit编写,可以知道两种方法,你可以通过调用API调用结构类型的数据。还有一个传递一个整数类型的数据。还有就是说这两个API会调一个比较底层的函数。我们为什么做被动的Fuzz,如果你把这个结构一一调出来的话,这是各个比较大的工程。这个idea,在做linux中,就相当于Hook deviceloControl。在内核发生数据的时候,我们在这里拦截下来,可以动态修改参数。我们可以得到一些畸形的参数,再把这些数据返回到内核层,来看是否可以挖掘漏洞。

首先找一个好的Hook框架,我选的就是MobileSubstrate,这个框架写的非常好。它的具体的使用方法大家可以去iPHONE可以察看这个框架如何使用,虽然的文档不是特别多,但是大概的过程基本上能够了解。首先它提供了两种方法:MSHookFunctIOn,你传递的方式就是要原始的Hook地址;第二个就是Hook以后要跳出来,这个MobileSubstrate是属性的Hook。

还有另外一个好的框架,也是基于MobileSubstrate。只需要首先定义Hook,让Hook有一个类,下面可以在代码里面直接实现这个类方法,或者属性。比如说用一些log,比如说你Original,就是调原始地址,如果你后面函数需要参数的话,具体的下载有这个的地址大家可以去下载。

我们也可以用苹果的功能,类似于Hook表的概念,可以导入表里面,转化到Dyod里面。具体Hook的方法,就是把地要Hook的代码,把它注入到你要Hook的进程里面。Scalar object,针对这些方式,你可以找到很多有意思的漏洞。其中一些大部分还是指针方面一些漏洞,当然还有一些比较复杂的你可以直接控制PC指向你的数据,还有一些本身内核里面就有很多Panic内核异常处理,但是像Panic已经日常处理很少能够利用。

另外一个在做完了Fuzz的时候我们也发现一些缺点。虽然它的结果还可以,但是有一些缺点它并不能覆盖所有的iOKit驱动,要是被动的方式没有就要做主动的Fuzz。主动的Fuzz就是通过每个驱动获得每个驱动的接口。首先我们就是需要在iOKit里面找到这些接口,找到定义这些方法的地方,找到这些所有调用方法。

徐浩:因为主动的时候讲到一些原理,Chen Xiaobo刚才说我们要做就是找到内核中iOKit的结构,最终结构叫External methods,一个是调用号,还有输入和输出,这就是我们要找的对象。从用户态到内核态有一些方法,这里截取的办法是开源方式截取。从这边可以看到一个调用,当Dispatch有值的时候,就调用这个结构大小,这就是为什么要做立项的原因,如果你的输入和输出设置的大小不对的话,说明你根本没有发生的函数,为之前的检查就已经返还了。

这边也还是从OSX开源工程中获取一些iOKit驱动的开源代码,从这个代码中也看到基本的两种来实现External methods。可以看到它的一个结构体,还有最终发展的一个内核函数就在这边。

另外一个方法就是iOKit可以写这个函数,可以达到同样的目的。这里有一个代码的举例,有的就是采用这种代码的方式。作为一个主动Fuzz关键就是要获取这两种类型的结构,针对两种不的方法去获取两种类型的结构。

那么我们首先要找一些结构的话,首先第一个要解决的问题就是符号的问题,只有解决这些符号的问题,你才能通过符号的文件和字符串来定位这些驱动iOKit的方法Cache表;之后我们这里用了一个比较简单的方法,你的Cache符号解决以后,你就可以直接在IDA窗口里面搜索,大家看这个图就是我们通过IDA从关键字里面得出来的结果,一个是接口的名字,然后是接口的地址。

其实我们直接点开Methods的时候,看起来并不是那么直观,其实仔细看的话,每四个DCB其实就是一个调用了一个地址,我们可以手动把DCB改成BCD,它就是一个内核处理函数一个地址,但是还是不是非常直观。我们所做一个事情就是说我们写了一个脚本,自动的把所有这些放到这个库里面,这样我们比较直观察看这些结构。

首先要做的要能够定位这个Dispatch,这样我们才能自动把这个结构添加进去。这里是用我们的脚本以后得出的结果,我们可以看到基本上我们之前把那些所有结构里面所有的参数层面我们都已经仔细的把它归类区分好了,比如说他处理的函数地址和符号,还有它的Flag,这个是比较关键一个地方。如果说你自己写的长度跟预先的长度不符合,你这个请求不可能达到内核里面去。

我们对Flags的一个简单的定义,比如说枚举类型。举一个简单的例子,类型3就是输入跟输出的结构都是Struct,还有比较复杂的,我们必须要能够把所有这些类型Flags这些传入输出做好才能更顺利做下面的工作。之前我们没有做处理的都是一些IDB,如果我们说要做一些白盒或者肉眼挖掘代码工作的话非常有工作。

徐浩:刚刚Chen Xiaobo已经提到了,Fuzz跑了不久之后就产生了一些Crash,其实苹果内核代码中的质量也不是非常的高,所以产生BUGS应该还是比较多。这边我们会在PPT中公开两个我们找到目前是尚未修补的拒绝服务类型的内核漏洞。

典型的来说要分析一个内核的Bug会有这么几个步骤:

  • 第一个我们把Bug生成很多的一些Call把它精简化,精简化之后才能帮助我们进行静态分析,才能真正定位到到底是哪一个出的问题。
  • 第二个就是察看一些Panic log,我们可以定位出错的类型和出错的PC。
  • 第三个就是主要是在IDA中进行静态分析。
  • 第四步如果要写溢出的话,可能还是需要用到DEBUG。

这里我们列举第一个漏洞的代码就在这边,这个漏洞的代码非常简单,其实就是打开之后只调用了一个IOConnectCall,就导致了系统的崩溃。看一下我们拿回来的Panic log,从这个Panic log里面出现的问题的位置和出现的问题,这个基本上引用到了空置,我们如果在IDA把这个打开来看的话,这一行代码起初从R1一个值寄存器到R2这个里面,其中R1是一个0,所以导致这个问题。

我们会深入看一下这个BUG到底是什么问题?第一个你要找到这个问题就是去定位这个结构,如果在没有符号的情况下,我们其实同样可以定位到这个结构。因为通过之前他们才说的我们知道其实有两种方法来实现承载sMethod的函数,这边我们用了一个小的技巧来定位,这边我们通过定位它的一个偏移之后,我们就可以在这个字段里,通过查找临近的函数来能够最终确认他承载的函数的地址,在这边就标出来了。

后面三个其实没有承载它的一个地址,他的地址还是到临近函数。这个函数返回代码是很简单,它调用了它父类,它之前是从一个数组中计算了一个偏移,他把一个数组的地址加载到AR2之后,然后进行一个ED操作,正好是一个指针的长度。其实从这边如果大家仔细看一下这个汇编代码的话,最终应该是偏移一个结构体的长度。

我们Crash的代码是出现在第一个调用号的函数,我们在这个数组中找到第一个调用号的函数,就会看到这个函数的地址,80757534,刚刚讲过它的结构体中,它的Input 是2.4,Output大小是2.8,继续往下。

通过一个简单的分析,我们这边从整个崩溃,就是最上层的调用一直到一个路径,我们猜猜这个函数是用于去释放这个对象自身。那么R1等于0的话,就是说明他想释放某个对象或者结构,它存储在这里,但是并没有进行一个合理的初始化。这边给出一个详细的解释,这边这个BUG最后一个描述我们认为是这样,当去释放这个UserClient,它会释放自身的结构体和对象。

这些结构体和对象从来没有被创建过,所以它的指针是零,如果你去调用它,肯定会导致一个引用的问题。这个BUG是非常非常简单一个BUG,大家写代码的时候都会注意到,我在释放到肯定要判断这个对象是不是存在。就是判断它是不是Root的依据,不知道为什么苹果内核代码连非常基本的判断都没有添加。

这边我们还公开第二个漏洞。第二个漏洞相对来说比第一个要复杂一些,这边调用了6号函数,然后又调用了3号函数,3号函数实际上是一个Scalar的函数,我们目的就是整形输入的时候,把输入的数从0到100分别输入调用,同样我们看一下Panic Log,说明控制寄存器已经被修改了,看上去还可以。怎么定位它崩溃的位置呢?PC已经被修改,所以我们不能通过PC,大家如果对ARM比较熟悉,会知道ARM被调用之后,返回函数不是放在栈,而是通过AR2,所以通过AR2这个寄存器来找到。

大家仔细看一下这四行代码,其实是非常典型的调用C++对象成员函数代码。首先是从R4首地址读出了调用函数的地址,就是放到R1中进行调用。如果完整一点的崩溃时候的代码就是这样的。从这个代码中可以看到它是这个R4这个指针是从R0,对应的就是Client,在这个偏移中读出之后再进行偏移。

我们在调用3号函数进行循环之前,我们调用了6号函数,明显就已经超出了这个表的范围了,因为这个表六个结构体是0到5,这时候我们如果因为我们之前就已经定位到他的vtable,通过vtable的话,他有一个子类,这个叫IOIMGSGXU,我们同样察看这个子类的函数就会发现,其实我们真正打开的是一个子类,这个子类中有一个判断,就是判断你这个Selector大于5,就用自己的数字进行处理,小于5就是交给自己的数组处理。

我们具体要确定到底是一个什么东西的话?通过分析察看汇编可以看到,在这一边一个函数创建了一个对象,创建了一个对象之后,它在把这一个对象在下面一个方法里面去把它插入到OX10所在的数组。这个对象我们具体去看会发现创建这个对象我们找到它的构造函数,找到构造函数之后可以找到第一个指针,然后我们把这个指针给X3C偏移之后,就会发现的确是0的值。

最后对这个BUG做一个总结,这个BUG问题是类似这样,6号函数去创建了一个对象,并且把这个对象放到一个iOS对象一个10偏移数组中。3号是指针并且不经过检测对象类型就调用该对象的方法。因为存在父对象和子对象的问题,通过子对象去创建了一个Object之后,调演父对象的函数,在调用之前没有用一些函数进行安全转换导致了这个问题。

这个BUG分析还是花了比较多的时间,我们评论下来,因为它是一个固定偏移的,虽然它是一个类型转换的问题,因为代码都是公开,如果大家有兴趣直接写一下把这个代码嵌入你们自己应用中,手机应该就会被拒绝服务。当然如果大家觉得这个漏洞可能是可以利用,也很欢迎大家研究一下有没有办法利用。

我们今天提到这两个公开的代码到现在为止最新系统中是没有被修补的,我们可以通过简单一个DEMO看一下。这个是十月份做的一个DEMO,用的是一台iOS 6,我们非常简单地按一下,就会看到手机直接被迫重启,非常简单拒绝服务的例子。

最后的总结苹果在iOS驱动中,由于历史原因,一些比较老的代码检查或者代码的质量不太高,所以我觉得苹果有必要对这部分代码进行简单的审计,肯定会发现很多问题。从一个漏洞分析过程中来说,因为移动跟桌面不一样,它的内核调试非常非常困难,而且不方便,所以静态分析在移动平台上的话会需要更花功夫去做一些静态的分析,就是你对汇编什么都比较熟悉。

其实可以看到我们做的工作也不是非常的困难,但是还是可以发现一些成果,也希望大家可以去抓安全方面的研究,谢谢大家。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值