目录
本文总结了目前最为流行的存储器固件提取技术,虽然在技术的层面上没有任何新意可言,但是确是少数以防守为目的进行的总结,可以更好地理解ECU的存储安全需求,制定更加合理的安全存储策略。
提取固件的技术大致可以分为两类:侵入式提取与非侵入式提取。”侵入“在固件提取中的具体涵义为”是否需要拆卸存储器“,如果不需要拆卸存储器,那么我们就将这类提取技术归为”非侵入式提取“,反之则归类为”侵入式提取“。
非侵入式有两种实现思路,一种思路是通过JTag、SWD等调试接口与设备建立连接,然后通过协议实现的数据读取功能实现提取;另外一种思路是通过夹具连接存储器管脚,将存储器变成树莓派等提取工具的外挂存储器,使用树莓派给存储器上电并提取存储器中的内容。
侵入式提取,即将存储器拆卸下来,通过读卡器或者编程器读取存储器内的固件。
这种方式主要针对控制器的内置Flash,因为内置Flash没有直接向攻击者暴露管脚,物理拆卸难度极大,所以站在攻击者的角度考虑,通过调试提取的方式可行性最高。如下为通过调试接口提取STM32系列芯片存储器固件的原理图:
调试器(如JTag、Jlink等)通过调试接口与设备相连,实际的设备接线方式类似下图:
连接好设备之后,通过调试命令即可提取固件,如下为通过JTag提取固件的命令:
- jtag> readmem 0x1fc00000 0x400000 flash.bin
- address: 0x1FC00000
- length: 0x00400000
- reading:
- addr: 0x20000000
- Done.
需要注意的是,在线提取需要一些前提条件,比如设备release之后调试端口依然可用,并且调试连接的建立不需要password,以及控制器的读保护功能未开启。
针对调试提取的方式,STM32自带的一种读保护机制(RDP)值得我们参考和学习。根据STM32开发手册的描述[1],STM32在内置存储器中划分了一篇选项配置区域,该区域用来配置STM32运行时需要用到的各种重要参数,其中就包含了对RDP的描述。RDP配置包含一个用来描述是否开启RDP功能的比特位,以及开启后保护的地址范围的字节信息。选项配置区域的写权限由管理员掌握的password保护,以保证该区域的安全性。
调试器与STM32建立连接后,会给STM32发送调试命令。当STM32收到读取存储器内容的命令时,首先会判断RDP位是否已经置位,如果已经置位,说明开启了RDP功能,在这种情况下STM32直接返回否定响应NACK,以拒绝调试器的读取请求。下图为STM32在USART协议中对读命令的处理流程:
通过相关安全研究人员的博客[2],我对STM32 RDP的代码层实现进行了求证。STM32中RDP的实现主要集成在bootloader之中,因为bootloader实现了各种通信或调试协议,因此理所当然地应该在这些协议中加入对RDP的判断逻辑。下图所示汇编代码包含了在USART通信过程中,当STM32收到读地址内容的请求之后,调用RDP判断函数的过程:
如下汇编代码包含了RDP的判断逻辑,该代码片段表明STM32从0x40022000获取了一个地址,然后以此为基址,取了0x1C偏移处的内容,通过LSLS移位和BPL跳转条件判断第二个bit位是否置位:
这个实现完全符合STM32开发手册中的描述,STM32使用选项寄存器FLASH_OBR的第一个比特位RDPRT控制RDP:
在设备的bootloader中实现RDP具备一定的开发成本,对于本身不具备RDP功能的设备,需要充分考虑安全需求和实现成本。如果成本受限但又必须考虑安全性,那么可以考虑直接禁用调试端口,或实现基于口令的安全调试。从安全的角度考虑我们应该直接禁用调试,但是从研发的角度考虑,我们在各个阶段都会存在调试需求,因此基于口令或证书的安全调试可能是一个不错的选择。
另外,STM32的开发文档明确指出了RDP是对”internal Flash“的保护,但是按照实际的实现逻辑,只要STM32的bootloader在收到读取指令时进行RDP的判断,理论上对外置存储器的读请求也会拒绝掉。STM32这么描述的原因,我猜测其出发点是保护的效果,因为对于内置存储而言,拆卸难度太大,最有效的提取方式就是调试连接,如果在调试连接中实现了RDP,可以说基本阻断了提取途径;但对于外挂存储器,还存在其他可行的攻击手段,比如通过夹具挂载Flash,以及拆卸Flash,禁用调试读命令完全达不到保护的效果。
使用芯片夹具挂载Flash,首先需要一个Host,可选的设备包括树莓派、Arduino等。另外,是否能够使用夹具挂载Flash,直接取决于芯片的封装方式。如果芯片的封装方式暴露了管脚,则可以很方便地使用夹具,如果芯片的封装方式完全隐藏了管脚,那么这种方式就失去了尝试的必要。从这个角度来讲,芯片的管脚也属于一种攻击面。另外,我觉得没有必要研究芯片具体采用了哪种封装方式,因为管脚是否暴露是肉眼可见的,如下图所示:
隐藏管脚的封装方式一般如下图:
针对暴露管脚的存储器芯片,使用夹具进行提取的接线方式如下图所示:
以使用树莓派为例,需要开启树莓派SPI,结合存储器芯片管脚的定义以及树莓派管脚的定义,将存储器挂载为树莓派的SPI Flash,然后通过flashrom工具从SPI Flash中提取固件[3]。
flashrom -p linux_spi:dev=-regexp">/dev/spidev0.0,spispeed=1000 -r rasp.-property">bin
因此站在防守的角度,如果我们能够控制存储器芯片封装的方式,那么最佳的实现应该是使用能够隐藏芯片管脚的封装方式。
最后一种方式可能成本相对最高,但是理论却最为简单。首先需要购买一个编程器,然后使用热风机融化芯片焊锡,取下芯片之后放在编程器上,通过编程器对应的界面工具选择芯片型号,读取芯片内容,如下所示[4]:
为了防范通过编程器提取固件,我们可以选择加密存储的方式,或者依赖文件系统的NAND Flash,来增加提取原始固件的成本。
因为提取固件的成本和难度相较于其他实验较高,本问相关内容均是参考其他研究人员的成果。但这并不妨碍我们加深对存储器攻防技术的理解,通过这种方式我们依然可以掌握攻击的原理,并且总结防守的方案。关于防守,除了文中提到的相关参考,在实际应用中需要更加细致的考虑,比如对内置和外置存储安全方案的区分,对不同代码、数据保护方案的细化,等等,希望读者结合项目实际灵活应用。
[2] Read secure firmware from STM32F1xx flash using ChipWhisperer - Prog.World
[4] unsafe.sh - 不安全
网站声明:如果转载,请联系本站管理员。否则一切后果自行承担。
加入交流群
请使用微信扫一扫!