Coldcard 硬件錢包爆出漏洞:2026 年遇襲最少 1,719 BTC 被盜

AI 市場總結
據報道,Coldcard 韌體的熵值缺陷令在預設配置的裝置上可進行離線暴力破解以復原種子,並已導致至少 1,719 BTC 在數以千計的地址中被確認盜取。事件之所以重大,是因為它將「冷存儲」的風險框架重塑為圍繞密鑰生成質量,而非網絡被入侵,或會對硬件錢包的信任造成壓力,促使緊急韌體更新,並在短期內因安全因素帶動資金流動及對 BTC 託管做法的審視加劇。
影響等級
● 高
主要受影響標的
BTC/USDT-1.73%
AI 觀點 · BTC/USDTAI 觀點
▼ 看空
立即交易
⚠️ AI觀點僅為演算法基於新聞內容的自動生成分析,不構成投資建議,不代表BingX立場。市場有風險,投資需謹慎。
作者:Johan 及 Lisa|編輯:77 【事件概況】 2026 年 7 月 30 日,鏈上多個地址出現連續轉出資金的異動;短短 41 分鐘內,1,196 個單簽地址被清空,合共約 1,082 枚比特幣失蹤。這只是第一波。至 8 月初,已確認損失至少 1,719 枚比特幣,按當時估值約 1.11 億美元,涉逾 5,200 個地址,整體攻擊分三至四波完成。 最令人費解之處在於受害錢包狀態:不少資金長期存放在冷錢包,數月甚至數年未動,問題並非出自網絡入侵,而是私鑰層面被破解。調查顯示,涉事私鑰均由 Coldcard 硬件錢包生成,用戶未額外加入骰子熵(dice entropy),亦未啟用 BIP39 passphrase,全部採用最簡單的預設設定。 按不同固件線分析影響範圍: - Mk2、Mk3(固件 4.0.1 至 4.1.9):有效熵僅約 40 bits,情況最嚴重。 - Mk4、Mk5 及 Q(部分固件版本):有效熵約 72 bits,仍落在可離線暴力破解的風險區間。 【廠商回應】 Coinkite 迅速作出處理:7 月 30 日發出警告,翌日推出修補固件,並即時停售/停止出貨受影響版本,銷毀相關庫存。更新要求如下: - Mk2、Mk3:升級至 4.2.0 或以上 - Mk4、Mk5:升級至 5.6.0 或以上 - Q:升級至 1.5.0Q 或以上 廠方亦指出,此次事件與遠端入侵或供應鏈投毒無關,攻擊者不需接觸任何實體裝置,而是透過離線暴力破解,系統性枚舉種子空間以還原私鑰。漏洞多年潛伏,直到外部研究者做出可行的暴力工具才被引爆;其後 Muan 重現了完整攻擊鏈。本文以 Mk3、固件 v4.1.9 為例,從程式碼層面重建導致大規模被盜的關鍵。 【漏洞根因:TRNG 被繞過,退化為可預測 PRNG】 Coldcard 原設計是用 STM32L475 晶片內建硬件 TRNG(真隨機數產生器)生成 seed。但固件兩個錯誤疊加,令 seed 生成實際落入軟件 PRNG,且各裝置狀態高度相近。該 PRNG 為 Yasmarang,使 Mk3 的有效熵降至約 40 bits。 錯誤一:硬件 RNG 支援被主動關閉 在 mpconfigboard.h 的 build 設定中,Coldcard 明確關閉 MicroPython 的硬件 RNG: // stm32/COLDCARD/mpconfigboard.h // 團隊已有自家 ckcc.rng_bytes,因此關閉 MicroPython 內建版本 #define MICROPY_HW_ENABLE_RNG (0) 表面看似無害,因為 Coinkite 自行實作的 ckcc.rng_bytes 的確可直接讀取 STM32 硬件 TRNG。但問題在於,libngu 的 my_random_bytes() 透過 CHIP_TRNG_32() 宏取隨機數;宏展開後改為呼叫 MicroPython STM32 port layer 提供的 rng_get(),並假設其為硬件 TRNG。當 MICROPY_HW_ENABLE_RNG 被設為 0,rng_get() 會靜默退化,直接引出第二個錯誤。 錯誤二:把 rng_get() 的 fallback 當成硬件 TRNG 使用 追蹤 libngu random.c 的宏展開後,最終會落到 MicroPython 的 ports/stm32/rng.c 的 else 分支: // external/libngu/ngu/random.c #ifdef MICROPY_PY_STM // ports/stm32/rng.c extern uint32_t rng_get(void); # define CHIP_TRNG_SETUP() # define CHIP_TRNG_32() rng_get() #endif ... void my_random_bytes(uint8_t *dest, uint32_t count) { uint32_t chip = CHIP_TRNG_32(); // 假設是硬件 TRNG,實際可能是 Yasmarang 輸出 if (chip == last) ... chip ^= my_yasmarang(); ... } rng_get() 會依 #if MICROPY_HW_ENABLE_RNG 決定分支: - 若宏為非零:讀取 RNG>DR(硬件 TRNG) - 若宏為 0:編譯進 fallback,返回軟件 PRNG 輸出 Coldcard 的設定正是 0,令所有出廠 Mk3 走向 fallback。該 fallback 的 seed 來源高度可預測: - pad = UID ^ SysTick>VAL - n = RTC>TR - d = RTC>SSR 其中 SysTick 的 VAL 在 80MHz、reload 80,000 的情況下,落在 0 至 79,999,約 17 bits 熵;RTC>TR 與 RTC>SSR 是上電後、應用層初始化前的 RTC 暫存器值,取值空間很小。研究者在已核實的鏈上命中樣本中,RTC>TR 與 RTC>SSR 皆為 0。 綜合估算:UID 約提供 14 bits、SysTick 約 17 bits、兩個 RTC 項目約 0 bits,再加上按鍵操作帶來約 5 bits,整體真熵約 36 至 37 bits。Coinkite 所稱「約 40 bits」與逆向所得的搜索空間吻合,意味著 GPU 叢集可在數天內完成暴力破解。 【攻擊流程:重建 PRNG 消耗路徑,離線枚舉 seed】 在 Mk3 首次開機從程式碼到私鑰的過程中,隨機數被消耗的模式大致分三類。攻擊者的核心是為每一類模式建模,精準重演 PRNG 每一步狀態。 階段一:上電初始狀態 裝置上電後立即形成兩條狀態來源: - libngu 的全域 Yasmarang 常數流(ngu/random.c):pad=0x0a8ce26f、n=69、d=233、dat=0 - 第一次呼叫 rng_get() 的 seeding(ports/stm32/rng.c):pad=UID^SysTick、n=RTC_TR=0、d=RTC_SSR=0、dat=0 此時所謂「隨機性」被壓縮為 32-bit pad 再加兩個可枚舉的小維度,令整體空間可被窮舉。 階段二:按鍵操作導致的狀態推進 首次設定必須設 PIN、按 OK 同意條款並在選單操作。每次按鍵會觸發 shared/mempad.py 的 _start_scan(),其透過 shuffle(self.scan_order) 進一步呼叫 _rand_below() 多次。scan_order 長度為 NUM_ROWS=4;shuffle 為 Fisher-Yates,randbelow 直接綁定到 libngu 的 ngu.random.uniform(C 層 _rand_below)。 研究 v4.1.9 原始碼後,可歸納三種消耗模式: Profile A(一般零售首次設定) - 同意條款前按兩次(kpad_a=2):每次觸發 shuffle(4) - settings.save() 在 32 個 slot(排除 my_pos)中找空位:等同 30 次 _rand_below - 輸入 PIN 與選單操作:kpad_b 枚舉範圍約 4 至 34(每次按鍵對應 shuffle(4)) - 最後 random_bytes(32) 收集熵 Profile B(刷機/清除後首次開機、NVRAM 為空) - nvstore 先做一次 shuffle(32)(31 次 _rand_below) - 接著以 3 slots × 16 blocks × 256 bytes 方式鎖步消耗共 3072 步,僅推進狀態不回饋輸出 - 空 NVRAM onboarding 會再做一次 shuffle(32) - 然後進入按鍵消耗(kpad_b 約 4 至 34) - 最後 my_random_bytes(32) Profile C(紙錢包 Paper Wallet) 紙錢包用作離線保存私鑰並可列印。Coldcard 的 Paper Wallets 選單在既有錢包建立後,再次進入需按 8 至 25 次按鍵,之後直接以 my_random_bytes(32) 作為私鑰,完全繞過 BIP39 詞表流程,是最直接的熵消耗路徑。 階段三:由 random_bytes(32) 到地址的可重現管線 Mk3 從 random_bytes(32) 生成最終比特幣地址的步驟為: raw_bytes = random_bytes(32) entropy = ngu.hash.sha256s(raw_bytes) // 單次 SHA256 mnemonic = BIP39(entropy, wordlist=english) // 24 字 seed = PBKDF2HMACSHA512(mnemonic, "mnemonic", 2048) master = HMACSHA512("Bitcoin seed", seed) child = m/{44,49,84}'/0'/0'/0/0 address = bech32(hash160(compressed_pubkey)) // 預設 BIP84 上述全為確定性函數。只要攻擊者猜中 pad 及按鍵次數等參數,即可離線重算整條鏈,推導同一地址與私鑰。 階段四:GPU 暴力破解與鏈上命中 攻擊方法可概括為四步: 1)建立候選 pad 集:把晶圓座標 X、Y(0 至 72)與約 5,300 個 UID 值組合,再與 SysTick(0 至 79,999)做笛卡兒積,得到約 4.24 億個候選 pad。規模雖大,但 GPU 在數天內可跑完。 2)對每個 pad 枚舉按鍵次數:kpad_b 取 4 至 34。由於 rtc_tr 與 rtc_ssr 命中均為 0,可作為外層枚舉但實際負擔很低。 3)在 GPU kernel 內跑完整 seed 管線:SHA256、BIP39、PBKDF2(2048 輪)、BIP32、Hash160、Bech32。libngu 的常數 PRNG 流可離線預計算,kernel 只按索引取值,降低每 thread 推進常數流的開銷。 4)以 Bloom filter 或排序後的 hash160 陣列做匹配(O(log n)),目標為全網單簽 P2WPKH 地址集合。 成本評估方面:以單顆 Apple M1 GPU 跑 Profile A 搜索空間(72×72 UID、80,000 個 SysTick、31 個按鍵次數),約 148 億候選,耗時約 8.6 日;若使用數據中心級 A100 叢集,可把時間壓縮至數小時。 至於 Mk4、Mk5 及 Q 的約 72-bit 空間,較 Mk3 大約 2^32 倍,但漏洞同源、建模方法一致,只是暴力成本由「單機數天」變為「叢集數週」。在具備資源與動機的攻擊者眼中,成本仍可接受,亦解釋了其同樣出現在受害名單之中。