損失超過 8800 萬美元:Coldcard 硬體錢包漏洞分析與被盜資金追蹤 7 月 31 日,約 500 個 Coldcard 硬體錢包共計 594 枚 BTC 被盜,價值約 3800 萬美元。隨後負責生產 Coldcard 錢包的公司 Coinkite 確認該產品在密鑰生成環節存在安全漏洞,涉及 Coldcard Mk2、Mk3、Mk4、Q 和 Mk5 等多代產品。
目前,因此漏洞攻擊造成的損失金額已超過 8800 萬美元,且攻擊仍在持續,Coldcard 錢包用戶應盡快將資金轉移至其他地址。Beosin 對本次漏洞的分析及相關被盜資金的追蹤情況如下。
一、漏洞分析
分析 Coldcard firmware 代碼的提交記錄,可以發現在此前的 Commit 37e4af5451c260c1e7d429fe8972c4cb5e68ee59 中,開發團隊更新了多個關於 MK4 配置的代碼,在 mpconfigboard.h 中包含:
// 我們有自己的版本的此代碼。#define MICROPY_HW_ENABLE_RNG (0)
在 MicroPython STM32 端,此巨集控制預設硬體隨機數生成器(RNG)綁定與通用隨機數實作的編譯路徑。將其設為 0 會導致預設硬體 RNG 路徑不會作為通用 rng_get() 的後端使用。
其備註說明開發者會自行實現 RNG,檢查其自定義的 rng.h 發現只聲明了兩個 MicroPython 對象:
MP_DECLARE_CONST_FUN_OBJ_0(pyb_rng_get_obj); MP_DECLARE_CONST_FUN_OBJ_1(pyb_rng_get_bytes_obj);
對應的實現為:
/// \function pyb_rng_get()///// 傳回一個 30 位的硬體生成隨機數:或失敗!//STATIC mp_obj_t pyb_rng_get(void){ // 取得並傳回新的隨機數 return mp_obj_new_int(rng_get_or_fault() >> 2);}
/// \function rng_get_bytes()/// 用隨機位填滿緩衝區;呼叫者必須提供指定大小的緩衝區。STATIC mp_obj_t pyb_rng_get_bytes(mp_obj_t buffer_io) {
mp_buffer_info_t bufinfo; mp_get_buffer_raise(buffer_io, &bufinfo, MP_BUFFER_WRITE);
mp_uint_t count = bufinfo.len; if(count < 1) { mp_raise_ValueError(NULL); }
// 讀取 32 位字並解包至提供的緩衝區 random_buffer(bufinfo.buf, count);
return mp_const_none;}
MP_DEFINE_CONST_FUN_OBJ_0(pyb_rng_get_obj, pyb_rng_get);MP_DEFINE_CONST_FUN_OBJ_1(pyb_rng_get_bytes_obj, pyb_rng_get_bytes);
rng_get_or_fault() 本身讀取 STM32 RNG 外設:
static uint32_t rng_get_or_fault(void){ // 如果 RNG 外設尚未啟用,則啟用它 rng_init();
// 等待新的隨機數準備就緒,耗時約 10us uint32_t start = HAL_GetTick();
while (!(RNG->SR & RNG_SR_DRDY)) { if (HAL_GetTick() - start >= RNG_TIMEOUT_MS) { // 硬體故障... 不返回任何值! mp_raise_OSError(MP_EFAULT); } }
// 取得並返回新的隨機數 last_value = RNG->DR;
return last_value;}
這說明 Coldcard 的自定義代碼確實希望使用硬體 RNG,但它僅保證在調用 pyb_rng_get* 或其內部的 random_buffer() 時使用該硬體函數。
而在錢包創建時實際調用的是以下代碼,其中 shared/seed.py 中的錢包創建函數為:
async def make_new_wallet(nwords): # 選擇一個新的隨機種子。 await ux_dramatic_pause('Generating...', 3) seed = generate_seed() words = await approve_word_list(seed, nwords) if words: await commit_new_words(words)
該調用首先进入 Coldcard 的 shared/random.py 模組。歷史版本中,random.py 明確依賴 ngu.random,並保留:
# random.py - random 模組的子集,無兼容性,並使用密碼學級隨機數生成器# 對於位元組,請使用 ngu.random.byte(len)# bytes = ngu.random.bytes
由於錢包初始化使用的是 random.bytes,而非 pyb.rng(),因此自定義的 pyb_rng_get_obj 並未自動覆蓋 random.bytes。
由於 MICROPY_HW_ENABLE_RNG 被設為 0,錢包生成時並未使用硬體 RNG,而是調用了 micropython/ports/stm32/rng.c 的 pyb_rng_yasmarang:
#if MICROPY_HW_ENABLE_RNG
uint32_t rng_get(void) { // 使用 STM32 硬體 RNG ...}
#else
// 對於沒有 RNG 的 MCU,我們仍需提供 rng_get() 函數// 雖然偽 RNG 並非理想方案,但目前我們暫時採用。
// Yasmarang 隨機數生成器static uint32_t pyb_rng_yasmarang(void) { static bool seeded = false; static uint32_t pad = 0, n = 0; ...}
uint32_t rng_get(void) { return pyb_rng_yasmarang();}
#endif
而 pyb_rng_yasmarang 是偽隨機數生成器,作為硬體錢包種子生成非常不安全,攻擊者可透過暴力破解取得密鑰。目前,Coinkite 已在 makefile 檔案中明確排除了 stm32/rng.c:
# 不編譯 MicroPython 的備用 PRNG。板載的 rng.c 提供 rng_get(),此空物件符合上游物件清單。
$(BUILD)/rng.o: CFLAGS += -Dpyb_rng_yasmarang=error-do-not-want-this
$(BUILD)/rng.o:
$(ECHO) "SKIP stm32/rng.c"
$(Q)$(CC) $(CFLAGS) -x c -c /dev/null -o $@
二、被盜資金追蹤
目前,已有多個受害者的錢包資金被轉移並完成歸集,存放在多個地址中,尚未進行進一步的清洗。Beosin Trace 透過威脅情報及鏈上行為分析,監測到以下歸集地址:
bc1qq85v2c926eg6pgxhwp6q7lf6cnsz80qs3fcu9r(562 BTC)
bc1qx76cae2706qd5q576feh7xq8rfcsjpf2htfhe3(398.47 BTC)
此外,還有以下歸集地址,其資金流向圖與上圖類似,目前並未有進一步的轉移:
- bc1q8jy96fe5lf8vfugydnte3cguk92gpev7kwtp3q (89.62 BTC)
- bc1q0rvn88w08j75k4h48lf9fvhan7unjp7vjf5q6m(64.9 BTC)
- bc1qtfrwa4j6rmj9rsgspv6a0yjumkg39js2numu75(45.9 BTC)
- bc1qmd5m5ktv7m5ffujxv4248fxv36myvdx79n8jp6(30.18 BTC)
針對 Coldcard 錢包的攻擊仍在進行,Beosin 團隊正持續監測更多歸集地址及分析相關資金流向。
三、結語
此次 Coldcard 硬體錢包的重大安全事件源於開發團隊在隨機數生成實現上的錯誤,在關鍵的錢包種子生成環節使用了偽隨機數生成。開發團隊應對代碼進行持續且全面的測試與審計,而 Coldcard 錢包用戶應盡快轉移資產,並密切關注 Coinkite 後續的安全公告。
Beosin 是一家領先的區塊鏈安全與監管合規科技公司,專注於項目上線前的智能合約安全審計、項目運行時的安全風險監控與阻斷、盜竊追回、虛擬資產反洗錢(AML)以及調查追蹤。Beosin 已為全球 20 多個國家和地區的監管與執法機構、200 多家虛擬資產服務商以及 4500 多家 Web3 項目提供「一站式」區塊鏈合規產品 + 安全服務。

