30 июля 2026 года началась серия выводов с биткоин-адресов, приватные ключи которых оказались предсказуемы. Причина была не в криптографии, а в сборке прошивки: генератор случайных чисел аппаратного кошелька молча подменился программным, и энтропия создаваемых сидов упала на порядки. Основа: Сид на костях: как создать ключ, не доверяя генератору.
Это самая наглядная иллюстрация тезиса, который в теории звучит абстрактно: вся безопасность биткоин-кошелька держится на качестве одного случайного числа, и качество это снаружи не видно.
Ончейн-картина
Данные на 2 августа 2026 года
Цифры ниже — снимок на указанную дату. Кража разбиралась по частям несколько суток, оценки в ходе разбора уточнялись в разы, а средства вора могут прийти в движение в любой момент. Читайте их как порядок величины и состояние на конкретный день, а не как итог.
По раскладке Galaxy Research вывод прошёл тремя волнами, затронул 4 585 адресов и составил 1 367,05 BTC.
Выведенное сведено в восемь хранилищ, и на дату снимка все они лежат нетронутыми — ни одно не потрачено:
| Хранилище | BTC |
|---|---|
| 1 | 562,02 |
| 2 | 398,48 |
| 3 | 89,62 |
| 4 | 45,90 |
| 5 | 32,45 |
| 6 | 30,18 |
| 7 | 0,61 |
| 8 | 0,33 |
| Итого | 1 159,55 |
Сложение столбца даёт 1 159,59 — расхождение в четыре сотых возникает из-за округления каждого баланса до двух знаков и реальной разницы не отражает.
Две приведённые суммы измеряют разное: 1 367,05 BTC — это сколько увели с кошельков пострадавших, 1 159,55 BTC — сколько лежит в известных хранилищах вора. Публично сведённого баланса между ними нет. Неподвижность средств при этом ничего не говорит о перспективах возврата — она означает лишь то, что на дату снимка вор их не тратил.
Все пострадавшие адреса, по тому же ончейн-анализу, вели к кошелькам с одной подписью.
Кто разбирал
Разбор цепочки транзакций принадлежит Робу Гамильтону из AnchorWatch и разошёлся по прессе; ончейн-раскладку по волнам и адресам публикует Galaxy Research.
В первый же день появились три независимых технических разбора: предупреждение и технический бэкграундер от Coinkite (производителя COLDCARD), собственное расследование инженеров Block и модель стоимости атаки от исследователя LLFOURN, на которую ссылаются сами Coinkite.
Причина: #ifndef вместо проверки значения
Цепочка событий восстанавливается по публикациям Coinkite и Block.
В 2021 году Coinkite перешли на библиотеку libsecp256k1 от Bitcoin Core и ради этого добавили в прошивку промежуточную прослойку libNgU. При переезде вызов генерации сида сменился с ckcc.rng_bytes() на ngu.random.bytes() — и этот путь разрешился в программный запасной генератор MicroPython вместо аппаратного TRNG.
Собственный TRNG-код при этом никуда не делся. Он лежал в прошивке и даже работал — но, по признанию самого разработчика, «лишь по случайности, и только для менее важных вещей».
Флаг MICROPY_HW_ENABLE_RNG был явно выставлен в ноль в расчёте на то, что так отключаются обе ветки. Но проверка в коде была написана через #ifndef: она смотрела, определён ли макрос, а не равен ли он нулю. Директива #error не срабатывала, и сборка молча выбирала программную реализацию.
Дальше сработала комбинация обстоятельств, из-за которой проблему не заметило ревью:
- обе реализации имели одинаковую сигнатуру;
- нужный TRNG-код физически присутствовал в бинарнике;
- ревью это видело и на этом успокаивалось.
Никто не проверил, какая именно реализация вызывается из ветки генерации сида.
Масштаб потери энтропии
Программный генератор строил «случайность» из заводских данных чипа и показаний таймеров. Оценки оставшегося пространства поиска расходятся, и расхождение само по себе поучительно.
| Источник оценки | Mk2 / Mk3 | Mk4, Mk5, Q |
|---|---|---|
| Coinkite | ≈ 40 бит | ≈ 72 бита |
| Block (верхняя граница) | ≈ 2⁴⁰·⁷ | < 2⁷³·³ |
| Норма | 128 бит | 128 бит |
Block специально оговаривают, что 2⁷³ — это широкая верхняя граница, а не 73 бита реальной стойкости: поля таймеров скоррелированы, и фактическое пространство меньше.
Сорок бит — это порядка 1,1 триллиона вариантов. Перебор такого массива сопоставим с подбором пароля из девяти латинских букв. Именно поэтому Coinkite сами предложили считать такие сиды скомпрометированными.
Кто затронут и что делать
Если сид создавался на COLDCARD начиная с марта 2021 года и при создании не добавлялось минимум 50 честных бросков кости — сид следует считать скомпрометированным.
Границы версий у двух расследований немного разные: Coinkite отсчитывают от прошивки Mk3 4.0.1 (март 2021) и любых последующих; Block — от 4.0.0 (17 марта 2021), указывая для Mk2/Mk3 диапазон 4.0.0–4.1.9. Исправленные версии — 5.6.0 для Mk4 и Mk5, 1.5.0Q для Q и 4.2.0 для Mk3.
Ключевой момент, который легко понять неправильно:
- Обновление прошивки уже созданный сид не лечит. Прошивка исправляет генерацию новых ключей, а не качество старых.
- Перенос сида в другое устройство тоже не лечит. Слабое число остаётся слабым в любом кошельке.
- Спасает только новый сид и миграция средств.
Отдельная угроза второго порядка: после любого громкого инцидента появляется волна фишинговых «проверялок уязвимости» и писем «от производителя». Все они существуют ровно для того, чтобы вы ввели свои слова. Настоящая проверка сида не требует — она требует посмотреть, на какой прошивке создавался ключ.
Главный урок: невидимый отказ
У этой истории есть свойство, которое стоит проговорить отдельно: баг был невидим.
Устройство показывало красивые двенадцать слов, отпечаток кошелька, адрес — всё как обычно. Никакой индикации, что аппаратный источник случайности молчит, не было. Пользователь не мог это заметить в принципе.
Отсюда следует правило куда более важное, чем любые номера прошивок: всё, что вы не можете проверить, вы обязаны считать точкой уязвимости. Генератор случайных чисел внутри закрытой коробки проверить нельзя по определению — и правильный, и сломанный генератор выдают внешне одинаковый результат.
У игральной кости этой проблемы нет. У неё нет прошивки, нет сабмодулей, нет флагов сборки. Вы видите результат каждого броска своими глазами, а весь дальнейший путь от бросков до слов можно повторить на нескольких независимых реализациях и сверить.
Что спасло тех, кого не тронуло
Два обстоятельства реально сработали в этом инциденте, и оба стоит разобрать без упрощений.
Подмешанная энтропия
У COLDCARD есть функция Add Dice Rolls: устройство подмешивает ваши броски к собственной энтропии. Именно это спасло часть пользователей — сломанный генератор давал мало энтропии, но 50 честных бросков сверху перекрывали недостачу целиком.
Важно понимать, чем этот режим отличается от полностью самостоятельной генерации: здесь вы не проверяете результат, а страхуете его. Если генератор устройства окажется не просто слабым, а враждебным, подмешивание не спасёт.
Мультиподпись — но только наполовину
Пострадали кошельки с одной подписью, и отсюда легко сделать вывод «мультисиг спасает». Вывод верен ровно наполовину, и ограничение инженеры Block проговаривают прямым текстом:
Даже если COLDCARD используется в схеме мультисиг, если эта схема составлена исключительно из уязвимых устройств, воздействие уязвимости сохраняется. Для защиты от этой проблемы необходим кворум из защищённых устройств.
То есть мультисиг из трёх COLDCARD на затронутой прошивке не защищает ни от чего: все три ключа выведены одним и тем же сломанным генератором и перебираются так же, как один.
Защищает не мультиподпись сама по себе, а разнородность кворума — устройства разных производителей на разных кодовых базах. Чтобы вскрыть такой кошелёк, багу нужно случиться независимо в нескольких местах сразу.
А то, что в этом конкретном инциденте не тронули мультисиг, говорит скорее о выборе целей атакующим, чем о неуязвимости схемы.
Выводы для модели угроз
Инцидент не добавил в модель угроз ничего принципиально нового — он подтвердил то, что уже было записано в теории, ценой более чем в тысячу биткоинов.
- Источник случайности — точка отказа наравне с прошивкой и цепочкой поставок. Подробнее: аппаратные кошельки: модель угроз.
- Отказ может быть тихим. Отсутствие видимых симптомов не является свидетельством исправности.
- Однородность кворума сводит мультиподпись к одной подписи. Разнородность важнее количества ключей.
- Проверяемость важнее сложности. Метод хорош не тем, что он «сильнее», а тем, что каждый его шаг можно воспроизвести независимо.
- Не спешите. Паническая миграция угробила больше монет, чем большинство уязвимостей. Новый сид, проверка резервной копии, проверка адреса на экране устройства, тестовая транзакция — и только потом остальное.