Coldcard Default Seed Bug Exploited: 1,719 BTC Drained Across 5,200+ Addresses in 2026

AI مارکیٹ کا خلاصہ
A reported Coldcard firmware entropy flaw enabled offline brute-force recovery of seeds for default-configured devices, leading to confirmed theft of at least 1,719 BTC across thousands of addresses. The incident is significant because it reframes "cold storage" risk around key-generation quality rather than network compromise, potentially pressuring hardware-wallet trust, prompting urgent firmware updates, and increasing near-term security-driven fund movements and scrutiny across BTC custody practices.
اثر کی سطح
● ہائی
متاثرہ اثاثے
BTC/USDT-1.03%
AI تجزیاتی سمجھ · BTC/USDTAI تجزیاتی سمجھ
▼ Bearish
ابھی ٹریڈ کریں
⚠️ AI سے تیار کردہ تجزیاتی سمجھ خبروں کے مواد پر مبنی ہے اور صرف معلوماتی مقاصد کے لیے فراہم کی گئی ہے۔ یہ سرمایہ کاری کا مشورہ نہیں ہے اور نہ ہی BingX کے خیالات کی نمائندگی کرتی ہے۔ سرمایہ کاری میں رسک شامل ہے۔ براہ کرم ذمہ داری سے ٹریڈ کریں۔
Author: Johan & Lisa | Edit: 77 A large-scale Bitcoin theft tied to Coldcard hardware wallets came to light after a coordinated on-chain sweep on July 30, 2026. Over a 41-minute window, attackers emptied 1,196 single-signature addresses, removing about 1,082 BTC. That was only the opening round. By early August, confirmed losses had risen to at least 1,719 BTC (about $111 million), spanning more than 5,200 addresses, with the drains unfolding in three to four distinct waves. Investigators noted an unusual commonality: many of the affected UTXOs had been sitting in cold storage, untouched for months or years. The failure point was not device access but private-key generation. Every compromised wallet used seeds generated by Coldcard devices where owners kept the simplest default setup—no added dice entropy and no BIP39 passphrase. Impact depends on device generation and firmware line. For Mk2 and Mk3 running firmware 4.0.1 through 4.1.9, researchers estimate effective entropy at roughly 40 bits, the most severe case. For Mk4, Mk5, and Q on certain firmware versions, the effective entropy is around 72 bits—still within practical range for offline brute force using sufficient compute. Coinkite moved quickly after the first reports. On July 30, it issued a warning. On July 31, it released patched firmware: Mk2/Mk3 must upgrade to 4.2.0+; Mk4/Mk5 to 5.6.0+; and Q to 1.5.0Q+. The company also destroyed inventory of affected versions and paused shipments. The incident is not attributed to remote compromise or supply-chain tampering. Attackers did not need physical access to devices. Instead, they recovered private keys by brute forcing the seed space offline—systematically testing candidate seeds until derived addresses matched known on-chain targets. The flaw had existed for years but became actionable once external researchers produced effective brute-force tooling; Muan later reproduced the full end-to-end attack chain. Root cause: TRNG sidelined, seed generation collapses into a shared software PRNG state Coldcard is intended to generate seeds using the STM32L475's built-in hardware TRNG. In practice, two firmware issues combined to route randomness through a software PRNG (Yasmarang) whose initial state was near-identical across devices. On Mk3, this reduced effective entropy to about 40 bits. Mistake 1: Hardware RNG support was explicitly disabled In the build configuration, Coldcard disables MicroPython's hardware RNG support in mpconfigboard.h: // stm32/COLDCARD/mpconfigboard.h // The team has implemented their own version of ckcc.rng_bytes, so disable MicroPython's builtin version #define MICROPY_HW_ENABLE_RNG (0) Coinkite does provide ckcc.rng_bytes that can call the STM32 hardware TRNG directly. The problem is that other parts of the stack still depend on MicroPython's RNG plumbing via libngu. Mistake 2: rng_get() fallback was treated as true hardware entropy libngu's my_random_bytes() reads random words via CHIP_TRNG_32(), which expands into rng_get() from MicroPython's STM32 port layer. When MICROPY_HW_ENABLE_RNG is 0, rng_get() compiles a fallback path that returns Yasmarang output rather than true hardware randomness: // 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(); // Assumes reading from hardware TRNG, but may actually receive Yasmarang output if (chip == last) ... chip ^= my_yasmarang(); ... } // external/micropython/ports/stm32/rng.c #if MICROPY_HW_ENABLE_RNG uint32_t rng_get(void) { ... read RNG>DR hardware TRNG ... } #else // Vulnerability point: fallback exists, and seed is almost entirely predictable static uint32_t pyb_rng_yasmarang(void) { static bool seeded = false; static uint32_t pad = 0, n = 0, d = 0; static uint8_t dat = 0; if (!seeded) { seeded = true; rtc_init_finalise(); pad = *(uint32_t *)MP_HAL_UNIQUE_ID_ADDRESS ^ SysTick>VAL; // pad = UID ^ SysTick n = RTC>TR; // n = RTC_TR d = RTC>SSR; // d = RTC_SSR } ... } #endif The seeding material is thin. SysTick>VAL at first call ranges from 0 to 79,999 on an 80 MHz setup (about 17 bits). RTC>TR and RTC>SSR are read immediately after power-on, before application initialization; in all verified on-chain hit vectors, both values were 0. UID contributes roughly 14 bits, button-press timing contributes around 5 bits, putting the seed-generation entropy around 36–37 bits in practice. Coinkite's "about 40 bits" estimate matches what reverse engineering implies: a search space that a GPU cluster can exhaust in days. How the brute-force attack models device behavior On Mk3 first boot, random consumption follows repeatable patterns. The attacker's main job is to model each consumption profile and replay Yasmarang state transitions exactly. Phase 1: Initial state on power-up Two states are established immediately: Power on └── libngu static Yasmarang (ngu/random.c) pad=0x0a8ce26f n=69 d=233 dat=0 ← Global constant └── rng_get() initial call seeding (ports/stm32/rng.c) pad=UID^SysTick n=RTC_TR=0 d=RTC_SSR=0 dat=0 Randomness collapses largely into a 32-bit pad plus small enumerated dimensions. Phase 2: Button-driven state advances During onboarding, every keypress triggers _start_scan() in shared/mempad.py, which shuffles a 4-item scan_order list. That shuffle consumes RNG steps through randbelow(), bound to libngu's ngu.random.uniform: # shared/random.py import ngu randbelow = ngu.random.uniform bytes = ngu.random.bytes def shuffle(lst): for i in reversed(range(1, len(lst))): j = randbelow(i + 1) lst[i], lst[j] = lst[j], lst[i] # shared/mempad.py # Each key press > _start_scan() > shuffle(self.scan_order) # scan_order length = NUM_ROWS = 4 Source review of Mk3 firmware v4.1.9 identified three common consumption profiles: Profile A (typical first-time retail setup) - Two keypresses before accepting terms - settings.save() searches empty slots (31-slot shuffle = 30 calls to _rand_below) - Additional keypresses for PIN entry and menu navigation (kpad_b enumerated from 4 to 34) - Finally random_bytes(32) to gather entropy Profile B (first boot after flashing/wiping with empty NVRAM) - nvstore shuffle(32) (31 calls) - nvstore blanking advances state in lockstep across 3 slots × 16 blocks × 256 bytes (3072 steps), with output not fed back - Another shuffle(32) - Keypress consumption (kpad_b from 4 to 34) - my_random_bytes(32) Profile C (paper wallet flow) After a wallet already exists, re-entering the Paper Wallets menu typically involves 8–25 button presses and then uses my_random_bytes(32) directly as the private key, bypassing the BIP39 word list. Phase 3: Deterministic seed-to-address pipeline Once raw bytes are produced, the derivation is fully deterministic and standard: raw_bytes = random_bytes(32) entropy = ngu.hash.sha256s(raw_bytes) # single SHA256 mnemonic = BIP39(entropy, wordlist=english) # 24 words seed = PBKDF2HMACSHA512(mnemonic, "mnemonic", 2048) master = HMACSHA512("Bitcoin seed", seed) child = m/{44,49,84}'/0'/0'/0/0 address = bech32(hash160(compressed_pubkey)) # default BIP84 With a correct guess for the initial pad (and modeled consumption), an attacker can recompute the entire chain offline and test for address matches. Phase 4: GPU brute force at scale The reported approach is computationally heavy but conceptually straightforward: 1) Build candidate pad sets by combining wafer coordinates (X, Y from 0 to 72), a UID space of about 5,300 values, and SysTick values from 0 to 79,999, producing about 424 million candidate pads. 2) Enumerate keypress counts (kpad_b from 4 to 34). Since rtc_tr and rtc_ssr hit as zero, they can be fixed or placed in a minimal loop. 3) Run full derivation inside GPU kernels: SHA256, BIP39, PBKDF2 (2048 rounds), BIP32, HASH160, and Bech32. The constant libngu stream can be precomputed; kernels index into it instead of stepping per thread. 4) Match derived targets using a Bloom filter or a sorted HASH160 set with O(log n) lookups, focusing on single-signature P2WPKH addresses. Performance estimates included a reference run of the Phase A space on an Apple M1 GPU: roughly 14.8 billion candidates (72×72 UID, 80,000 SysTick values, 31 keypress counts) processed in about 8.6 days. With data-center GPUs such as an A100 cluster, the same search compresses into hours. For Mk4/Mk5/Q, the ∼72-bit effective entropy space is about 2^32 larger than Mk3's, but the flaw has the same underlying cause and uses the same profiling logic. The difference is the compute budget: brute forcing shifts from a single machine to a cluster and from days to weeks, a cost attackers still appear willing to bear, consistent with these models appearing on victim lists as well.