【譯】 Stealing the funds of all HTC EXODUS 1 users (HTC 區塊鏈錢包安全漏洞分析)

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的受信任聯繫人而受到破壞。

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章