博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
iOS冰与火之歌 – UAF and Kernel Pwn
阅读量:6229 次
发布时间:2019-06-21

本文共 6151 字,大约阅读时间需要 20 分钟。

蒸米 · 2016/06/12 17:08

作者:耀刺,蒸米,黑雪 @阿里移动安全

英文版:

0x00 序


冰指的是用户态,火指的是内核态。如何突破像冰箱一样的用户态沙盒最终到达并控制如火焰一般燃烧的内核就是《iOS冰与火之歌》这一系列文章将要讲述的内容。这次给大家带来的是 Use After Free 的漏洞利用方式以及iOS 9.0上如何利用UAF攻击iOS内核的技术。除此以外我们还公布了一个在iOS9.3.2中刚刚被修复的内核堆溢出漏洞,可以用来攻击iOS 9.3.2以下版本的iOS内核并实现iOS Pwn的终极目标 - 越狱。

《iOS冰与火之歌》系列的目录如下:

  1. Objective-C Pwn and iOS arm64 ROP

  2. 在非越狱的iOS上进行App Hook(番外篇)

  3. App Hook答疑以及iOS 9砸壳(番外篇)

  4. 利用XPC过App沙盒

  5. UAF and Kernel Pwn

另外文中涉及代码可在我的github下载:

0x01 Use After Free


Use After Free简称UAF,是一种常见的堆漏洞利用方式。在Pangu9的越狱中,就是利用了iOS 9内核中的一处UAF漏洞获取了iOS最高权限并完成了越狱。在我们讲这个漏洞之前,可能有很多同学对UAF并不是很了解,所以我们先简单介绍一下什么是UAF以及如何利用UAF漏洞(老鸟的话可以直接跳过这一节)。

我们先来看一段程序(全部源码在github):

#!cclass Human{public:    virtual void setValue(int value)=0;    virtual int getValue()=0;protected:    int mValue;};  class Talker : public Human{public:    void setValue(int value){        mValue = value;    }    int getValue(){        mValue += 1;        cout<<"This is Talker's getValue"<
setValue(0); cout<
getValue()<

这段程序有三个类,其中Worker和Talker都继承Human这个类,并且他们都分别实现了setValue()getValue()这两个函数。因此当程序调用handleObject()的时候,无论传入的参数是Worker还是Talker,handleObject()都可以进行处理。

我们接下来看main函数:

#!cint main(void) {        Talker *myTalker = new Talker();        printf("myTalker=%p\n",myTalker);           handleObject(myTalker);        free(myTalker);         Worker *myWorker = new Worker();        printf("myWorker=%p\n",myWorker);           handleObject(myTalker);}复制代码

我们先new一个Talker,然后调用handleObject()打印它的value,然后free掉这个Talker。接着我们new一个Worker,然后继续调用handleObject()打印Talker(注意:不是Worker)的value,会发生什么呢?正常情况下,程序员调用handleObject(myTalker),是期望处理Talker这个对象,但是Talker这个对象已经被free了,并且指针没有置为NULL。这时候,如果有另一个对象(e.g., Worker)刚好被分配在了Talker指针所指向的地址,handleObject()也会对这个对象进行处理,并且不会报错。这,就是一个典型的UAF漏洞。我们来看一下程序执行的结果:

#!shellMinde-iPad:/tmp root# ./hellomyTalker=0x17d6b150This is Talker's getValue1myWorker=0x17d6b150This is Worker's getValue100复制代码

可以看到MyTalker对象在内存中的地址为0x17d6b150,然后MyTalker就被free掉了。随后,程序又创建了另一个对象myWorker。因为堆的特性,系统会把刚刚free掉的内存再分配给Worker。因此myWorker在内存中的地址也是0x17d6b150。所以当程序调用handleObject(myTalker)的时候,本应该期待调用Talker's getValue()函数却调用了Worker's getValue()函数,这就造成一个UAF错误。

如果程序不是在myTalker后面创建一个myWorker对象,而是自己malloc()一段可控的内存呢?我们再来看下面这段程序:

#!cint main(void) {            Talker *myTalker = new Talker();        printf("myTalker=%p\n",myTalker);           handleObject(myTalker);        free(myTalker);        int size=16;        void *uafTalker = (void*) malloc(size);        memset(uafTalker, 0x41,size);        printf("uafTalker=%p\n",uafTalker);         handleObject(myTalker);        return 0;}复制代码

我们并没有malloc一个Worker对象,而是自己malloc了一段16个字节的内存,并且把数据全部填充为0x41。如果free掉的myTalker指针指向了这段内存,并调用了handleObject(myTalker)会发生什么事情呢?

在运行程序前,我们先用lldb对程序进行调试:

然后用lldb连接程序并在main函数下一个断点:

确保程序正常进入main函数后,继续运行程序:

当程序执行到handleObject(myTalker)的时候,我们就能看到很有意思的error了:程序试图从r0这个地址读取数值到r2,然后blx r2,但是r0的值为0x41414141。这是因为我们在myTalker free后的malloc的内存刚好又重新分配在了myTalker指针指向的地址,随后程序调用了myTalker的的函数,根据c++的机制,程序会从myTalker的vTable里获取函数地址,但是我们已经利用UAF把vTable给填充成了0x41414141,所以才会报错。既然我们可以利用UAF控制r0的内容,只要配合上heap spray,我们就可以做到控制pc并执行rop指令了。Heap Spray的技巧可以参考我之前写的文章:。

0x02 iOS 9.0 UAF in IOHIDResourceUserClient


简单了解了UAF的原理以后,我们来看一下pangu越狱中搞定iOS 9.0内核的UAF漏洞。这个漏洞存在于IOHIDResource这个内核服务中。有一个好消息是这个服务是开源的(在IOHIDFamily中,我会把源码放到github上),

所以我们来看一下有漏洞的代码:

terminateDevice()这个函数中,内核服务调用OSSafeRelease()来释放一个device,但我们可以发现,虽然_device这个指针所指向的device被释放了,但是_device并没有置为NULL。如果我们再次调用已经释放后的device的函数的话就会触发UAF漏洞。随后Apple在9.1中修复了该漏洞,可以看一下修复的代码:

可以看到OSSafeRelease()已经变成了OSSafeReleaseNULL(),从而修复了UAF漏洞。

那么如何利用这个UAF漏洞呢?首先我们先利用IOKit提供的API创建一个device:

然后我们调用terminateDevice()方法将这个device释放掉:

然后我们再用释放后的device去调用一些需要用到device的函数,比如说IOHIDResourceDeviceUserClient::handleReport()就会触发UAF漏洞:

因为_device已经被释放掉了,但是_device这个指针的内容并没有赋值为NULL,所以函数会继续执到_device->handleReportWithTime(timestamp, report)。接着服务会去_device指向的内存地址查找vtable中的函数,如果我们能够在内存中malloc一段可控的内存并伪造一个fake的vtable,并且让这段内存刚好分配在_device所指向的地址,我们就可以成功的控制内核的PC指针了。

利用这个思路,我们成功的写出了利用程序,运行程序后,手机会重启,随后在~/Library/Logs/CrashReporter/MobileDevice/目录下的panic log中可以看到,我们已经成功的控制了pc指针并指向了0xdeadbeefdeadbeef:

对越狱来说,控制了内核的PC指针还只是一个开始,随后还要获取KASLR,利用ROP对内核进行读写,然后对内核进行patch,将签名校验disable等等,因为篇幅原因,这里就不一一介绍了,欢迎继续关注我们以后的文章。

0x03 inpuTbag – 一个影响了苹果设备15年的内核堆溢出漏洞


苹果在不久前发布的9.3.2中,修补一个非常典型的内核堆溢出漏洞,该漏洞存在于IOHIDFamily中。配合用户态漏洞触发,该漏洞能绕过内核所有安全机制,转化成内核任意读写,从而完成越狱。有该漏洞的代码最早是在2002年发布的mac os 10.2中引入,几乎影响了Apple全系设备15年之久。

出现漏洞的内核代码如下 ():

#!cIOHIDDevice::postElementValues(…) {… maxReportLength = max(_maxOutputReportSize, _maxFeatureReportSize);           - - - - - - - a report = IOBufferMemoryDescriptor::withCapacity(maxReportLength, kIODirectionNone);           - - - - - - - b… reportData = (UInt8 *)report->getBytesNoCopy()           - - - - - - - c… element->createReport(reportID, reportData, &reportLength, &element);//IOHIDElementPrivate::createReport           - - - - - - - d…}   IOHIDElementPrivate::createReport(…) {…            writeReportBits( _elementValue->value,      /* source buffer      */           - - - - - - - e                           (UInt8 *) reportData,    /* destination buffer */                           (_reportBits * _reportCount),/* bits to copy       */                           _reportStartBit);        /* dst start bit      */                           …}复制代码

代码行-a是漏洞的关键,IOHIDDevice的Report总共有三种类型:Output,Feature,Input;这些Report的Size是在创建IOHIDDevice时用户输入指定。这里只是根据Output,Feature来判断可能最大的Report Size是错误的,因为Input的size可能比OutPut和Feature都大。

代码行-b根据maxReportLength创建内核堆buffer。

代码行-c用来拿到创建的内核堆的buffer指针。

代码行-d是将Report内容保存到代码行-b创建的buffer,那么只要Post的Report类型是Input而且Size > max(_maxOutputReportSize, _maxFeatureReportSize)便能成功溢出。

代码行-e 是createReport ()的一部分。writeReportBits()在report count等于1的情况下等同于memmove操作,将clientMemoryForType中设定的Report内容拷贝到代码行-b创建的Buffer。

因此该漏洞可以从任意kalloc zone,达成任意长度的堆溢出。因为该漏洞是利用inpuT report来攻击iOS内核,再加上Tbag是《越狱》中一个非常有名的角色,所以我们将这个漏洞命名为inpuTbag。

0x04 One More Thing – iOS 9.2.1越狱


我们已经成功的利用inpuTbag堆溢出漏洞完成了iOS 9.2.1的越狱,如下是越狱的视频和截图 (因为Cydia的安装不太稳定,容易造成白苹果,所以我们的demo改成安装一个未签名的terminal app,并且可以用root权限执行任意指令,并且可以在系统的根目录下创建任意文件):

0x05 总结


这篇文章介绍了Use After Free的漏洞利用方式以及iOS 9.0上如何利用UAF攻击iOS内核的技术。除此以外我们还公布了一个在iOS9.3.2中刚刚被修复的内核堆溢出漏洞,并展示了iOS 9.2.1的越狱。另外文中涉及代码可在我的github下载:

0x06 参考资料


  1. Hacking from iOS 8 to iOS 9, POC 2015

转载地址:http://dcxna.baihongyu.com/

你可能感兴趣的文章
html弄多个按钮_HTML表单上的多个提交按钮 - 将一个按钮指定为默认按钮
查看>>
django判断checkbox是否选中_django视图层之请求与响应
查看>>
group by 怎么用java对象接收_生产服务宕机,线上业务挂了!Promtheus 怎么又不报警了呢?...
查看>>
himawari-8卫星叶绿素a产品、_走过50年,看“风云”眼中的世界| 卫星看中国特别版...
查看>>
mybatis使用$报空指针_打破你的认知!Java空指针居然还能这样玩,90%人不知道…...
查看>>
windows mysql 重置root密码_在Windows下Mysql如何重置root用户密码
查看>>
mysql5.6 linux下载_mysql-5.6.45-linux-glibc2.12-x86_64.tar.gz下载安装
查看>>
r语言操作mysql_R语言 RMySQL连接操作mysql数据库
查看>>
mysql 整形相除_整型相除截断的技巧
查看>>
mysql备份字符集_浅谈MySQL备份字符集的问题
查看>>
dos下设置mysql密码_dos命令下修改mysql密码的方法
查看>>
交换机如何设置我能访问它但他不能访问我_“交换机”有什么作用?怎样使用?...
查看>>
数据结构基本操作_R中的数据结构简介及类别变量的基本操作
查看>>
微分方程解法总结_视频教学:线性微分方程解的结构、问题类型及求解思路与方法...
查看>>
blt功能_bitblt()用法
查看>>
MySQL中level的用法_leveldb使用 (转载)
查看>>
卷积神经网络由谁提出_科研人员提出一种基于卷积循环神经网络的单通道渐进语音增强方法...
查看>>
python 曲线拟合求参数_Python:查找任意曲线的拟合参数数量
查看>>
python批量生成图片_python日常实用技能:如何利用Python批量生成任意尺寸的图片...
查看>>
python爱好者社区公众号历史文章合集_GitHub - thinkingpy/weixin_crawler: 高效微信公众号历史文章和阅读数据爬虫powered by scrapy...
查看>>