Об уязвимостях холодного кошелька Coldcard не писали только самые ленивые в криптоиндустрии. Я разобрал тему, стало интересно можно ли поучаствовать и скажу вам да, можно. Многие ИИ объясняли как используется эта уязвимость, поделюсь с вами. Это учебная статья, объясняющая принцип работы уязвимости.
1. Суть уязвимости (The Root Cause)
Причина уязвимости кроется не в дефекте протокола Bitcoin или стандарта мнемонических фраз BIP-39, а в синтаксической ошибке интеграции криптографической библиотеки в прошивку MicroPython (начиная с версии 4.0.1 в марте 2021 года).
Сбой в логике макроса C
При обновлении прошивки проверка активности аппаратного генератора истинно случайных чисел (Hardware TRNG) в коде C была реализована следующим образом:
Конструкция #ifndef проверяет факт определения директивы препроцессора, но не её логическое значение. Переменная была объявлена как MICROPY_HW_ENABLE_RNG = 0 (выключена). Так как флаг формально существовал в коде, проект успешно компилировался без ошибок.
В результате аппаратный TRNG микроконтроллера тихо отключался во время работы устройства.
Падение до предсказуемого PRNG
Не получая физического шума от TRNG, система генерации сид-фраз переключалась на программный генератор псевдослучайных чисел (PRNG), инициализация (seeding) которого выполнялась на основе внешних параметров системы:
View attachment 25233
Использование системного таймера и идентификаторов процессора взамен физической случайности снизило теоретическую стойкость ключей с $2^{256}$ степеней до локального пространства значений, доступного для прямого вычисления.
Сбой в логике макроса C
При обновлении прошивки проверка активности аппаратного генератора истинно случайных чисел (Hardware TRNG) в коде C была реализована следующим образом:
Code:
// Логическая ошибка компиляции:
#ifndef MICROPY_HW_ENABLE_RNG
#error "get a HW TRNG plz"
#endif
В результате аппаратный TRNG микроконтроллера тихо отключался во время работы устройства.
Падение до предсказуемого PRNG
Не получая физического шума от TRNG, система генерации сид-фраз переключалась на программный генератор псевдослучайных чисел (PRNG), инициализация (seeding) которого выполнялась на основе внешних параметров системы:
View attachment 25233
Использование системного таймера и идентификаторов процессора взамен физической случайности снизило теоретическую стойкость ключей с $2^{256}$ степеней до локального пространства значений, доступного для прямого вычисления.
Для реализации брутфорса злоумышленникам требовалось перебрать комбинации исходных данных PRNG. Теоретически 96-битный серийный номер микроконтроллера (Chip UID) и счетчик времени должны были давать высокую стойкость, однако каскад инженерных особенностей сжал пространство поиска.
1. Брокерский эффект производственных партий (Word 2)
Коммерческие партии чипов закупаются производителем ограниченными сериями. Для устройств, выпущенных в одном временном окне, значение Word 2 на фабрике является фиксированным или варьируется в узком диапазоне чисел.
2. Физические границы нарезки пластин (Word 0 & Word 1)
Номер пластины (Word 1) ограничен кассетой на 25 штук. Координатная сетка нарезки кристалла (Word 0) ограничена физическим размером 300-мм кремниевой пластины ($\approx 10\,000 \dots 50\,000$ валидных точек).
3. Ошибка приведения типов (Truncation Bug)
При передаче 96-битного UID в обертку MicroPython выполнялось приведение типов со сжатием через операцию XOR:
Это создало массовые коллизии, при которых тысячи различных физических UID давали одно и то же 32-битное значение в памяти.
4. Узкое окно таймера (Uptime)
Процесс создания кошелька человеком от момента включения питания до нажатия кнопки генерации занимает от 30 секунд до 5 минут ($30\,000 \dots 300\,000$ мс), что ограничивает вариативность таймера всего несколькими сотнями тысяч дискретных значений.
Code:
[ 96-bit STM32 Chip UID ]
├── Word 0 (bits 0..31) : Координаты кристалла на пластине (Die X/Y)
├── Word 1 (bits 32..63) : Номер пластины в партии (Wafer ID: 1..25)
└── Word 2 (bits 64..95) : Идентификатор производственной партии (Lot Number)
Коммерческие партии чипов закупаются производителем ограниченными сериями. Для устройств, выпущенных в одном временном окне, значение Word 2 на фабрике является фиксированным или варьируется в узком диапазоне чисел.
2. Физические границы нарезки пластин (Word 0 & Word 1)
Номер пластины (Word 1) ограничен кассетой на 25 штук. Координатная сетка нарезки кристалла (Word 0) ограничена физическим размером 300-мм кремниевой пластины ($\approx 10\,000 \dots 50\,000$ валидных точек).
3. Ошибка приведения типов (Truncation Bug)
При передаче 96-битного UID в обертку MicroPython выполнялось приведение типов со сжатием через операцию XOR:
C:
uint32_t truncated_uid = (uint32_t)(Word0 ^ Word1 ^ Word2);
4. Узкое окно таймера (Uptime)
Процесс создания кошелька человеком от момента включения питания до нажатия кнопки генерации занимает от 30 секунд до 5 минут ($30\,000 \dots 300\,000$ мс), что ограничивает вариативность таймера всего несколькими сотнями тысяч дискретных значений.
Имея открытый исходный код прошивки, атакующие полностью воспроизвели алгоритм деривации ключей на стороне своих вычислительных кластеров.
Расчет вычислительной сложности:
Сложность ≈720,000 х 270,000 ≈ 1.94 х 10^11 операций
Для параллельных вычислений на криптографических ядрах GPU (SHA-256d и эллиптическая кривая secp256k1) перебор порядка $200$ миллиардов итераций занимает от 1 до 4 часов на стандартном вычислительном узле из нескольких графических ускорителей.
Code:
[ Изыскание диапазонов UID & Uptime ]
│
▼
[ Воспроизведение уязвимого PRNG ]
│
▼
[ Сжатый сид 256-bit (Слабая энтропия) ]
│
▼
[ Генерация BIP-39 Мнемоники (24 слова) ]
│
▼
[ Вычисление Master Key и BIP-84 Адреса ]
│
▼
[ Поиск совпадений в публичном UTXO-сете ] ──► (Match) ──► Вывод средств
- Диапазон Truncated UID: ≈10^5 ... 10^6 логических комбинаций для серии устройств.
- Диапазон Uptime: ≈ 2.7 х 10^5$ дискретных миллисекунд.
Сложность ≈720,000 х 270,000 ≈ 1.94 х 10^11 операций
Для параллельных вычислений на криптографических ядрах GPU (SHA-256d и эллиптическая кривая secp256k1) перебор порядка $200$ миллиардов итераций занимает от 1 до 4 часов на стандартном вычислительном узле из нескольких графических ускорителей.
Для тех, кто в теме: грубо говоря, сработал базовый подбор сид фразы по изначальным параметрам. Брали устройства Coldcard, получали Word2 (0x00000000 — 0xFFFFFFFF) и дальше магия логики:
- Цикл по Word 0: Координаты на пластине (ограничены физическим размером).
- Цикл по Word 1: Номер пластины (строго от 1 до 25).
- Цикл по Word 2: Диапазоны номеров партий за 2021–2026 годы - если найти, например, номер партии в промежутке от 2021 до 2026 можно ограничить диапазом и урезать 32 бита ещё сильнее.
- Цикл по Uptime: Время включения от 30 000 до 300 000 мс.
Вот как мог бы выглядеть код, который использовали в этой уязвимости, но он не даёт вам всю схему:
