首页 公告列表 K网(Kraken) Kraken发现Trezor硬件钱包中的关键缺陷(谷歌翻译)

Kraken发现Trezor硬件钱包中的关键缺陷(谷歌翻译)

阅读数
1371
阅读数
2020-01-31 22:37:00
文章来源:K网(Kraken)

Kraken Security Labs设计了一种从行业领导者Trezor,Trezor One和Trezor Model T提供的加密货币硬件钱包中提取种子的方法。

攻击只需要对设备进行15分钟的物理访问即可。这是第一次公开了针对这些设备的当前攻击的详细步骤。

这是我们的操作方式:  

  • 这种攻击依靠电压毛刺来提取加密的种子。最初的研究需要一些专业知识和几百美元的设备,但我们估计我们(或犯罪分子)可以大量生产一种对消费者友好的故障设备,其售价约为75美元。
  • 然后,我们破解加密的种子,该种子受1-9位数的PIN保护,但对于暴力破解来说却微不足道。

攻击利用了Trezor钱包中使用的微控制器内部的固有缺陷。不幸的是,这意味着Trezor团队如果不重新设计硬件就很难对这个漏洞做任何事情。

在此之前,您可以采取以下措施来保护自己:

  • 不允许任何人 实际 访问您的Trezor钱包  
    • 您可能会永久丢失密码
  • 使用Trezor 客户端 启用您的BIP39密码短语
    • 该密码短语在实践中使用时有点笨拙,但没有存储在设备上,因此是一种防止这种攻击的保护措施。

这种攻击与 我们之前针对KeepKey钱包的研究 非常相似 ,这是可以预期的,因为KeepKey是衍生产品,并且所有设备都依赖于同一系列芯片。 自设计钱包以来, Trezor就知道这些缺陷

其他团队, 例如Ledger Donjon ,也执行了此攻击的变体,尽管到目前为止尚未公开完整的详细信息。

这些芯片并非旨在存储秘密,我们的研究强调,Trezor和KeepKey等供应商不应仅依靠它们来保护您的加密货币。

我们很幸运能与Trezor团队合作来协调此披露, 您也应该查看他们的回复 。 SatoshiLabs的CTO Pavol Rusnak补充说:“我们很高兴Kraken Security Labs正在投入他们的资源来改善整个比特币生态系统的安全性。我们珍视这种负责任的披露与合作。”

在Kraken Security Labs,我们试图在坏人之前发现对加密社区的攻击。 我们已于2019年10月30日负责任地向Trezor团队披露了此攻击的全部详细信息。我们现在公开此漏洞披露信息,以便加密货币社区可以在Trezor团队发布修复程序之前保护自己。

技术细节

从Trezor钱包中提取种子并不是新领域。 Trezor已经针对各种先前的硬件攻击,包括针对中表现出的毛刺攻击成功缓解实施显著缓解 Wallet.Fail 在谈 第35届混沌通信大会 。这种攻击是基于该研究绕过缓解措施的。

我们的攻击始于使用故障注入攻击重新启用处理器的集成引导加载程序。这种集成的引导加载程序具有读取设备闪存内容的功能,但在执行命令时会验证芯片的保护级别。通过利用第二次故障注入攻击,可以绕过此检查,然后可以一次提取256个字节的整个设备闪存内容。通过重复攻击,可以提取所有闪存内容。此外,由于Trezor固件使用了加密存储,因此我们开发了一个脚本来破解转储设备的PIN,从而完全损害了Trezor钱包的安全性。该脚本能够在2分钟内强行强制使用任何4位数字的图钉。该攻击表明,Cortex-M3 / Cortex-M4微控制器的STM32系列不应该用于存储敏感数据(例如加密种子),即使它们以加密形式存储也是如此。

STM32F205和STM32F427是分别用于Trezor One和Trezor T的基于闪存的微控制器。 Trezor One的许多派生工具(例如Keepkey)也使用STM32F205。 STM32F2和STM32F4均为ST Microelectronics STM32系列的ARM Cortex-M3微控制器。 STM32F2和STM32F4提供了实现硬件钱包所需的所有外围设备,包括PLL以及USB等接口。最值得注意的是,STM32F205提供了两个常见的ARM编程接口: JTAG 和ARM SWD 。除了这些编程接口外,STM32F205还提供了集成的引导加载程序,可用于使用UART,USB和CAN等接口对设备进行编程。

STM32上的闪存和SRAM读保护

STM32系列实现了一种称为读取保护或RDP的安全机制。由于ARM Cortex-M设备上唯一的非易失性存储是闪存,因此RDP值存储在闪存的特殊页面中,否则无法通过应用程序代码进行写入。 RDP值由称为选项字节的微控制器配置位定义。对应于STM32器件上三个RDP级别的三个选项字节值。

Kraken发现Trezor硬件钱包中的关键缺陷(谷歌翻译)表1:STM32系列设备上的RDP级别和相应的选项字节值。

由于STM32微控制器上唯一的非易失性存储器是闪存,因此它也是加密种子和私钥的唯一非易失性存储器。结果,必须保护闪光灯不被读出。幸运的是,Trezor及其所有派生工具正确地利用了RDP功能,并在首次引导时随RDP一起提供和/或将RDP设置为RDP 2级(请参见表1)。结果,实际上,用户设备上的非开发固件始终处于RDP2(RDP级别2),这可防止攻击者访问SRAM或Flash。但是,如Wallet.Fail和 Chip.Fail 研究所示,可以在启动时通过电压毛刺可靠地将RDP2降级为RDP1。一旦设备位于RDP1,就可以通过ARM SWD调试协议读取其SRAM。

由于STM32具有复杂的上电复位(POR)逻辑,正常的复位置位(即 NRST线在短时间内保持低电平的 软复位) 不会导致完全上电。重置并重新执行BootROM。更改安全配置(即更改选项字节以更改RDP级别)通常需要对芯片重新通电,这一事实在某种程度上也得到了证实。相反,这也意味着,一旦芯片成功故障并导致安全配置的降级,这种安全降级将一直有效,直到对芯片供电后才可以重启。这意味着攻击者可以反复尝试对设备进行故障处理,检查故障是否成功,同时仍然在不加载应用程序代码的情况下仍在应用程序代码中执行Bootrom或非常早地执行该操作。结果,没有针对此类攻击的有效对策,因为攻击者可以在执行应用程序代码之前确保故障已成功。一旦攻击者成功修复了设备故障,攻击者只需 对目标 执行 软复位,系统便会继续以RDP1运行,从而允许攻击者在任意给定时间点任意读取SRAM存储器的内容。这尤其成问题,因为许多实现签署加密货币交易所需的加密技术的库都依赖于将敏感信息加载到SRAM中进行计算。另外,可以在钱包派生期间将密码种子加载到SRAM中,并且可以加载用户的PIN以针对用户输入进行验证。如果验证了基础固件或计算校验和以检查固件的完整性,则此数据的部分或全部也可能会受到攻击[ O'Flynn Circuit Cellar ]。

STM32引导过程和故障参数

微控制器的许多行为由上电时读取的值定义。其中包括在启动时读取的绑扎引脚(STM32文档中的BOOT引脚)和安全配置位(STM32文档中的选项字节)。请注意,以下许多详细信息是通过对STM32F2的启动行为进行经验反向工程确定的。大多数Cortex-M微控制器都包含在引导时执行的ROM,通常称为BootROM。 BootROM是芯片执行的第一批软件,负责加载重要参数,例如芯片的安全性配置。随后,执行用户应用程序或应用程序代码。对于硬件钱包,这是制造商的实际固件。请注意,由于本工作中描述的故障攻击针对的是BootROM代码,因此无法通过供应商的固件中实施的任何对策可靠地缓解它。固件中的漏洞导致无法修复的固有硬件漏洞,需要使用新的硬件版本完全替换基础硬件。

大概是由于STM32F2的相对复杂性,STM32需要很长的启动时间,大约是在对芯片的电源循环上电后的1.2ms-1.8ms。可以通过两种方式可靠地测量启动时间:测量设备的功耗并测量功耗的初始上升所花费的时间(例如使用 LSCM) ,或者通过观察复位行为微控制器的(NRST / JTAG RST)线。在最初的100us – 200us之内,将执行芯片的BootROM。

重新启用JTAG,SWD和集成的BootROM Bootloader

在STM32启动期间,将在执行应用程序代码(即钱包固件)之前执行集成的BootROM。 BootROM会执行多项检查,这些检查又会配置设备的安全状态。其中包括启用/禁用JTAG和SWD调试接口以及集成的串行BootROM引导加载程序。因为STM32系列是基于闪存的,所以这些设备上唯一可用的非易失性存储器(NVM)是闪存[ Obermaier ]。由于闪存是STM32上唯一可用的NVM,因此安全配置也必须存储在闪存中。

闪存有一个专用区域,用于存储安全性和设备配置,称为选项字节(OB)。对于整体设备安全而言,最重要的是,OB包含读取保护级别(RDP级别),该级别实际上是设备的安全配置。 RDP级别包括禁用所有调试接口和引导程序的级别2,启用了有限的引导程序功能和有限的调试功能的级别1以及允许完全访问的级别0。默认情况下,Trezor及其衍生产品配置了RDP 2级,这是STM32提供的最强的安全性。

在执行BootROM期间,将检查RDP的值。如果在RDP检查期间的值不是RDP2,则BootROM将根据预定义的引导模式(即BOOT0和BOOT1引脚的逻辑电平)检查两个专用I / O引脚的引导。如果不满足启动模式,则继续常规执行,并从闪存执行应用程序代码。但是,如果满足模式要求,即在STM32F2中,如果BOOT0为高电平,而BOOT1为低电平,则启用集成的BootROM串行引导程序和DFU引导程序。由于STM32支持多种串行协议,因此当在一个串行接口上满足有效同步条件时,将初始化并禁用一个或多个串行引导加载程序。同步后,引导加载程序进入一种模式,在该模式下,它会接收并执行命令。

Kraken发现Trezor硬件钱包中的关键缺陷(谷歌翻译)表2:STM32F205数据表中的引导加载程序激活模式 STM32 BootROM引导程序

STM32 BootROM引导加载程序支持各种命令,包括能够读写闪存的命令。

Kraken发现Trezor硬件钱包中的关键缺陷(谷歌翻译)表3:文档“ STM32引导程序中使用的USART协议”中受支持的引导程序命令列表

“ Read Memory”命令可用于从设备的闪存中读取多达256个字节。对BootROM的分析确定,每次调用该命令时,该命令都会检查RDP级别,并且仅在RDP级别设置为RDP级别0时才允许读取闪存内容。

绕过RDP检查Read Memory命令

Trezor One等钱包中使用的STM32在制造时设置为RDP 2级。这会使所有调试功能失效,并禁用集成的BootROM引导加载程序。电压毛刺可能会破坏从选项字节读取的RDP值,如电子钱包失败研究所示。这实际上使攻击者可以将目标设备的安全配置从RDP级别2降级到RDP级别1。实际上,由于RDP级别0之间的汉明距离,从RDP级别1降级到RDP级别0被认为是不可行的。和RDP级别2。通过在BootROM执行期间执行电压故障,可以重新启用JTAG和SWD调试接口。确定可以以类似方式重新启用集成的BootROM引导加载程序。

例如,在STM32F205上,通过以大约170us的速度在BootROM执行过程中出现毛刺,可以重新启用JTAG和SWD。该系统将有效地使JTAG和SWD接口具有与RDP Level 1相同的行为(即,在访问方面有相同的限制)。确定在执行BootROM时大约180us的电压毛刺将重新启用集成的BootROM Bootloader。具有与RDP级别1的Bootloader相同的行为。一旦建立了与集成BootROM引导程序的通信,即同步完成,便可以在RDP级别1的BootROM引导程序中发布可用的命令,例如GET ID。但是,在RDP 1级上不可用的命令将导致STM32返回NACK并失败。

但是,由于已确定对于某些命令(即读取内存),BootROM引导加载程序命令处理程序针对发出的每个命令检查设备的RDP级别是否为RDP级别0。在处理针对RDP级别0以外的RDP级别禁用的命令时,定时与命令处理程序的RDP级别检查同时发生的电压毛刺会导致绕过RDP级别检查,从而使命令成功。这意味着可以根据设备的RDP配置对应该失败的命令进行故障处理(即绕过应该返回NACK的命令处理程序)。结果,可以执行在RDP级别1或RDP级别2上不可用的命令。如果应用于读取命令,则可以从微控制器中任意读取闪存。由于许多基于STM32的钱包的加密种子都存储在STM32闪存中,因此这些设备的种子存储可能会受到损害。

电压闪变硬件设置以转储闪存

Digilent Arty A7 FPGA开发板用于产生毛刺和脉冲,以及对STM32进行检测并精确定时毛刺。基于FTDI FT232H的分支板Adafruit FT232H用于与BootROM引导加载程序命令处理程序进行UART串行通信。使用Maxim MAX4619多路复用器在STM32 CPU内核电压的额定工作电压和毛刺电压(即GND或0v)之间进行多路复用。 BreakingBitcoin开发板用于简化交互,它是与引脚兼容的Trezor突破板。可以在硬件钱包上进行相同的攻击。但是,与现场焊接相比,焊接所有连接(简称Boot0和Boot1)比拆下微控制器并将其放置在插座中要容易得多。

Kraken发现Trezor硬件钱包中的关键缺陷(谷歌翻译)图1:故障设置。左上方为MAX4619模拟多路复用器。 STM32F205的64针LQFP64插座。右侧是一个红色FT232H USB-UART适配器,用于连接BootROM UART Bootloader。右上方是Digilent Arty A7 FPGA开发板。 强制执行Flash Dump的PIN

Trezor使用从PIN和盐导出的密钥对机密存储进行加密。该盐包括存储在OTP字节中的硬件盐和在配置设备时从闪存生成的随机盐。硬件盐的长度为44个字节,可以通过一次读取选项字节来读取。

Trezor将其存储组织为“条目”,可以通过其应用程序值和键值进行标识。包含盐,加密数据加密密钥(EDEK),加密存储身份验证密钥(ESAK)和PIN验证码(PVC)的条目可以在应用程序值0和密钥值2下找到。找到该条目,因为它始终以十六进制字节“ 02 00 3C 00”开头。完整条目的长度为64个字节,因此结合硬件盐读取,足以完成两次成功读取(因为每次读取可以转储256个字节)。

一旦检索到盐,EDEK,ESAK和PVC,就需要从OTP字节中检索其他硬件盐。可以通过重新启用SWD接口或重新启用Bootrom引导加载程序并发出相应的读取内存命令来完成此操作。

在单核CPU上,使用Python“ pycryptodome”库,每秒可实现约85个散列的性能。可以通过利用GPU对其进行显着优化。即使以这种相对较慢的速度,也可以在不到2分钟的时间内强行使用4位数字的大头针。

Kraken发现Trezor硬件钱包中的关键缺陷(谷歌翻译)

暴力脚本示例
#!/ usr / bin / env python3

导入hashlib
导入系统
导入argparse
进口binascii
导入结构
从Crypto.Cipher导入ChaCha20_Poly1305

parser = argparse.ArgumentParser(description ='破解某些钱包。')
parser.add_argument('flash_dump',help ='要分析的闪存转储。')
parser.add_argument('– otp',help ='来自OTP的随机性,十六进制。',默认值= 44 *“ 00”)
parser.add_argument('– debug',type = bool)

args = parser.parse_args()
flash_file =打开(args.flash_dump,“ rb”)

def find_header():
而True:
数据= flash_file.read(4)
如果数据== b” \ x02 \ x00 \ x3c \ x00”:
返回
elif数据==无:
打印(“找不到标题。”)
sys.exit(1)

find_header()

#闪盐
盐= flash_file.read(4)

#EDEK + ESAK
edek = flash_file.read(48)
pvc = flash_file.read(8)

#盐计算:
#hardware_salt(来自collect_hw_entropy)
#random_salt(生成的随机缓冲区,存储在闪存中)
#ext_salt –未使用
hardware_salt = hashlib.sha256(binascii.unhexlify(args.otp))。digest()

salt_assembled =硬件盐+盐
print(f”硬件盐:{binascii.hexlify(hardware_salt)}”)
print(f”组合盐:{binascii.hexlify(salt_assembled)}”)

def trezor_pbkdf(pin,salt):
#PIN始终以1为前缀
pin_bytes = struct.pack(“ <I”,int(“ 1” + pin))
如果args.debug:
print(f” PIN字节:{binascii.hexlify(pin_bytes)}”)
dk = hashlib.pbkdf2_hmac('sha256',pin_bytes,salt,10000,dklen = 352/8)
返回dk

def chacha_enc(kek,keiv,data):
密码= ChaCha20_Poly1305.new(key = kek,nonce = keiv)
#返回DEK和TAG
返回cipher.encrypt_and_digest(数据)

def chacha_dec(kek,keiv,edek):
密码= ChaCha20_Poly1305.new(key = kek,nonce = keiv)
返回cipher.decrypt(edek)

对于范围在(1,9999)中的i:
如果i%100 == 0:
print(f”当前正在尝试:{i}”)
dk = trezor_pbkdf(str(i),salt_assembled)

#前256位是密钥加密密钥(KEK)
KEK = dk [:int(256/8)]
#剩下的96位是密钥加密初始化向量(KEIV)
KEIV = dk [int(256/8):]

如果args.debug:
print(f” KEK:{binascii.hexlify(KEK)}”)
print(f” KEIV:{binascii.hexlify(KEIV)}”)

DEK = chacha_dec(KEK,KEIV,edek)
#加密以获取TAG,很遗憾,这似乎是唯一的方法
#从Pycryptodome检索它的方法。
enc,TAG = chacha_enc(KEK,KEIV,DEK)
如果args.debug:
print(f” DEK:{binascii.hexlify(DEK)}”)
print(f” TAG:{binascii.hexlify(TAG)}”)
打印(f” PVC:{binascii.hexlify(pvc)}”)

如果TAG中的pvc:
打印(“成功”)
打印(“ PIN是:”)
打印(i)
sys.exit(0)

密钥派生

该盐由硬件盐和条目中的盐组成,与PIN结合使用(以1开头并转换为32字节的Little Endian整数),以使用PBKDF2-HMAC和SHA256和派生的362位密钥长度。结果的前256位用作密钥加密密钥(KEK),而后96位用作密钥加密初始化向量(KEIV)。

密钥验证

通过使用KEK和KEIV解密EDEK,可以从EDEK生成数据加密密钥(DEK)。使用的加密算法是带有Poly1305消息身份验证代码的ChaCha20。如果PVC的前64位与TAG的前64位相同,则PIN正确且DEK已成功恢复。

如果您看到了什么,就赚了一些
  • 如您所知,我们的安全团队始终在寻找可能对我们的客户构成威胁的漏洞。
  • 如果您有兴趣帮助我们确定平台上的潜在错误,请查看我们的 Bug赏金计划
  • 我们为每期问题支付的最低金额为100美元,对于严重威胁,可能还会支付更高的金额。

分享这个:

像这样:

喜欢 加载...