Блокчейн6 мин чтения

Аудит проверил, что генератор на месте. Он был на месте — и не вызывался

Пять лет Coldcard брал случайность не оттуда, и ревью этого не видело: оно подтверждало наличие генератора, а не то, что исполняется именно он.

С 30 июля из биткоин-кошельков Coldcard вывели около 2000 BTC — примерно 130 миллионов долларов по текущей цене. Ни фишинга, ни вредоноса, ни украденного ноутбука. Устройства не подключаются к интернету, и никто их не трогал. Атакующие просто вычислили приватные ключи.

Один заход забрал 70 миллионов за 41 минуту. Galaxy Research подтверждает 1596 BTC с примерно 7300 адресов в трёх волнах плюс четырнадцать мелких эпизодов; с предполагаемой четвёртой волной выходит около 2055 BTC. Производитель насчитал не меньше пятнадцати независимых атакующих — дыру эксплуатировали все, кто успел.

Дальше — не хроника кражи, а разбор того, почему проверки её пропустили. Потому что пропустили они её не по недосмотру, а ровно так, как устроены сегодня.

Одна директива препроцессора

В марте 2021 года Coinkite перевела криптографию Coldcard на libsecp256k1 — ту же библиотеку, что использует Bitcoin Core. Решение здравое: своя криптография хуже общей. Развалилась интеграция.

Миграция незаметно увела генерацию сида с аппаратного генератора случайных чисел на программный запасной путь MicroPython — алгоритм Yasmarang, который существует для устройств вообще без чипа случайности. У Coldcard такой чип есть. Его просто перестали спрашивать.

Причина в одном токене. Защита сборки была написана через #ifndef — а эта директива проверяет, определена ли настройка, а не включена ли она. В Coinkite настройку определили как ноль, то есть «выключено». Ноль — тоже определённое значение, поэтому проверка прошла и сборка собралась. У обеих версий функции были одинаковые сигнатуры, так что и на глаз ничего не выглядело сломанным.

Результат: пространство поиска ключа схлопнулось со 128 бит примерно до сорока на старых моделях. Сорок бит перебираются офлайн, без единого запроса в сеть. Отсюда и 41 минута на 70 миллионов.

Почему пять лет никто не заметил

Coldcard проходил ревью. Ревью подтверждало, что настоящий аппаратный генератор в коде есть и работает. И это было правдой. Вот дословно из разбора самой Coinkite:

Основная масса случайности на COLDCARD приходила из PRNG, о наличии которого в кодовой базе я не знал. При этом аккуратно написанный мной код TRNG тоже использовался — но по случайности и только для менее важных вещей.

Правильный генератор существовал, был корректен и даже вызывался. Просто не там.

Ник Перкоко, директор по безопасности Kraken, сформулировал разрыв точнее всех: потребителя просят довериться реализации самой критичной функции в системе, и при этом нет независимой проверки, что одобренный путь получения энтропии — тот самый, который исполняется в бою.

Это не абстракция. В остальной индустрии такая проверка стандартна: NIST SP 800-90B в США и BSI AIS-31 в Германии описывают, как проектировать, тестировать и валидировать физические генераторы случайных чисел. У аппаратных кошельков эквивалента нет — есть Common Criteria на защищённые элементы, отдельные сертификации и оплаченные вендором аудиты, и ни один из них не заставляет систематически доказывать, какой код реально выполняется на устройстве в руках покупателя.

Мы писали, как готовить смарт-контракт к аудиту, и там же — что «нас аудировали» и «у нас нет дыр» это разные утверждения. Coldcard показал ту же мысль в предельной форме: аудит может быть добросовестным, подробным и при этом проверять не тот вопрос.

Вопрос «есть ли компонент» и вопрос «исполняется ли он» — разные. Первый проверяется чтением кода. Второй — только наблюдением за собранной прошивкой.

Осторожные пострадали первыми

Самая неприятная часть истории обнаружилась, когда начали разбирать, кто терял монеты до 2026 года.

Coldcard позволяет подмешать в сид собственную случайность — бросками игральной кости. Совет разумный и повторяется в сообществе годами. Проблема в арифметике: один бросок шестигранника даёт около 2,585 бита. Чтобы набрать те самые 128 бит, которые сама Coinkite считает минимумом, нужно не меньше 50 бросков; для 256 бит — 99. Устройство эту границу не обеспечивает: в документации прямо сказано, что число бросков не ограничивается, но при слишком малом количестве будет показано предупреждение.

Предупреждает — и пропускает дальше.

Исследовательница безопасности Тейлор Монахан, разбиравшая ранние кражи у владельцев Coldcard, говорит, что почти во всех случаях это были именно те, кто бросал кости. Недобросовестно доведённый до конца ритуал оставлял сид с настолько малой энтропией, что вскрыть его было тривиально — задолго до того, как о дефекте прошивки вообще узнали.

Тонкость в том, что путей два. В обычном режиме броски хешируются вместе с собственной случайностью устройства, и лишние броски только добавляют защиты поверх сломанного генератора. Но есть режим «только кости», где, по описанию Coinkite, последовательность бросков хешируется напрямую и генератор устройства не участвует вовсе. Выбрали его, бросили двадцать раз и остановились — вся безопасность сида держится на этих двадцати бросках.

Защита, применённая наполовину, оказалась опаснее её отсутствия. Это стоит держать в голове шире, чем про кошельки: механизм, который предупреждает, но не останавливает, перекладывает арифметику на пользователя — а пользователь считать не будет.

Что изменилось в 2026 году

Пять лет незаметности — это цена, которую раньше можно было заплатить. Теперь нельзя, и это видно по цифрам.

Пока шла эта кража, шестнадцать разработчиков собрали добровольный «красный отряд» и направили ИИ-модели на биткоин-кошельки, криптобиблиотеки и инфраструктуру. За сутки — 4962 находки по 390 проектам, из них 85 критических и 635 высокой серьёзности. В пересчёте это примерно один критический баг на человека в час при расходах порядка десяти тысяч долларов в день на вычисления.

Важнее самих чисел то, что говорят участники. Большинство критических находок владельцы проектов подтвердили быстро, потому что перепроверить их теперь можно почти бесплатно теми же инструментами. Узкое место сместилось: сложно уже не найти дыру, а доставить её нужному мейнтейнеру. И отдельно — мотив публиковать быстро: те, кто не в отряде, всё равно придут к тем же находкам.

Именно поэтому спор «нашёл ли дыру Coldcard человек или машина» второстепенен. Мы уже разбирали, как ИИ за 60 часов ослабил постквантовую подпись, которую люди изучали годами. Здесь то же самое с другой стороны: пятилетняя фора, на которую негласно рассчитывал любой давно не тронутый код, закончилась.

Что с этим делать

Если вы держите ключи на Coldcard, порядок известный: проверить модель и версию прошивки по объявлению производителя, сгенерировать новый сид на исправленной прошивке и перевести средства. Дефект касается одноключевых схем; мультиподпись он не затрагивает. Отдельная деталь для тех, кого уже метут прямо сейчас: в последней волне атакующие включили replace-by-fee, а значит транзакцию можно перебить — увидели свой адрес в мемпуле, ставьте комиссию выше и уводите монеты первыми. Окно — минуты.

Но выводы шире кошельков, и они про то, как вы проверяете свой код.

  • Проверять исполняемый путь, а не наличие кода. «Функция есть и корректна» не означает «вызывается именно она». Для критичных путей — тест, который падает, когда источник подменился: не «TRNG работает», а «сид собран из TRNG».
  • Считать подозрительными guard'ы, проверяющие определённость. #ifndef, defined(), if (config.FLAG) — ноль, пустая строка и false определены. Проверяйте значение, а не факт объявления.
  • Одинаковые сигнатуры двух реализаций — источник тихой подмены. Если у запасного и основного пути один интерфейс, компилятор молчит, ревью тоже. Разводите их по именам, чтобы подмена была видна в диффе.
  • Механизм, который предупреждает, но не запрещает, считайте неработающим. Порог, который пользователь может пропустить, он пропустит.
  • Пересмотреть, что означает ваш прошлый аудит. Если он был про наличие и корректность, а не про исполнение, его выводы устарели не потому, что аудиторы ошиблись, — а потому что находить стало на порядок дешевле.

Последний пункт — практический, а не риторический. Код, который пять лет считался проверенным, сейчас разбирают за часы и за десять тысяч долларов в день. Если у вас есть контракты или прошивки в продакшене, дешевле направить эти инструменты на них самим, чем ждать, пока это сделает кто-то другой. Мы занимаемся безопасностью и разбором web3-продуктов — но сделать это можно и без нас, главное сделать раньше.

Сергей Палий

Основатель, Sepia Software

Обо мне

Читать дальше

Все статьи