Nintendo Switch RCM漏洞分析

gewenbin
gewenbin
gewenbin
188
文章
15
评论
2020年12月22日22:07:19 1 1,785

1. 概述

本篇文档对任天堂的Switch RCM漏洞做一个介绍,国外黑客通过dump并逆向芯片内的BootRom,找到了Switch在RCM模式时通过USB传输数据的一个漏洞,通过该漏洞可以执行用户自己的代码,从而达到破解运行自制系统和软件的目的。黑客原始的演讲视频在b站就有,地址为https://www.bilibili.com/video/av59585943,视频中详细地介绍了如何绕过芯片的安全机制dump芯片内BootRom,并对RCM漏洞做了非常详细地分析。本篇文章可以看做是视频后半部分的文字版本,更详细的信息请参见视频。

Nintendo Switch RCM漏洞分析

图 1.1 Nintendo Switch

2. 硬件架构

2.1 总览

Switch的SOC采用的是英伟达的Tegra X1,CPU部分是4核Cortex A57 + 4核Cortex A53,armv8 64位架构;GPU部分是英伟达的GeForce Maxwell,如图 2.1所示。采用Tegra X1芯片的设备除了Switch之外还有英伟达自己的Shield安卓TV盒子、谷歌Pixel C平板。该SOC的官方开发板也是可以在网上买到的,数据手册只要在英伟达官网注册账号就可以下载。

Nintendo Switch RCM漏洞分析

图 2.1 硬件架构

2.2 BPMP

在Tegra X1数据手册中,将4核Cortex A57 + 4核Cortex A53称为Complex CPU,除此之外还有一个arm7架构的BPMP(Boot and Power Management Processor),用于运行芯片的BootRom和唤醒Complex CPU等。如图 2.2所示。从图中还可以看出,用于运行BootRom的SRAM大小为64kB * 4 = 256kB。

Nintendo Switch RCM漏洞分析

图 2.2 BPMP

3. Boot启动流程

图 3.1展示的是Tegra X1开发板上的系统启动流程,Switch上的流程类似,不同的是从BootRom之后引导的是任天堂自己的bootloader和地平线操作系统。图中红色部分表示代码是运行在BPMP上,蓝色表示代码运行在CCPLEX(CPU Complex)上,可以看出上电初始阶段的boot都是运行在BPMP上的。图中还有一个RCM分支,用于当设备变砖后,通过USB恢复bootloader和系统的目的。

Nintendo Switch RCM漏洞分析

图 3.1 Boot流程

虽然Switch和开发板的后续bootloader和系统都不一样,但是他们使用的BootRom是一样的,BootRom是烧录在芯片中的启动代码,一旦烧录进芯片就无法再被更改。所以通过开发板研究的BootRom漏洞同样适用于Switch。

4. RCM漏洞

4.1 RCM简介

RCM表示Recovery Mode,以前刷过安卓手机的同学应该知道,当由于某种原因手机变砖后,可以通过Recovery Mode重新刷入系统固件,以达到挽救手机的目的。一般RCM模式下可以通过SD卡或者USB重新刷入固件,Tegra X1的RCM模式下是通过USB来刷固件的。

4.2 USB协议简介
4.2.1 USB协议简介

由于RCM漏洞是BootRom在处理USB数据过程中的一个漏洞,所以有必要对USB的知识做一个基本的介绍,更详细的USB资料可以参见这个USB系列博文https://blog.csdn.net/qq_16777851/category_9283392.html

Nintendo Switch RCM漏洞分析

图 4.1 USB通用标识

4.2.2 端点

在USB体系中,设备分为主机和从机,比如我们的电脑就是主机,U盘、鼠标等等都是从机。主机和从机之间通过端点来建立通信,端点(Endpoint)是USB从机上的一个数据缓冲区,用于接收和发送USB各种数据,如图 4.2所示。

Nintendo Switch RCM漏洞分析

图 4.2 USB端点

端点是有方向属性,比如ep1就是输出型端点,ep2是输入型端点,注意输入还是输出是从主机的角度看的。ep0端点比较特殊,可以为输入也可以为输出,而且是USB协议中规定的这个端点必须存在,USB主机利用端点0实现缺省的控制管道,从而控制从机。除了端点0,其余的端点在该端点被配置前,不能与主机通讯。每个端点都有一定的属性,包括传输方式、带宽、端点号和数据包的最大容量等等。

4.2.3 控制传输

USB主机和从机之间通过端点来进行通讯和数据传输,根据传输的类型不同,可以将端点分为四种类型:控制传输、等时传输、中断传输、批量传输。控制传输用于访问并配置从机,比如端点0就是一个控制传输端点,通过该端点可以给USB从机发命令以查看从机的状态等等信息。

4.2.4 批量传输

批量传输用于需要大量传输数据的端点,比如主机和U盘之间传输数据就是通过批量传输端点来进行的。

4.2.5 标准USB请求

所有USB从机都响应默认控制管道上(端点0)主机的请求,这些请求是使用控制传输进行的。请求包的数据格式如图 4.3所示。

Nintendo Switch RCM漏洞分析

图 4.3 控制传输请求数据格式

  • bmRequestType:这个字段包含了传输方向、命令类型、接受者类型信息。
  • bRequest:具体的请求类型,比如获取状态信息、设置地址等等。
  • wValue:请求的参数,根据不同的请求而变。
  • wIndex:根据不同的请求意义不同。
  • wLength:表示要传输的数据的长度,这个字段可以为0,表示无数据传输。在输入请求下(主机获取从机数据),从机返回的数据长度不应多于wLength,但可以少于此长度。这个字段比较重要,因为Tegra X1的BootRom对此字段的处理有漏洞。
4.2.6 RCM使用的USB端点

BootRom在RCM模式下使用了两种USB端点,控制传输端点(ep0)和批量传输端点(ep1)。批量传输端点用于主机和设备之间传输RCM消息,控制端点用于主机获取设备的一些信息,比如状态信息、获取描述符信息等等,如图 4.4所示。

Nintendo Switch RCM漏洞分析

图 4.4 RCM使用USB端点

4.3 BootRom处理RCM数据

以下展示的数据结构和伪代码都是黑客通过逆向分析BootRom得到的。

4.3.1 RCM数据

主机发送给设备的RCM数据由两个部分组成,RCM消息加RCM Payload数据,如图 4.5所示。设备会对RCM消息进行合法性检查,如果是合法的就接着处理RCM Payload数据。

要注意的是,BootRom是将RCM消息和Payload数据一块接收完成后再对RAM消息做合法性验证的,这就为利用RCM漏洞执行非法的用户代码提供了可能性

Nintendo Switch RCM漏洞分析

图 4.5 RCM数据

4.3.2 RCM消息结构

RCM消息的数据结构如图 4.6所示。从图中可以看出主机发送到设备上的RCM数据都是经过签名的,BootRom程序会对RCM数据做各种合法性检查,如果发现是不合法的消息就不会做出进一步的反应。如果想让RCM消息变成合法消息,就需要破解RCM消息的签名算法,从图 4.6中可以看出RCM数据使用的是RSA加密,想要破解这个基本是不可能实现的。

Nintendo Switch RCM漏洞分析

图 4.6 RCM消息结构

从图中还可以看出数据结构中的第一个成员len_insecure表示RCM消息加上Payload数据整体的大小,最后一个成员表示RCM的Payload数据的大小。

4.3.3 整体流程

BootRom处理RCM消息的伪代码流程如图 4.7所示。可以看出总体上分为4个步骤:

  • 处理USB主机控制传输请求,将设备的基本信息传输给主机。
  • 接收RCM消息,这里只是接收数据到缓冲区而不会对数据做检查。
  • 接收到RCM消息之后就会对消息的合法性做一系列的检查,如果不合法直接返回不进行后面的操作。
  • 如果所有的合法性检查都通过之后,保存用户代码的地址,然后跳转执行。

可以看出如果想让芯片执行用户代码,就必须准备一个合法的RCM消息,否则就没戏。

Nintendo Switch RCM漏洞分析

图 4.7 RCM消息处理流程

4.3.4 iRAM内存布局结构

在分析rcm_receive_message函数之前我们先来看看,BootRom运行RCM时,SRAM中的内存功能布局,如图 4.8所示。

Nintendo Switch RCM漏洞分析

图 4.8 SRAM内存布局

  • 有两个USB的DMA Buffer,用于接收和发送USB数据,大小分别为16kB。
  • 栈大小为12kB,紧接在第二个USB DMA Buffer之后,用来存放局部变量和函数调用的返回地址信息
  • 用于接收存放RCM Payload数据的缓冲区大小为3 * 64kB = 192kB。
  • 还有一个全局变量区域,设备接收到的RCM消息就存放在全局变量区,如图 8所示。
4.3.5 接受RCM消息

我们来看看rcm_receive_message这个函数的功能,rcm_receive_message的伪代码流程如图 4.9所示。

Nintendo Switch RCM漏洞分析

图 4.9 RCM数据接受流程

  • 通过read_ep1_out_async函数通知设备到ep1端点读取数据,前面我们已经知道了RCM有两个端点,其中ep1端点用于批量传输功能,主机就是通过ep1端点来发送数据给设备的。注意这个函数是异步执行的,也就是说只是通知设备去接收数据,并不会等待数据接收完成才返回。同时我们也可以看到每次尝试接收的数据大小是0x4000,这个大小就是一个USB DMA Buffer的大小16kB,也就是每次设备都尝试从主机接收一个USB DMA Buffer的大小的数据。
  • 当第一次执行while循环时,由于数据不会立马接收到,所以执行完步骤1会直接到步骤4,步骤4 就是等待数据接收完成才返回。当数据接收完成时,此时数据是存放在USB的DMA Buffer中的。
  • 如果设备接收到了数据,接下来会判断接收的数据的大小,如果小于680字节表示当前接收的是RCM消息,因为RCM消息的总大小是677字节,参见图 6。这里要说的是主机发送数据到设备是分批次的,先发送RCM消息然后再发送RCM Payload数据。
  • 如果接收的是RCM消息,则将接收的数据从USB DMA Buffer拷贝到全局变量区,同时查看RCM消息第一个成员以确定接下来还有多少RCM Payload数据需要传输。
  • 如果接收的数据大小大于680字节,表示接收的RCM Payload数据,就将数据从USB DMA Buffer拷贝到RCM Payload区域,参见图 8。

可以看出,设备是将RCM消息和RCM Payload数据一起接收的,并且存放在内存中的不同区域。

4.3.6 BootRom处理批量传输流程

我们来看看设备是如何通过USB来传输数据的,现在我们知道RCM是通过ep1端点来从主机获取数据的,这个伪代码流程如图 4.10所示。

还记得我们在USB简介章节说的除了ep0端点之外,USB从机的其他端点在未被主机配置之前是不能和USB主机通讯的么,所以在使用ep1之前需要主机对ep1进行配置。如图 4.10所示,红框内的代码检查ep1端点是否被主机配置过,如果配置过则跳出while循环进行数据读取,如果没有配置过,则调用绿框中的函数等待主机进行配置。

Nintendo Switch RCM漏洞分析

图 4.10 RCM批量传输

4.3.7 BootRom处理控制传输流程

RCM通过handle_ep0_control_transfer函数来进准备数据和USB主机进行通讯,响应主机的各种请求,比如获取状态、设置地址等等。我们来重点关注下主机获取状态这个请求,如图 4.11所示。

  • USB标准请求的数据格式如图 3所示,第一个字段包含了请求的方向,如果是设备到主机的请求则进入图 4.11中的处理分支。
  • 如果请求的类型是GET_STATUS,则会进一步判断是什么的状态,比如图中的设备和端点状态。图中对要返回给主机的数据大小计算不同,如果是设备状态,返回数据大小为2个字节,如果是端点状态,则大小是请求数据包中的wLength其实根据USB协议,GET_STATUS返回的数据大小应该固定为2个字节!
  • 当知道要返回的数据和大小后,回到第四步对返回的数据大小做调整,因为假设主机请求获取10个字节的数据,但是设备上对应的数据有20个,那么设备只能分2此返回数据,每次传输10个,这就是图中第四步做的工作。
  • 当一切准备就绪之后,通过memcpy将数据拷贝到USB DMA Buffer然后发送给主机。这就是图中第五步做的事情。

Nintendo Switch RCM漏洞分析

图 4.11 RCM控制传输流程

4.3.8 漏洞

正常情况下,GET_STATUS返回的是2个字节的数据,memcpy其实是将栈中的2个字节数据拷贝到DMA Buffer中,因为status这个变量是函数中的局部变量,对应的内存在栈区,如图 4.12所示。

Nintendo Switch RCM漏洞分析

图 4.12 GET_STATUS正常拷贝操作

但是我们看看图 4.11中的第3步,在这个步骤中,返回的数据长度是由USB请求中的数据长度字段决定的,而这个字段是由主机发送控制的!!!正常情况下,这个wLength应该是2,但是如果这个值不是2而是一个更大的值呢!比如是8kB的话,memcpy就会将栈区后面的Payload区域的数据也拷贝到DMA Buffer中了!如图 4.13所示。

Nintendo Switch RCM漏洞分析

图 4.13 wLenght字段改为8kB大小

那么如果我们再将wLength改大一点,比如(16+12)kB呢,由于在DMA Buffer区域之后是栈区,那么memcpy会用Payload区域的数据覆盖栈区!如图 4.14所示。

Nintendo Switch RCM漏洞分析

图 4.14 wLenght改为28kB大小

前面我们说过,栈中除了保存局部变量数据之外还保存了函数调用的返回指针!而覆盖到栈区的是Payload区域的数据,这个区域的数据是我们可以控制的,如果我们精心设计Payload区的数据,让其覆盖栈中的函数返回指针,那么当BootRom的函数执行完返回时就会跳转到我们自己的代码执行了!

我们来看看到现在为止的整个函数调用链,如图 4.15所示。

Nintendo Switch RCM漏洞分析

图 4.15 函数调用链

可以看出,当执行完memcpy函数后,由于返回指针已经被我们给替换,所以不会返回到原来的memcpy之后代码执行,而是跳转执行我们自己的代码了!

4.4 利用漏洞执行用户非法代码大致流程

现在我们来总体看下RCM漏洞执行非法代码的流程。

  • 设备进入RCM模式。
  • 使用USB连接设备和主机。
  • 通过RCM传输非法数据给设备。
  • 不要结束传输过程,可以通过将RCM消息结构中的大小字段设置一个比RCM Payload数据还大的值。
  • 主机发送一个GET_STATUS请求,并且将其中的wLength字段设置一个较大的值。
  • Payload数据被memcpy覆盖了栈区,从而控制了返回指针
  • 执行用户非法代码
4.5 Switch如何进入RCM模式
  • 将eMMC从主板上移除。
  • 按住音量增加键 + Home键。这里的Home键不是Joycon上的Home键,而是右Joycon插槽中的Pin10,通过将Pin10和Pin7或者Pin1短接就相当于按下Home键。

5. 总结

通过前面的分析可以看出由于两个原因导致了RCM漏洞可以执行非法代码:

  • 核心错误是GET_STATUS时本来应该返回两个字节,结果却复制了wLenth个数据。
  • 另外一个错误是在一次处理中同时接收RCM消息和RCM Payload数据,然后再做RCM消息合法性验证。如果处理逻辑是先只接受RCM消息做验证,那么即使用户使用了RCM漏洞拷贝了过多数据覆盖栈区,但是由于RCM Payload数据还没接收并保存到Payload区域,那么覆盖到栈区的也仅仅是垃圾数据而已,虽然这种情况下返回指针同样被垃圾数据覆盖,但是当函数返回时只会造成系统崩溃而不会跳转到非法代码执行。

这个世界上没有绝对安全的系统,只要有人系统就有漏洞~

gewenbin
  • 本文由 发表于 2020年12月22日22:07:19
  • 转载请务必保留本文链接:http://www.databusworld.cn/9660.html
匿名

发表评论

匿名网友 填写信息

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

评论:1   其中:访客  1   博主  0
    • 监控大奇葩 监控大奇葩 4

      b哥你怎么这么吊啊