你的SoC上有个洞:联发科BootROM出现故障

2020-10-17 14:07:21

这项研究是由我们的实习生Ilya Zhuravlev进行的,她已经回到学校,但毕业后将重新加入我们的团队,NCC集团硬件和嵌入式系统业务部门的Jeremy Boone为她提供了建议。

随着ChipWhisperer等负担得起的工具链的出现,故障注入不再是一种仅限于资金充足和技术高超的对手的攻击媒介。与此同时,现代设备比以往任何时候都嵌入了更多的秘密,需要加以保护。这可以包括加密的用户数据或专有供应商机密。

电压毛刺是一种故障注入攻击,其中目标设备的电源电压被修改以导致意外行为。通常,这涉及暂时将处理器的核心电压轨短路到地,这将破坏处理器的内部执行状态。虽然毛刺的副作用可能很难准确预测,但通过观察系统的行为并仔细调整毛刺参数,可能会导致系统跳过某些指令的执行或破坏数据获取操作。通常,这些类型的故障可以使攻击者绕过由低级软件执行的关键安全操作,例如当引导加载程序在将执行控制传递给它之前验证后续固件映像的签名时。

过去,故障注入的研究大多集中在低功耗微控制器上,例如最近针对STM32系列MCU、恩智浦LPC和ESP32的攻击。鉴于这些类型的微控制器很少出现在功能更强大的手机或物联网设备中,NCC Group试图证明,当应用于更复杂的处理器时,此类攻击将会成功。

这篇博客文章描述了NCC Group描述联发科MT8163V片上系统(64位ARM Cortex-A)引导过程的方法,以及能够可靠地产生针对SoC的故障注入攻击的设备的设计。最终,我们的结果显示,联发科BootROM容易出现故障,允许攻击者绕过预加载器的签名验证。这绕过了所有安全引导功能,并允许执行未签名的预加载器映像,从而完全破坏了硬件的信任根。

我们的工作主要集中在MT8163V芯片组上,并且我们没有尝试针对SoC的较新变种进行此攻击。然而,我们知道许多联发科SoC共享相同的BootROM-to-Preloader执行流程。我们(尚未经过测试)怀疑该漏洞会影响目前市场上的其他联发科SoC。鉴于此平台的盛行,此漏洞将影响使用联发科芯片的各种嵌入式设备,包括平板电脑、智能手机、家庭网络产品、物联网设备等。

由于此漏洞出现在掩码ROM中,因此无法为所有受现场影响的产品修补此问题。然而,这个问题的严重性在很大程度上取决于产品威胁模型。电压毛刺攻击需要物理访问目标设备,因此在假设物理访问的威胁模型中风险最高,例如经常丢失或被盗的移动设备。相反,拒绝攻击者物理访问的部署可以适当降低顾虑。

NCC集团选择了一款流行的平板电脑设备,它使用了联发科MT8163V系统级芯片。目标的选择是基于其价格、广泛的可获得性,以及PCB有许多暴露和标记的测试点的事实。这简化了电路板反向工程过程,并使探测和故障板变得更容易。

许多联发科移动和平板电脑SoC都遵循共同的引导过程,如下图所示。我们的故障注入攻击的目标是BootROM,因为它正在加载和验证预加载器可执行文件。

BootROM是引导过程中不变的第一阶段,并且充当SoC的硬件信任根。通常,这些SoC包含可在OEM设备制造期间配置的eFuse组,以便启用安全引导并指定预加载器签名证书的散列。在启动期间,BootROM将读取这些熔丝以确定配置的安全引导策略。接下来,BootROM将把预加载器从eMMC加载到RAM中,并在执行之前验证其签名。

联发科的预加载器是引导过程的第二阶段,也是第一个可变代码。预加载器存储在BOOT0 eMMC分区上。如eMMC规范的第7.2节中所述,引导分区是与主用户数据分区分开的特殊硬件分区。

联发科SoC在BOOT0中存储预加载器的两个副本。如果第一个映像损坏(即未通过签名验证检查),则BootROM将加载第二个映像。如果两个副本都损坏,则引导ROM将进入下载模式,如通过通用异步收发器发送的字符串“[DL]00009C40 00000000 010701”所示。

为了将预加载器从闪存加载到RAM,需要使用eMMC引导模式功能。BootROM不发送单独的读取命令,而是将eMMC重置为此“备用引导模式”。这是通过发送两个GO_IDLE_STATE(CMD0)命令来实现的:第一个命令的参数为0xF0F0F0F0,用于将卡置于“预空闲”状态;另一个命令为0xFFFFFFFFA,用于将卡置于引导状态。

收到第二条命令后,eMMC开始通过DAT0线路以1位模式传输BOOT0分区的内容。接收整个分区内容大约需要100ms。

一旦BootROM从BOOT0分区接收到完整的第一预加载器映像,则通过发送GO_IDLE_STATE重置命令来中断该过程。

如果第一个预加载器映像有效,我们的观察显示,从传输预加载器的最后一个字节到观察到预加载器发出的第一个eMMC命令之间大约需要2秒。

另一方面,如果第一预加载器镜像无效(即,签名验证失败),则重复此过程。但是,现在BootROM直到接收到预加载器的第二个副本之后才会发送重置命令。在这种情况下,BootROM尝试加载第一个和第二个预加载器映像之间只需要大约700ms。

因此,我们假设在第一个~700ms期间,BootROM忙于解析预加载器映像结构和执行签名验证,接下来的1.2秒执行主要是预加载器初始化代码。为此,NCC集团决定电压毛刺攻击应针对从eMMC读取预加载器后的第一个700ms窗口。

为了精确定时注入电压毛刺,使用廉价的FPGA(Sipeed Don Nano)实现了自定义触发器。FPGA连接到eMMC CLK和DAT0线路(虽然图中还连接了CMD引脚,但它仅用于逻辑分析仪的调试)。

虽然FPGA的逻辑电平默认为3.3V,但它也可以在不进行任何电路板修改的情况下使用1.8V输入。FPGA的输出为3.3V触发信号,并连接到ChipWhisperer触发输入引脚。

Verilog触发器代码非常简单:FPGA由eMMC时钟信号提供时钟,代码使用DAT0实现移位寄存器,以跟踪通过线路传输的最后4个字节。当观察到所需图案时,在512个eMMC时钟周期内产生触发输出信号:

Always@(posedge eMMC_CLK或Negedge sys_rst_n)BEGIN CAPTURE<;=CAPTURE;COUNTER<;=COUNTER;TRIGGER<;=TRIGGER;IF(!sys_rst_n)BEGIN TRIGGER<;=1';b0;COUNTER<;=24';b1000000000;CAPTURE<;=32';b0;END ELSE IF(COUNTER&>0)BEGIN COUNTER<;=COUNTER-1;CAPTURE<;=32';b0;END ELSE IF(CAPTURE==32';h4ebbc04d)BEGIN TRIGGER<;=1';b1;COUNTER<;=24';b1000000000;END ELSE BEGIN TRIGGER<;=1';b0;CAPTURE<;={CAPTURE[31:0],eMMC_Data0};END。

正在匹配的模式4e、bb、c0、4d是位于预加载器的第一个副本末尾附近的四个字节:

然后,触发输出信号被馈送到ChipWhisperer,在那里插入延迟并产生特定宽度的毛刺。

将SMA连接器焊接到平板电路板的一侧,然后通过导线连接到目标焊盘:VCCK_PMU。该故障通过ChipWhisperer的低功耗MOSFET将VCCK_PMU短路到地。通过在非常短的时间内降低核心电压,我们预计会破坏处理器的内部状态(例如寄存器的值),而不会完全使整个系统崩溃。为了访问VCCK_PMU焊盘,用刀从PCB上刮下了一部分防焊掩模。没有执行其他电路板修改(即,我们没有发现有时需要移除去耦电容器)。

毛刺装置的总体设置及其连接如下图所示。

1.8V UART:使用1.8V逻辑电平的UART适配器。这样我们就可以查看目标输出并确定故障尝试何时成功($2美元)。

Raspbercrypi:用于通过使用uhubctl禁用和重新启用USB电源以编程方式重置目标设备($50 CAD,CanaKit)。

FPGA:被动侦听eMMC流量,并将毛刺触发信号输出到ChipWhisperer($10 CAD,Digikey)。

Scope e.glitch.clk_src=";clkgen";scope e.glitch.output=";enable_only";scope e.glitch.rigger_src=";ext_Single";scope e.clock.clkgen_freq=16000000 scope e.io.glitch_lp=True scope e.io.glitch_hp=false。

接下来,需要确定目标毛刺宽度。为了实现这一点,当设备在BootROM和预加载器中执行时,手动注入不同宽度的毛刺。观察到大约80-100个时钟周期的毛刺宽度,从而在预加载器中引入各种类型的状态损坏。然而,这些国家腐败中的许多似乎都是不可利用的。例如,在一次迭代期间观察到以下输出:

[2176][PART]CHECK_PART_OVERLAPPED DONE[2180][PART]LOAD";TEE1";从0x0000000000B00200(Dev)到0x43001000(MEM)[Success][2181][PART]加载速度:15000KB/s,46080字节,3ms[2213][Platform]错误:<;assert>;div0.c:行41 0[2213][Platform]错误:P1致命错误...。[2214][平台]印刷机长时间重启的PL延迟

如前所述,我们假设签名检查发生在最后的GO_IDLE_STATE命令之后的700ms窗口内。为了覆盖整个700毫秒的计时,使用了渐进式暴力方法。

首先,将未经修改且签名正确的预加载器加载到eMMC BOOT0分区。然后,在偏移范围[25400,100000]中执行步长为200周的粗磨力。假设有用的毛刺偏移将导致设备崩溃(在通用异步收发器上看不到输出),或者被置于DL模式(在通用异步收发器上观察到“[DL]00009C40 00000000 010701”输出字符串)。

通过此实验过程,我们确定大多数尝试的偏移不会导致设备行为发生明显变化,并且预加载器正常加载和运行。然而,在运行这个第一阶段的撞击力几个小时后,发现了多个感兴趣的区域,并对它们施加了更细粒度的撞击力。这种细粒度方法使用20个周期的步长值,而不是200个周期。

此时,NCC Group通过修改调试字符串篡改了预加载器映像。由于签名检查失败,BootROM应拒绝加载此被篡改的映像。但是,如果加载并执行此篡改的图像,我们将知道故障是否成功。NCC集团再次确定了感兴趣的领域,并继续强制执行毛刺参数。经过大约2个小时的猛烈攻击,确认了几个成功的小故障。然而,这些成功是不可靠的,需要进行更多的微调。

接下来,围绕这些特定的偏移量和宽度对brteforce进行微调,以发现完美的毛刺参数。有了适当的参数和几天的强力,我们能够获得15-20%的绕过签名检查的成功率。下表总结了这些运行的统计输出,演示了多组参数(宽度和偏移量)能够实现成功的毛刺。

请注意,所有成功的毛刺都集中在宽度(93-130)和偏移量(41428-41438)的狭窄范围内。这些值可以与本文末尾提供的ChipWhisperer脚本一起使用。

除了简单地篡改调试字符串之外,我们的目标是执行任意代码。因此,接下来,将一个有效负载注入到预加载器二进制文件中,替换字符串部分的一部分。预加载器也被修改为跳转到它通常执行GPT解析的有效负载。之所以选择位于预加载器后期的特定位置,是因为在故障成功后,UART必须重新配置不同的波特率参数,这需要一些时间,并且会导致预加载器的早期输出丢失。

注入的有效负载将打印一条日志消息,然后读取BootROM内存和eFuse内容。以下UART输出中显示了成功的毛刺尝试:

演练已完成,开始!105 41431 b';\x00[DL]00009C40 00000000 010701\n\r&39;105 41433 b';\x00';99 41432 b&39;\x00\n\rF0:102B 0000\n\rF3:4000 0036\n\RF3:0000 0000\n\rV0:0000 0000[0001]\n\r00:0007 4000\n\r01:0000 0000\n\rBP:0000 0209[0000]\n\rG0:0190 0000\n\rT0:0000 038B[000F]\n\r跳到BL\n\r\n\r\xfd\xf0';重新打开序列!<;之后出现故障。[1167][Dram_Buffer]dram_buf_t大小:0x1789C0[1167][Dram_Buffer]part_hdr_t大小:0x200[1168][Dram_Buffer]g_dram_buf起始地址:0x4BE00000[1169][Dram_Buffer]g_dram_buf->;msdc_gpd_pool起始地址:0x4BF787C0[1169][Dram_buffer]g_dram_buf->;MSDC_BD_POOL起始地址:0x4BF788C0[1187][RAM_CONSOLE]SRAM(0x12C000)sig 0x0不匹配[1188][RAM_CONSOLE]START:0x44400000,大小:0x10000[1188][RAM_CONSOLE]sig:0x43074244[1189][RAM_CONSOLE]off_pl:0x80[1189][RAM_Console]sz_pl:0x10[1190][RAM_Console]WDT status(0x0)=0x0<;----联发科MT8163V电压故障概念验证。-引导ROM:00000000:08 00 00 EA FE FF FF EA 00000010:Fe FF FF EA 00000020:BB 38 00 20 10 00 A0 E3 00 10 A0 E3 00000030:00 20 A0 E3 00 30 A0 E3 00 40 A0 E3 00 50 A0 E3 00000040:00 60 A0 E3 00 70 A0 E3 00 80 A0 E3 00 90 A0 E3 00000050:...。电子保险丝:10206000:11 00 0F 00 62 00 00 00 10206010:00 00 00 10206020:00 00 00 10206030:00 00 00:00 10 02 00 00 00 50 0C 00 00 00 10206050:00 00 00 10206060:46 08 00 00 00。00 07 00 00 00 10206070:00 00 00 10206080:00 00 00 10206090:47 C8 DE F6 A6 A9 A1 8B 7A 8D 71 91 06 BC 18 86 102060A0:9F 97 E1 CD A3 7C 4C E8 AB E8 7F 60 E8 A6 FD 77 102060B0:...。

至此,我们已经展示了我们的毛刺技术是成功的,并且注入的有效负载能够执行任意代码。尽管没有演示,但是也可以执行预加载器通常负责的任何高特权操作,例如解密和加载修改后的TrustZone映像、加载恶意LK/Android映像等。

我们已经证明,联发科MT8163V SoC容易受到电压毛刺攻击。此外,我们观察到无需预先设置毛刺设备(例如时钟同步或从电路板上移除电容器)即可获得很高的毛刺成功率。虽然每组毛刺参数都有大约20%的成功率,但对手只需在两次毛刺尝试之间重新启动,就可以简单地实现100%的总体成功率。

由于此漏洞会影响BootROM,因此无法在现场打补丁,因此所有现场产品都将无限期地易受攻击。在我们与联发科的谈话中,联发科表示计划在即将推出的未命名SoC的BootROM中实施故障注入缓解措施。我们没有机会评估这些缓解措施的有效性,也没有机会评估它们是基于硬件还是基于软件。

NCC集团担任许多半导体公司以及设计和制造智能手机或物联网产品等嵌入式设备的公司的战略安全顾问。为了支持整体安全工程,我们建议我们的客户考虑减轻故障注入攻击。对于电压毛刺,基于硬件的缓解措施,如快速反应的硅内掉电检测电路,是最有效的防御措施。或者,也可以使用基于软件的缓解措施,尽管它们只提高了对手的门槛,并不能完全缓解攻击。基于软件的缓解示例包括:

冗余地执行关键检查,如果产生冲突的结果,则终止执行。这种缓解会迫使攻击者执行多个连续的故障,以绕过单个关键安全检查。

在整个安全关键代码的各个点插入随机持续时间延迟。这种缓解会迫使攻击者实施多个准确的触发条件。

在BootROM中实现控制流完整性,特别是在代码的安全关键部分周围。这种缓解可以帮助检测何时注入故障导致程序执行意外的代码路径,例如跳过分支指令。

对于设备OEM来说,缓解更困难。它们通常影响其上游硅供应商实施的抗毛刺特性的能力有限。在这种情况下,NCC集团建议设备OEM与其供应商密切合作,以了解组件的安全状况。如果在理解上存在差距,请考虑第三方评估。此分析必须在组件选择阶段早期完成,以便在可能的供应商组件之间进行有用的比较。只有那些符合产品安全目标和威胁模型的组件才应考虑使用。在芯片组级别之上,额外的物理保护层可以帮助减缓这种性质的攻击,包括仔细的PCB设计、广泛的防篡改措施,以及明智地使用加密技术来保护重要的用户数据。

对于进一步远离BootROM实现的用户和消费者来说,重要的是从表现出对其产品安全性的承诺的供应商那里购买设备。对于容易丢失或被盗的移动设备尤其如此,因此容易受到这里讨论的各种类型的物理攻击。最低的价格往往意味着最不重视安全的重要性。寻找积极的安全特征,例如发布的错误奖励程序。

.