【译】 Stealing the funds of all HTC EXODUS 1 users (HTC 区块链钱包安全漏洞分析)_Omni-Space的博客-程序员秘密

技术标签: 区块链钱包  Shamir Secret Sharing  安全  区块链研究院  系统安全与漏洞分析  区块链 / 安全  

HTC EXODUS 1手机带有集成的硬件钱包。该钱包允许通过拆分并将其发送给“受信任的联系人” 来备份其主种子。通常需要三个受信任的联系人来重建整个种子。我们表明,任何受信任的联系人或破坏了该受信任联系人的电话的攻击者都可以收回全部种子并窃取EXODUS 1所有者的所有资金。如果在2019年4月之前使用社交密钥恢复功能,我们强烈建议HTC EXODUS 1用户将其资金转移到另一种子。

介绍

在2018年,HTC推出了其首款面向区块链的智能手机EXODUS 1。与其他智能手机相比,它具有硬件钱包功能,可将主种子存储在安全的区域中。这样可以确保即使具有root特权的攻击者也无法访问主种子-它在安全区域内被加密。

图1:HTC EXODUS 1设备

 

我们对此款(硬件)钱包特别感兴趣,因为它提供了一个不错的功能:Social Key Recovery。在此博客文章中,我们将重点介绍EXODUS 1的特定功能。

它包含一个原始机制,可以强制执行种子的备份。种子被分成五份,每份被发送到受信任的联系人。如果用户丢失了手机,他们将可以通过请求其五个受信任联系人中的三个来传播种子来重建种子。份额数(5)和阈值(3)是固定的。

我们将首先提供有关实施社交密钥恢复的更多详细信息。然后,我们将介绍两种攻击方法:

  • 第一个演示了如何将阈值从三个可信联系人降低到两个。
  • 第二个示例演示了如何将阈值从三个受信任的联系人降低到一个,这意味着您的任何一个受信任的联系人都可以检索主种子并访问您的资金。

社交密钥恢复

主种子备份是硬件钱包用户的常见问题。仅从该种子生成每个用户机密。必须备份该种子,以确保您的钱包丢失并不意味着您的秘密丢失:可以从备份的种子将其恢复到新的钱包中。

如何备份种子?大多数硬件钱包都会提出一份纸质回收表(图3),用户必须在纸上写下其BIP39助记符(助记符是将您的种子表示为人类可读的单词的一种方式)。但是,要保证此纸张的安全性并非易事,因此为此设计了一些专用设备(图2)。例如,可以使用加密钢来防止助记符种子恶化。

图2:Cryptosteel-备份种子的设备

 

另一种解决方案是拥有一个备份硬件钱包,并用相同的种子初始化。但是,没有完美的解决方案可以解决所有问题。

图3:总帐回收表

 

图4:实践中的恢复表存储

 

HTC EXODUS 1带有自己的备份机制:社交密钥恢复。用户的种子被分成共享,并发送给受信任的联系人。1或2 的知识不会带来有关种子的任何信息。3 的唯一知识可以重建完整的种子。在该方案中,主种子永远不会在单个位置完全备份。

HTC硬件钱包采用名为Zion的Android应用程序的形式,以及存储种子并执行敏感操作的trustlet(在智能手机安全OS中执行的安全应用程序)的形式(图5)。秘密共享也在trustlet中计算:在下面,研究的机制在安全OS中实现

图5:Zion-体系结构概述

 

Shamir’s Secret Sharing

 

此问题可以通过以下方法解决:

  • 安全地存储多项式系数,以便以后可以恢复它们以生成其他份额,
  • 或仅在拆分前保持PRNG状态。

HTC使用的SSS实现受一个开源项目的启发,可在此处获得。此开源实现一次生成所有共享。一个人不能要求一个份额。为了允许随意添加受信任的联系人,HTC修改了实现,但牺牲了安全性。

HTC选择保留PRNG种子。但是该实现还使用了DRBG:这可确保输出是可预测的,并且生成的系数将始终相同。DRBG使用的种子(即PRNG状态)存储在加密分区内,仅可用于安全OS

随机数生成器:

原始实现的RNG(不确定的)已由以下功能代替:

#define RANDOM_POOL_SIZE 128

static uint8_t random_pool[RANDOM_POOL_SIZE];

size_t sss_rand(uint8_t *data, size_t len) {
  if (len == 0) {
    return 0;
  }

  while (len > RANDOM_POOL_SIZE) {
    memcpy(data, random_pool, RANDOM_POOL_SIZE);
    data += RANDOM_POOL_SIZE;
    len -= RANDOM_POOL_SIZE;
  }
  memcpy(data, random_pool, len);
  return len;
}

PRNG仅返回随机缓冲区的内容。此128字节的缓冲区由函数手动更新sss_update_secure_random_buffer

void sss_update_secure_random_buffer(const uint8_t *entropy, size_t size) {
  SHA256_CTX ctx;
  uint8_t digest[SHA256_DIGEST_LENGTH];
  uint8_t *p = random_pool;

  sha256_Init(&ctx);
  sha256_Update(&ctx, entropy, size);
  sha256_Final(digest, &ctx);

  for (int i = 0; i < 4; i++) {
    memcpy(p, digest, SHA256_DIGEST_LENGTH);

    sha256_Init(&ctx);
    sha256_Update(&ctx, p, SHA256_DIGEST_LENGTH);
    sha256_Final(digest, &ctx);
    p += SHA256_DIGEST_LENGTH;
  }
}

我们可以看到,作为输入参数传递给此函数的熵完全确定了PRNG的内部状态,因此也确定了PRNG的输出。正如我们之前所解释的,此行为是HTC想要的。该熵来自智能手机TRNG,其值由返回qsee_prng_getdata。使用128位熵并将其存储在加密分区中。

知道PRNG的输出足以完全确定整个随机输出序列。例如,如果我们知道返回的前32个字节,那么我们知道接下来的字节将对应于这些字节的SHA-256,然后对应于该值的SHA-256,依此类推……此外,PRNG的周期非常短128个字节。

但是,PRNG缺乏鲁棒性对我们的攻击无济于事:将使用PRNG的状态在两次调用之间固定的事实。如果两次之间的未调用,sss_rand则两次调用将始终返回相同的值sss_update_secure_random_buffer

HTC Social Key Recovery Shares计算

共享秘密是钱包的种子,用于导出每种加密货币的所有密钥。但是该实现增加了一个加密层来保护秘密。选择的密码是基于Salsa20和Poly1305(与TweetNaCl相同)的经过身份验证的流密码。

Fig. 6: The bitslice function

 

/*
 * Create `n` shares with theshold `k` and write them to `out`
 */
void sss_create_shares(sss_Share *out, const unsigned char *data,
                       uint8_t n, uint8_t k)
{
  unsigned char key[32];
  unsigned char m[crypto_secretbox_ZEROBYTES + sss_MLEN] = { 0 };
  unsigned long long mlen = sizeof(m); /* length includes zero-bytes */
  unsigned char c[mlen];
  int tmp;
  sss_Keyshare keyshares[n];
  size_t idx;

  /* Generate a random encryption key */
  sss_rand(key, sizeof(key));
...

  /* Generate KeyShares */
  sss_create_keyshares(keyshares, key, n, k);
...

Here is the sss_create_keyshares code :

/*
 * Create `k` key shares of the key given in `key`. The caller has to ensure
 * that the array `out` has enough space to hold at least `n` sss_Keyshare
 * structs.
 */
void
sss_create_keyshares(sss_Keyshare *out,
                     const uint8_t key[32],
                     uint8_t n,
                     uint8_t k)
{
...

  uint8_t share_idx, coeff_idx, unbitsliced_x;
  uint32_t poly0[8], poly[k-1][8], x[8], y[8], xpow[8], tmp[8];

  /* Put the secret in the bottom part of the polynomial */
  bitslice(poly0, key);

  /* Generate the other terms of the polynomial */
  sss_rand((void *)poly, sizeof(poly));
...

sage: load("rebuild_secret.py")
0102030405060708090a0b0c0d0e0f100102030405060708090a0b0c0d0e0f100a2b7065ad61a3ca403d62f61b21fabbab4de9811b3d2ce55c847488f231bf4e
Recovered 1 secret in 0.111693s

我们刚刚展示了如何降低到22重建机密所需的份额数(而不是33)。除了五分之三的阈值不再有效这一事实外,我们仍然认为安全威胁是不可忽略的。理想情况下,用户应将种子分成5个5彼此不认识的可信任联系人。实际上,使用55 受信任的联系人,即使其中一些彼此认识。

借助这种攻击,恶意联系人只需说服(或折衷)其他信任的联系人即可完全收回种子并访问资金。

打破机制

受先前攻击影响的固件如下(使用了欧洲ID):

  • 固件1.47.2401.2,它似乎是初始固件;
  • 固件1.53.2401.2,于2019-12-18。

2019年2月19日,发布了第三个固件。

通过研究这一点,我们非常惊讶地注意到sss_update_secure_random_bufferPRNG初始化函数从未被调用过。PRNG始终返回相同的值:其熵缓冲区,以固定值初始化(可能通过测试向量验证)。我们认为,trustlet已使用测试选项进行编译,而该选项应永远不会在生产中使用。结果,用于加密种子的密钥是固定的。由于此密钥已发送给每个联系人,因此任何人都可以解密种子并访问资金。

<span style="color:#514134"><span style="color:#2e2925"><code>secret_key <span style="color:#000000"><strong>=</strong></span> b<span style="color:#dd1144">"</span><span style="color:#dd1144">\x0e\x74\xcd\x69</span><span style="color:#dd1144">..."</span>
box <span style="color:#000000"><strong>=</strong></span> nacl<span style="color:#000000"><strong>.</strong></span>secret<span style="color:#000000"><strong>.</strong></span>SecretBox(secret_key)
nonce <span style="color:#000000"><strong>=</strong></span> b<span style="color:#dd1144">"</span><span style="color:#dd1144">\x00</span><span style="color:#dd1144">"</span> <span style="color:#000000"><strong>*</strong></span> <span style="color:#009999">24</span>
encrypted_seed <span style="color:#000000"><strong>=</strong></span> share1[<span style="color:#009999">1</span> <span style="color:#000000"><strong>+</strong></span> <span style="color:#009999">32</span>:]
seed <span style="color:#000000"><strong>=</strong></span> box<span style="color:#000000"><strong>.</strong></span>decrypt(nonce <span style="color:#000000"><strong>+</strong></span> encrypted_seed)[:<span style="color:#009999">16</span>]
<span style="color:#000000"><strong>print</strong></span>(mnemonic<span style="color:#000000"><strong>.</strong></span>Mnemonic(<span style="color:#dd1144">'english'</span>)<span style="color:#000000"><strong>.</strong></span>to_mnemonic(seed))
</code></span></span>

结论

负责任的披露

我们于2019.02.15向HTC Exodus披露了所有上述缺陷。

两个月后,还披露了其他漏洞(触摸屏驱动程序内部,受信任的UI内部以及ETH / BTC事务解析中的内存损坏)。HTC安全团队已经找到并修复了它们。

2019年3月5日,HTC Exodus团队在巴黎并借此机会访问了我们。他们甚至有机会进入Donjon。

HTC 在2019.03.25发行了新固件(1.62.2401.7)解决了所有这些问题。SSS修补程序包括使用可靠的PRNG,并将每个生成的共享保存在安全存储中。每当添加新的受信任联系人时,都会使用这些共享。

HTC Exodus于2019.04.05开始为Zion Hardware Wallet设立赏金计划。

在这些讨论之后,HTC向我们表明,此漏洞的披露触发了他们自己赏金计划的创建。当我们在建立赏金计划之前报告了这些错误时,我们没有得到任何赏金,但是当他们访问我们时,我们得到了出埃及记衬衫和贴纸。:) 非常感谢!

带走

我们研究了HTC Exodus 1手机的硬件钱包,并发现了社交密钥恢复机制上的两个关键漏洞。在攻击者能够在任何Zion信任联系人的Android手机上执行代码(Android漏洞,常规Android应用)的情况下,他可能会窃取相应EXODUS 1所有者的资金。或者,受信任的联系人可以直接访问种子。这些漏洞已得到正确修补。

不过,我们强烈鼓励所有使用社交密钥恢复来更改种子(并转移其资金)的EXODUS 1用户。确实,他们的种子可能早些受到破坏,或者仍然可以通过不会更新Zion的受信任联系人而受到破坏。

 

 

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/omnispace/article/details/103232595

智能推荐

Rancher 2.1平台搭建及使用_tldesign3d_w844916072的博客-程序员秘密

Rancher 2.1平台搭建及使用Posted on2019-10-15 13:46Mr.智 阅读(11696) 评论(1)编辑收藏目录一、概述 1.1、什么是Rancher 1.2、Rancher架构 1.2.1、Docker简述 1.2.2、Kubernetes简述 1.2.3、Rancher架构 二、相...

数据库Oracle通用函数_有园人的博客-程序员秘密

通用函数:可用于任意数据类型,并且适用于空值。• NVL (expr1, expr2)• NVL2 (expr1, expr2, expr3)• NULLIF (expr1, expr2)• COALESCE (expr1, expr2, ..., exprn)NVL(expr1, expr2) 函数:转换一个空值到一个实际的值。expr1,expr2:可用的数据类型可以是日期...

C语言实现链表之双向链表(十二)判断链表是否为空和获取链表长度_君临丶天下的博客-程序员秘密

C语言实现链表之双向链表(十二)判断链表是否为空和获取链表长度    上一篇文章给出了设置结点数据与获取结点数据的两个函数,本篇文章将给出判断链表是否为空和获取链表长度的函数,共两个函数。/*============================================================================== * 操作 :检

python数据分析与挖掘实战 之笔记2_潇洒佳爷的博客-程序员秘密

《python数据分析与挖掘实战》学习笔记2经过前面章节的分析,即对数据进行探索和预处理,得到了处理后的数据。根据所得到的数据建立分类与预测、聚类分析、关联规则、时序模式、和偏差检测等模型,提取数据中蕴含的有价值的信息。下面就将对这部分知识做大致的介绍。1、分类与预测1.1 回归分析本小节只对二分类Logistic回归模型的相关原理进行介绍:(1)该模型的因变量只有0-1两个取值,...

Go语言并发之道学习-----基本概念_go语言并发之道学习十五_alvin_666的博客-程序员秘密

并发概述并发:通常指的是一个或多个进程同时发生的过程1、竞争条件:当两个或多个操作必须按正确的顺序执行,而程序并未保证顺序var data intgo func(){ data++}()if data == 0{ fmt.Printf("The data:%d\n",data)}这段代码可能会有三种情况不打印打印 The data:0打印 The data:1因此代...

Linux系统通过远程终端修改分辨率_ssh登录屏幕分辨率_zifehng的博客-程序员秘密

最近的工作是测试服务器的板载GPU,由于一切处于开发阶段,连USB驱动都没有,无法通过键鼠操作,切换桌面分辨率成了一项难题。用谷歌、用百度,总算找到了一个在远程终端修改分辨率的方法,在此记录:修改DISPLAY环境变量由于通过远程终端登录,DISPLAY环境变量不是指向GPU输出的终端,我们可以手动修改DISPLAY环境变量,以达到在当前远程终端上控制GPU输出的目的,在单显卡单显示器的场...

随便推点

KEIL C51报错 UNCALLED SEGMENT_宋哥的博客-程序员秘密

<br />*** WARNING L16: UNCALLED SEGMENT, IGNORED FOR OVERLAY PROCESS<br />    SEGMENT: ?CO?CJM<br />该警告表示存在定义了未调用的函数,但是找了半天也未发现,虽然可以不管他,但总觉得有个问题没解决,后来仔细查找,才发现是我定义了代码段常量没有使用造成的。即下面的代码:<br />//定义液晶显示数字<br />code const unsigned char ShowTable[10]={0x88,0xbb,0

zookeeper启动失败的排错_Username_Password_R的博客-程序员秘密

1.执行zkServer.sh start 后显示:JMX enabled by defaultUsing config:/home/hadoop/app/zookeeper-3.4.5/bin/../conf/zoo.cfgStarting zookeeper ... STARTED2.jps后发现没有QuorumPeerMain进程3.查看zookeeper-3

Nginx与Redis解决高并发问题_nginx redis_dizzthxl的博客-程序员秘密

原文链接:http://bbs.phpchina.com/forum.php?mod=viewthread&tid=229629第一版产品采用的是Jquery,Nginx,PHP(CI框架),Memcache,Mysql这种常用的架构。作为一名PHP工程师对于这种架构已经非常的熟悉了,目前站点并发并不是很高,线上环境使用的是阿里云主机,1.5G的内存,PHP并发能支持400~500左右

KenLM 在Ubuntu18.04上安装出现的一个错误_董慕秋的博客-程序员秘密

前言:最近在做任务时用到KenLM (KenLM Language Model Toolkit)一开始在公司服务器上安装,根据官方的安装向导来,很顺利。完成任务后,由于疫情影响一直远程办公,操作大量的数据倒腾不方便,就想着在自己ubuntu 18.04上安装一下 KenLM,结果在“cmake …”就出错了。官方安装如下:wget -O - https://kheafield.com/cod...

java客户端关闭链接服务端_java – 在Netty中关闭客户端连接_踢踢番茄的博客-程序员秘密

我有简单的Netty客户端/服务器应用程序.在服务器端,我检查客户端是否从正确的主机连接,如果它不正确,请关闭客户端的连接.在服务器端我使用此代码:@Overridepublic void channelActive(ChannelHandlerContext ctx) throws Exception {String remoteAddress = ctx.channel().remoteAdd...

如何使用GSS7000测试ublox接收机设置 北斗模式 EVK M8N_evk_m8n gnss_东方V550的博客-程序员秘密

最近买了一块大名鼎鼎的 Ublox公司的 GNSS 定位模块, 需要接一下测试它的功能。拿到手还是比较明确的,USB转micro-usb 接到电脑和 设备,同时给设备供电。 给设备接上GPS天线。安装好驱动,打开软件U-Centor。 选择好对应的串口输出即可。...

推荐文章

热门文章

相关标签