Apple史诗级DFU漏洞分析

gewenbin
gewenbin
gewenbin
188
文章
15
评论
2021年3月16日23:08:26 评论 605

1. 概述

国外黑客通过研究苹果更新的补丁发现了一个史诗级的漏洞,该漏洞存在于从A5到A11的大部分iPhone、iPad等苹果设备中。这些设备的BootROM在设备DFU模式时处理USB数据流程方面有漏洞,通过该漏洞可以执行用户自己的代码,从而达到破解运行自制系统和软件的目的。针对于该漏洞的详细分析原文请参见https://habr.com/en/company/dsec/blog/472762/,中文机翻请参见https://blog.csdn.net/cullen2012/article/details/108487994,文章详细地分析了漏洞的原理和攻击手段,各个细节阐述的都很清楚。

为了更通俗易懂的理解该漏洞,本篇文章以前文为基础,概括出漏洞攻击的基本原理。

Apple史诗级DFU漏洞分析

图 1‑1  iPhone7

2. 漏洞发现历史

这里主要介绍本漏洞从发现到利用的过程中发生的几件比较重要的事件。

2.1 最初发布

本漏洞最初由网名为axi0mX的黑客于2019年9月27日在推特上公布,同时他还把利用本漏洞进行越狱的工具的源码公布在github上:

Apple史诗级DFU漏洞分析

图 2‑1  漏洞首次公布

从他的推文中可以看出此漏洞是BootROM漏洞,影响了从iPhone4S到iPhoneX之间的大部分苹果设备。

2.2 漏洞分析

虽然axi0mX公布了漏洞,但是他并没有详细地说明漏洞原理,随后在2019年10月24日,一名网名为a1exdandy的用户在网上公布了他的团队对漏洞的详细研究成果,也就是之前的那篇分析文章:

Apple史诗级DFU漏洞分析

图 2‑2  漏洞分析

根据a1exdandy的github简介来看,他应该是来自俄罗斯从事安全研究工作的人员:

Apple史诗级DFU漏洞分析

图 2‑3  a1exdandy资料

2.3 运行Linux

2020年1月30日,一名名为qwetyoruiop的用户在推特发帖称已经在iPhone7上成功运行Linux:

Apple史诗级DFU漏洞分析

图 2‑4  iPhone7运行Linux

2.4 运行Android

2020年3月5日, Corellium团队在推特上发布了一个名为“Project Sandcastle”的项目,通过该项目可以在iPhone平台运行Android系统:

Apple史诗级DFU漏洞分析

图 2‑5  Project Sandcastle

文中提到的Checkra1n和PongoOS是由最先公布漏洞的axi0mX和其团队开发。Checkra1n是漏洞攻击程序,为运行自制程序提供环境,PongoOS是引导程序,可以用来引导Linux。

2.5 运行Ubuntu

2021年1月11日,一名叫newhacker1746的用户在reddit上发帖称其已经在iPhone7上成功跑起来Ubuntu20.04系统,并附带运行视频和步骤:

Apple史诗级DFU漏洞分析

图 2‑6  iPhone7运行Ubuntu

3. iPhone启动流程

由于本次暴露的漏洞是由BootROM引起的,我们有必要先来看看苹果iPhone等设备的一个基本启动流程:

Apple史诗级DFU漏洞分析

图 3‑1  iPhone启动流程

设备上电后最先运行的是BootROM程序,这段程序在工厂生产时被烧录到芯片中,且无法再次被更改,BootROM做一些基本的初始化之后就会尝试引导iBoot,这是苹果自己的系统引导程序。iBoot随后寻找合法的Kernel镜像进行引导,内核镜像随后又会加载系统的各个组件运行,这个流程就是正常情况下设备的启动流程。

在设备异常情况下,比如升级出问题变砖,这时可以通过某种方式让设备进入DFU(Device Firmware Update)模式,通过USB来引导系统启动,从而进行系统恢复。但是不幸的是,苹果设备DFU模式下USB数据处理流程方面有很大的缺陷,从而导致可以利用漏洞运行自制程序。

4. USB简介

为了更好的理解DFU漏洞,这里对本次漏洞涉及到的USB知识做一个简单的介绍,详细的USB资料请到互联网搜寻。

4.1 USB四种传输模式

为了适应不同场景下的数据传输需求,USB协议里规定了四种传输方式,每种传输模式的介绍如下所示。

4.1.1 批量传输

批量传输用于需要大量数据传输的情况,比如U盘和主机之间的传输就属于批量传输。

Apple史诗级DFU漏洞分析

图 4‑1  批量传输

4.1.2 中断传输

中断传输适用于那些不经常传输数据的场合,比如鼠标、键盘和主机之间的传输就属于中断传输。

Apple史诗级DFU漏洞分析

图 4‑2  中断传输

4.1.3 等时传输

等时传输又叫同步传输,用于那种对传输实时性和速率有较高要求的场合,比如摄像头、声卡数据的传输。

Apple史诗级DFU漏洞分析

图 4‑3  等时传输

4.1.4 控制传输

控制传输用于对设备进行配置和设置,获取设备的一些状态信息等等,当USB设备没被初始化之前,主机就通过控制传输来和设备进行通信并初始化设备。

另外本次要讲的漏洞就是在USB控制传输处理方面的漏洞。

4.2 USB包概念

USB总线上的数据传输是以包为基本单位的,包总共有四种类型:令牌类、数据类、握手类和特殊类:

Apple史诗级DFU漏洞分析

图 4‑4  USB包类型

4.2.1 令牌包

令牌包用于在正式传输之前发起一个通知作用,通知对侧设备接下来会有什么样的操作以让设备提前做准备。在本次的漏洞分析中,我们只关注OUT和SETUP这两个令牌包。

4.2.2 数据包

数据包携带有负载数据,从图 4‑4中还可以看出根据数据的不同还可以有四种数据包类型。

4.2.3 握手包

类似于TCP/IP,握手包主要用于给对侧设备一个回应,来表明本设备是否正常处理对侧发来的数据。

4.2.4 USB事务

虽然USB定义了数据在总线上传输的基本单位是包,但是我们还不能随意地使用包来传输数据,必须按照一定的关系把这些不同的包组织成事务才能传输数据。

事务通常由这几个包组成:令牌包、数据包、握手包。

  • 令牌包用来启动一个事务,总是由主机发送。
  • 数据包传送数据,可以从主机到设备,也可以从设备到主机,方向由令牌包决定。
  • 握手包的发送者通常为数据接收者,当数据接收正确后,发送握手包。设备也可以用NAK握手来决定数据还未准备好。
4.3 控制传输事务过程

控制传输包括三个过程:建立过程和状态过程分别是一个事务,数据过程则可能包含多个事务。这三个过程中的每一个过程都可以有本阶段要传输的数据:

Apple史诗级DFU漏洞分析

图 4‑5  控制传输事务

4.3.1 SETUP阶段

建立过程阶段只能使用SETUP令牌包,此阶段也可以携带自己的数据,但是数据类型只能是DATA0。

4.3.2 DATA阶段

数据过程阶段是可选的,一个控制传输有可能没有数据过程。此阶段可以使用OUT或者IN类型令牌,在本漏洞分析中,我们只关注OUT类型数据传输。

4.3.3 STATUS阶段

状态阶段用于结束数据传输,此阶段可以使用OUT或者IN类型令牌。针对控制传输读写数据和不带数据三种情况下的传输流程如图 4‑6所示:

Apple史诗级DFU漏洞分析

图 4‑6  控制传输三种情况

5. DFU漏洞

5.1 DFU简介

DFU在第3章节iPhone启动流程中介绍过,主要作用是通过USB来恢复系统。

5.2 BootROM在DFU模式处理流程

其实苹果BootROM的源码在网上有过泄露,但是在之前的文章中由于法律风险,并没有引用这些源码,而是通过DUMP出BootROM的镜像并逆向分析的手段来整理出BootROM在DFU模式下的处理流程,下面就简单来看看这个流程。

5.2.1 DFU初始化

Apple史诗级DFU漏洞分析

图 5‑1  DFU初始化

如图 5‑1所示,初始化的时候会申请一个0x800大小的buffer并清零,并注册两个回调函数。有STEUP包来临时就会调用handle_interface_request函数来处理,当有数据包来临时会调用data_received函数来处理。

5.2.2 SETUP包处理

Apple史诗级DFU漏洞分析

图 5‑2  SETUP处理

在SETUP处理阶段如果解析出接下来有数据要传输的话,则将初始化阶段申请的buffer地址返回出去并被保存在全局变量中。

5.2.3 DATA包处理

Apple史诗级DFU漏洞分析

图 5‑3  DATA处理

在DATA处理阶段,首先将接收的数据拷贝到之前申请的buffer中,然后根据一些列的条件来判断是否需要调用之前注册的数据处理回调函数,如果数据都接收处理完则将全局变量都清零。

在数据处理回调函数中会将接收的数据再次拷贝到一个固定的地方,当设备退出DFU模式时,之前分配的buffer会被释放掉,并对接收的镜像进行验证并尝试启动。如果有任何异常情况则DFU将再次初始化,并且整个过程从头开始重复。

5.2.4 漏洞

通过上面的描述我们发现这个处理过程有“use after free”漏洞,如果在数据传输过程中我们再次发送SETUP包(USB规范流程下不该出现此情况)并跳过DATA处理阶段,则全局变量中保存的地址等信息不会被清除。在下一个DFU阶段中我们可以不用发SETUP包直接发送DATA包,则发送的数据会被放入上一DFU阶段中申请的buffer地址中。

由于在堆中malloc的数据还有其他的数据结构,如果我们能找到一个成员中有函数指针这种数据结构,并通过某种方法将其malloc的地址控制在上一个DFU阶段申请的buffer地址范围内,那么我们就可以用自己发送的数据覆盖这个数据结构中的函数指针,当BootROM去调用这个函数指针时就会跳到我们希望的地方去执行了。

6. 漏洞攻击

6.1 Checkm8简介

Checkm8是用于利用漏洞进行攻击的一段程序,是一段python程序。这里我们只关注其主要步骤,详细的流程请参见源程序。

6.2 堆风水

堆风水英文叫Heap fang-shui,意思是通过一系列的控制将堆中数据的分布布局成自己想要的样子以进行不可告人的目的。这正好和我们的风水学说有异曲同工之妙,所以叫做堆风水吧==

6.2.1 USB请求数据结构

前面我们说过希望找到一个成员带函数指针的数据结构,现在还真有这么个数据结构。BootROM会为每个到来的USB请求申请一个如图 6‑1所示的数据结构:

Apple史诗级DFU漏洞分析

图 6‑1  USB请求数据结构

这个数据结构大小为48字节,其中有一个成员是callback回调函数,用于对接收到的USB请求进行处理。

6.2.2 USB标准请求回调函数

对于一个标准的USB请求,callback函数如图 6‑2 所示:

Apple史诗级DFU漏洞分析

图 6‑2  标准请求回调函数

其中io_length等于描述符长度和请求包中的长度这两者中最小值,由于描述符长度很大,所以其值等于请求包中的长度值。g_setup_request.wLength的值等于最后一个请求包中的长度值。

当条件满足时,usb_core_send_zlp函数被调用,这会创建一个数据长度为0的包,这个包是在状态阶段用于回应OUT令牌以结束整个传输过程,如图 4‑5所示。

6.2.3 清除请求队列

在BootROM中有一个请求队列,在队列中的USB请求在USB重置的时候会被依次执行,我们通过某种手段让USB请求队列中的请求组织称如图 6‑3所示:

Apple史诗级DFU漏洞分析

图 6‑3  初始请求队列

可以看出,图中有2个数据长度为0xc0的请求和6个数据长度为0xc1的请求。当处理第1、2个请求时,根据6.2.2小节的描述,g_setup_request.wLength值为0xc1,io_length值为0xc0,所以6.2.2小节中的判断条件满足,也就是处理完这两个请求后会生成两个ZLP(zero-length-packet)请求,大致过程时这样的:

第一个请求被处理完,生成一个ZLP请求,同时释放请求数据结构所占的内存空间:

Apple史诗级DFU漏洞分析

图 6‑4  处理完第一个请求

在BootROM中,malloc采用的是寻找最合适大小内存块这种算法,所以在第二个请求被处理完时,新生成的ZLP请求数据结构就是原来第一个请求所在的内存,如图 6‑5所示:

Apple史诗级DFU漏洞分析

图 6‑5  处理完第二个请求

接下来依次处理完剩余的6个请求,这6个请求由于不满足6.2.2.中的条件,所以不会生成ZLP请求,都处理完时,堆中的情况如图 6‑6所示。

Apple史诗级DFU漏洞分析

图 6‑6  处理完请求

经过上面的操作,我们就获得了一个大小为48*7=336字节大小的内存块了,其中两个ZLP请求如果在没有收到IN或者OUT令牌的情况下是不会被处理和释放的,所以这两块内存不会被释放。

6.3 重置DFU

a1exdandy在分析iBoot源代码和对BootROM反向工程时,得到了以下堆内存使用情况:

Apple史诗级DFU漏洞分析

图 6‑7  堆内存使用情况

  • Nonce大小234字节。
  • Manufacturer大小22字节。
  • Product大小62字节。
  • Serial Number大小198字节。
  • Configuration string大小62字节。
  • Task structure大小0x3c0字节。
  • Task stack大小0x1000字节。
  • io_buffer大小0x800字节。
  • hs conf大小25字节。
  • fs conf大小25字节。

当重置DFU进行第二次DFU处理时,堆中的布局就会如图 6‑8所示:

Apple史诗级DFU漏洞分析

图 6‑8  第二次DFU处理

可以看出来先前的几个结构体的内存被安排了第一次DFU阶段中组织处的336字节内存块中,所以在fs conf和ZLP之间就空出来了一个内存块,当有新的USB请求来临时,malloc出来的48字节的请求数据结构就会从图 6‑8中的红框中进行申请。

全局变量中记录的io_buffer地址本应该时下图中绿色箭头所指的地方,但是由于在第一轮DFU处理中全局变量的值没有被清零,如果我们在第二轮DFU中跳过SETUP阶段,这个全局变量就不会被覆盖成新的io_buffer地址,其地址还是下图中红色箭头所指的位置。

新的USB请求结构是在下图中紫色箭头所指的位置,只要我们计算出红色箭头和紫色箭头之间的偏移值,我们就可以用自己的数据进行覆盖请求结构了。

Apple史诗级DFU漏洞分析

图 6‑9  溢出攻击

6.4 覆盖USB请求数据结构

经过计算,图 6‑9中的偏移值为0x5c0,而io_buffer的长度为0x800,所以这个偏移完全在io_buffer的访问范围内,这时只要事先准备好一个构造的数据,就可以用我们想要的值覆盖USB请求结构中callback成员的值,如图 6‑10所示:

Apple史诗级DFU漏洞分析

图 6‑10  组织数据覆盖callback

图 6‑10中的t8010_nop_gadget值就是新的callback的值。当覆盖完USB请求数据结构后,只要触发BootROM去处理这个请求,新的callback就会被调用也就会跳到我们想要的地方去执行了。

7. 结尾

7.1 iPhone7如何进入DFU模式
  • 同时按住iPhone7音量-键和电源键
  • 等到屏幕黑屏之后3秒松开电源键,继续按住音量-键
  • 再等待7s左右松开音量-键,手机进入DFU模式
7.2 总结

从上面的分析中我们可以看出,漏洞被利用的根本原因就是全局变量没有被及时清零,如果在DFU初始化的时候也强制初始化全局变量为0的话,这个漏洞就没法利用了。所以写程序一定要注意该清零的时候要清零啊==

gewenbin
  • 本文由 发表于 2021年3月16日23:08:26
  • 转载请务必保留本文链接:http://www.databusworld.cn/9990.html
匿名

发表评论

匿名网友 填写信息

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: