Back to Blog

Потеряно ~88 млн $: эксплойты COLDCARD и LULA | BlockSec Weekly

Code Auditing
5 августа 2026 г.
10 min read
Key Insights
  • В этом отчёте представлены 2 заметных инцидента безопасности, на которые в совокупности приходится около 88 млн долларов убытков в сетях Bitcoin и BNB Chain. Один-единственный сбой энтропии аппаратного кошелька (COLDCARD) стал причиной более 99% от общей суммы — это напоминание о том, что незаметная ошибка конфигурации сборки в прошивке кошелька может подорвать энтропию, от которой зависит безопасность средств.

  • Эти два инцидента демонстрируют различные шаблоны уязвимостей: ошибочная генерация энтропии в прошивке кошелька (COLDCARD), где создание seed-фразы незаметно переключалось на детерминированный программный генератор псевдослучайных чисел (PRNG) вместо аппаратного генератора случайных чисел (RNG), и уязвимость бизнес-логики в токене сети BNB Chain (LULA), где доступный для злоумышленника путь мог вызвать привилегированную функцию recycle(), позволяя контракту Rental выводить LULA из пары PancakeSwap V2 и пересинхронизировать её резервы с манипулированным балансом.

  • Уязвимость COLDCARD появилась в 2021 году и не использовалась в массовом масштабе вплоть до конца июля 2026 года, когда затронутые кошельки были опустошены в серии ончейн-волн, начавшихся 30 июля. Коренная причина сбоя заключалась в том, что прошивка никогда не проверяла, действительно ли её API генерации seed-фразы обращается к предполагаемому аппаратному RNG: защита сборки проверяла лишь наличие конфигурационного макроса, а не его значение. Защита сборки для криптографической энтропии должна проверять значение макроса и отказывать по умолчанию при неудаче (fail closed), а компоненты офчейн-подписи и генерации ключей требуют столь же строгой проверки безопасности.

За прошедшую неделю (2026/07/27 - 2026/08/02) в центре внимания оказались 2 значимых инцидента безопасности, суммарные потери от которых составили порядка $88M.

Дата Инцидент Тип Оценочные потери
2026/07/29 LULA Изъян бизнес-логики ~$578K
2026/07/30 COLDCARD Ошибочная генерация энтропии ~1,370 BTC (~$88M)*

* Потери COLDCARD варьируются в зависимости от метода подтверждения: показанные ~1,370 BTC (~$88M) — это публично проверяемый минимум на блокчейне (coldcardwatch.com); сверка по закрытым каналам (Galaxy Research, на основе переписки с 73 жертвами) дает более высокую оценку, около 1,596 BTC и до ~2,055 BTC (~$130M), если учитывать подозреваемые, но неподтвержденные выводы средств.

Причины выбора

  • LULA: Привилегированная функция токена, способная перемещать балансы пары AMM и принудительно пересинхронизировать резервы, становится многократно повторяемым примитивом для истощения ликвидности через манипуляцию ценой.
  • COLDCARD: Ошибка сборки и интеграции в прошивке кошелька незаметно перенаправила генерацию сида через детерминированный программный резервный механизм, подрывая гарантии энтропии и превращая восстановление сида в offline-поиск, который перерос в крупномасштабную потерю средств.

Лучший аудитор безопасности для Web3

Проверяйте дизайн, код и бизнес-логику перед запуском

Главное событие недели: COLDCARD

Мы выбрали COLDCARD главным событием этой недели, потому что ошибка энтропии кошелька привела к самым крупным потерям за этот период. Основная причина — защита сборки, проверявшая наличие макроса конфигурации вместо того, включен ли он, — это тот вид незаметной ошибки интеграции, который невозможно выявить функциональным тестированием, и этот урок применим к любой системе, где безопасность на блокчейне зависит от случайности вне цепи.

COLDCARD, аппаратный биткоин-кошелек, в 2021 году выпустил прошивку, которая генерировала сиды кошелька, используя детерминированный программный источник вместо предполагаемого аппаратного генератора случайных чисел (RNG) [1][2]. Этот изъян не эксплуатировался в крупных масштабах до конца июля 2026 года, когда затронутые кошельки были вычищены серией транзакций на блокчейне, начавшихся 30 июля: публично подтвержденные потери составляют не менее 1,370 BTC (~$88M, по курсу $64,099 на 5 августа) [3], в то время как приватно подтвержденные данные указывают на цифру до 1,596 BTC [4]. Основной причиной была ошибка сборки и конфигурации, которая направила генерацию сида на программный резервный механизм вместо предполагаемого аппаратного RNG. Для затронутых устройств это превратило восстановление сида из криптографически невозможной задачи в offline-поиск.

Предпосылки

COLDCARD — это аппаратный биткоин-кошелек. Приватные ключи и адреса кошелька выводятся из одного секретного значения — его сида, поэтому безопасность любого кошелька с самостоятельным хранением средств зависит от двух свойств этого сида: он должен оставаться секретным и быть непредсказуемым при генерации. Аппаратные кошельки существуют в основном для защиты первого свойства; этот инцидент — сбой второго. Сид-фраза BIP-39 читаема человеком, но свойство безопасности, лежащее в основе требования непредсказуемости, — это все еще энтропия случайных байтов, использованных для ее создания. Если эти байты можно воспроизвести, сид можно воспроизвести. Интуитивный пример — сейф, комбинация которого выбирается путем бросания костей: если кости подогнаны, сейф остается открытым для любого, кто знает смещение, независимо от того, насколько прочен замок.

Для кошелька "подогнанные кости" означают слабый генератор случайных чисел. Ожидается, что аппаратные кошельки будут получать энтропию сида от аппаратного генератора истинно случайных чисел (TRNG) на своем защищенном микроконтроллере, потому что программный генератор псевдослучайных чисел (PRNG) детерминирован: зная его внутреннее состояние и историю вызовов, его вывод можно точно воспроизвести. Если выводы генератора предсказуемы, его диапазон кандидатов можно перечислить и сопоставить с публичными данными кошелька, такими как адрес, xpub или публичный ключ, что превращает восстановление сида из криптографически невозможной задачи в offline-поиск.

Прошивка COLDCARD раскрывала два отдельных источника RNG. MicroPython поставлялся с платформенным слоем STM32, который раскрывал глобальный символ rng_get(), ожидаемый криптографической библиотекой кошелька, в то время как COLDCARD также поддерживал собственную локальную обертку аппаратного RNG. Оба источника предназначены для предоставления аппаратной энтропии, и криптографическая библиотека кошелька обращается к одному из них через единый глобальный символ RNG, разрешаемый при сборке прошивки.

Анализ уязвимости

Основной причиной была ошибка сборки и интеграции вокруг макроса конфигурации MICROPY_HW_ENABLE_RNG. Конфигурация производственной платы COLDCARD устанавливала этот макрос в 0, потому что прошивка предполагала использование собственной локальной обертки аппаратного RNG вместо реализации аппаратного RNG в MicroPython. Однако путь генерации кошелька был перенесен на ngu.random.bytes(32), и путь STM32 в криптографической библиотеке в итоге зависел от глобального символа rng_get(), разрешаемого MicroPython.

Цепочка проблемы была такой:

generate_seed()
  -> ngu.random.bytes(32)
  -> libngu CHIP_TRNG_32()
  -> rng_get()
  -> MicroPython STM32 RNG module
  -> Yasmarang software fallback because MICROPY_HW_ENABLE_RNG == 0

Конфигурация платы отключала ветвь аппаратного RNG в MicroPython:

// We have our own version of this code.
#define MICROPY_HW_ENABLE_RNG (0)

Криптографическая библиотека все же считала этот макрос достаточным доказательством наличия аппаратного RNG [5], потому что она проверяла только существование макроса перед вызовом rng_get():

extern uint32_t rng_get(void);
#define CHIP_TRNG_32() rng_get()

#ifndef MICROPY_HW_ENABLE_RNG
#error "get a HW TRNG plz"
#endif

Эта защита пропускает опасный случай: макрос, определенный как 0, все равно считается определенным, поэтому проверка #ifndef проходит и сборка завершается успешно. Поскольку MicroPython выбирает реализацию RNG по значению макроса, а не по его существованию, MICROPY_HW_ENABLE_RNG == 0 направил rng_get() на ветвь программного резервного механизма вместо локальной обертки COLDCARD [6]:

#if MICROPY_HW_ENABLE_RNG
    // STM32 hardware RNG
#else
    // Yasmarang software fallback
#endif

Последствия различаются в зависимости от поколения устройства. Для прошивки Mk2/Mk3 версий v4.0.0-v4.1.9, согласно анализу Block [7], к ngu.random не добавлялась криптографическая энтропия, поэтому генерация кошелька может стать детерминированной, если известны состояние резервного механизма и история вызовов (рекомендация Coinkite [2] немного точнее ограничивает диапазон Mk2/Mk3 версиями v4.0.1-v4.1.9). Для Mk4/Q/Mk5 материал безопасного элемента хешировался, но в ngu.random.reseed() передавались только четыре байта, что ограничивало безопасный ресид одним 32-битным словом состояния — значительно меньшим эффективным пространством поиска, чем ожидаемая пользователями энтропия сида. Хеширование итоговых 32 случайных байтов не может увеличить энтропию результата; оно лишь преобразует уже ограниченный набор кандидатов.

Анализ атаки

В отличие от эксплойта смарт-контракта, у этого инцидента нет единой атакующей транзакции в блокчейне для трассировки. Это была офлайн-проблема восстановления сида, за которой последовали выводы средств на блокчейне. Предпосылкой было то, что затронутые пользователи сгенерировали свои сиды кошелька через уязвимый путь ngu.random.bytes(32), поэтому их сид-материал зависел от воспроизводимого состояния программного резервного механизма, а не от полной аппаратной энтропии. Вторая, выведенная логически предпосылка заключается в том, что затронутые кошельки можно было получить только из сида: сильная, уникальная парольная фраза BIP-39 добавляет независимую, предоставленную пользователем энтропию через PBKDF2, которую ошибка RNG никогда не затрагивала, выводя такие кошельки за рамки чистого перечисления сидов, а масштаб выводов средств позволяет предположить, что большинство затронутых пользователей не устанавливали такую парольную фразу. Отсюда восстановление, вероятно, проходило в три шага:

  1. Атакующий ограничивал или перечислял состояния-кандидаты RNG, используя метаданные устройства, время загрузки, предположения RTC/SysTick и вероятную историю вызовов RNG.
  2. Для каждого состояния-кандидата атакующий выводил кандидатные сиды кошелька и сверял их офлайн с публичными данными кошелька, такими как адреса, xpub'ы или сгенерированные публичные ключи.
  3. Как только кандидат совпадал с реальным кошельком, атакующий восстанавливал сид, реконструировал приватные ключи и выводил связанные BTC.

На блокчейне кража проявилась как волна выводов средств с адресов, начавшаяся 30 июля. Независимое отслеживание на основе эвристики на блокчейне [3] идентифицирует несколько волн вывода средств на общую сумму не менее 1,370 BTC (примерно $88M, при оценке монет по курсу $64,099 на 5 августа), выведенных с 4,580 проверенных адресов, что является подтвержденным минимумом, а не итоговой суммой. При этом закрытый канал [4], подтвержденный через переписку с 73 жертвами, указывает на цифру до примерно 1,596 BTC, возрастающую до 2,055 BTC (~$130M), если добавить подозреваемые, но неподтвержденные выводы средств.

Заключение

Инцидент с COLDCARD был сбоем генерации энтропии: критически важный для безопасности API генерации сида незаметно переключался на детерминированный программный резервный PRNG вместо предполагаемого аппаратного RNG, потому что защита сборки проверяла существование макроса конфигурации, а не то, включен ли он. Для затронутых устройств это превратило восстановление сида из криптографически невозможной задачи в offline-поиск, и результатом стал вывод более 1,370 BTC [3] в нескольких волнах (оценки по закрытым каналам указывают на цифру примерно от 1,596 до 2,055 BTC [4]).

Основной инженерный сбой заключается в том, что выпущенная прошивка никогда не проверяла, действительно ли ее самый критически важный для безопасности API достигал предполагаемого аппаратного RNG. Три практики позволили бы выявить это: защиты сборки для криптографической энтропии должны проверять как существование макроса, так и его значение; резервные механизмы энтропии должны работать по принципу "отказ закрыт", а не незаметно подменяться программным PRNG; а верификация итогового образа прошивки должна охватывать происхождение символов и сквозной поток энтропии, а не только компилируемость кода. Затронутый сид не может быть исправлен на месте; его средства следует переместить в кошелек, созданный на исправленной прошивке, а сильная, уникальная парольная фраза снижает непосредственный риск без устранения проблемы сида [2].

Стоит подчеркнуть характер этого сбоя: ошибки случайности такого рода невидимы для функционального тестирования, потому что каждый сгенерированный сид сам по себе валиден. Дефект заключается не в каком-либо отдельном выводе, а в источнике, который его произвел: поскольку этот источник предсказуем и воспроизводим, сиды в совокупности попадают в небольшой, перечислимый диапазон. Компоненты генерации ключей вне цепи заслуживают внимания к безопасности первого класса.

Источники

Начните работу с Phalcon Explorer

Погрузитесь в транзакции, чтобы действовать мудро

Попробовать бесплатно

Другие инциденты этой недели

LULA

LULA, токен BEP-20 в сети BNB Chain, потерял примерно $578K 29 июля 2026 года из-за изъяна бизнес-логики в своем токен-контракте. Доступный для атакующего путь мог активировать привилегированную функцию recycle(), позволяя контракту Rental напрямую переводить LULA из пары PancakeSwap V2, а затем вызывать sync(), обновляя резервы пары до манипулированных балансов. Атакующий многократно активировал recycle(), чтобы сократить резерв LULA в паре практически до нуля, а затем обменял небольшое количество LULA обратно почти на весь USDT [1].

Предпосылки

LULA — это токен BEP-20 в сети BNB Chain с механизмом командных наград на основе аренды. Подходящие адреса накапливают ожидающие командные награды в контракте Rental и получают их через claimTeamReward(). Во время процесса получения контракт Rental вызывает функцию recycle() токена, чтобы получить LULA для распределения наград. recycle() не может быть вызвана произвольными пользователями; только контракт Rental авторизован выполнять ее.

Точка входа claimTeamReward() содержит проверку "только EOA". Она поддерживает прямые вызовы от внешних аккаунтов, где msg.sender == tx.origin, а также поддерживает делегированные вызовы EIP-7702, проверяя префикс делегированного кода.

На автоматическом маркет-мейкере (AMM) пара оценивает обмены на основе своих сохраненных резервов, и эти резервы обновляются через функцию sync() пары, которая устанавливает их равными текущим балансам токенов пары. Резервы обычно отслеживают подлинную торговлю, потому что они изменяются вместе с обменами и событиями ликвидности, но баланс токена пары также может быть изменен прямым переводом, и sync() копирует любой присутствующий баланс, манипулированный или нет, в сохраненные резервы.

Анализ уязвимости

Основной причиной было то, что LULA.recycle() позволяет контракту Rental напрямую переводить LULA из пары PancakeSwap V2, а затем вызывать sync(), обновляя резервы пары до манипулированных балансов [1].

Поскольку sync() устанавливает резервы равными тому балансу LULA, который остается в паре, этот привилегированный путь может произвольно снизить резерв LULA в паре, оставив сторону USDT нетронутой. Как только резерв LULA становится близким к нулю, пара оценивает небольшое количество LULA как стоящее почти весь ее USDT.

Анализ атаки

Следующий анализ основан на транзакции 0xa219ab9...411d7c.

  • Шаг 1: Атакующий профинансировал манипуляцию, накопив ~197.05M USDT. Средства поступили из нескольких источников флэш-займов и заимствований, включая Moolah/Lista, Aave V3, Venus, PancakeSwap V3, PancakeSwap Vault, Uniswap V4 PoolManager и Uniswap V3.
  • Шаг 2: Атакующий использовал ~197.05M USDT для выполнения крупного обмена USDT -> LULA через роутер PancakeSwap V2. Это резко снизило резерв LULA в паре с примерно 8M LULA до 24,022 LULA, увеличив сторону USDT до примерно 197.64M USDT.
  • Шаг 3: Атакующий вызвал путь наград через несколько кошельков EIP-7702. Каждый кошелек вызывал claimTeamReward() в контракте Rental, который затем активировал LULA.recycle(), сокращая резерв LULA в паре с 24,022 LULA до 0.004 LULA, при этом пара все еще держала очень крупную сторону USDT.
  • Шаг 4: Атакующий провел итоговый обмен через роутер PancakeSwap V2, отправив в пару всего ~4,749 LULA и получив ~197.64M USDT.
  • Шаг 5: Атакующий погасил все флэш-займы, получив прибыль примерно $578K.

Заключение

Токен LULA в сети BNB Chain был эксплуатирован на сумму примерно $578K через изъян бизнес-логики в его токен-контракте: доступный для атакующего путь мог активировать его привилегированную функцию recycle(), позволяя контракту Rental напрямую переводить LULA из пары PancakeSwap V2, а затем вызывать sync(), пересинхронизируя резервы пары до манипулированных балансов. Атакующий многократно активировал это, чтобы сместить цену пары и обменять небольшое количество LULA обратно почти на весь USDT.

Токен-контракт никогда не должен предоставлять привилегированный путь, способный перемещать балансы пары AMM и принудительно пересинхронизировать резервы, потому что это передает контроль над ценой любому, кто может получить доступ к этому пути. Токены, интегрирующиеся с парами AMM, должны обеспечивать, чтобы резервы пары были привязаны к подлинным, рыночным изменениям баланса, а любая логика, считывающая резервы пула для оценки цены, должна рассматривать их как манипулируемые, а не как авторитетные.

Источники

Начните работу с Phalcon Security

Обнаруживайте каждую угрозу, получайте важные оповещения и блокируйте атаки.

Попробовать бесплатно

О компании BlockSec

BlockSec — это полностековый провайдер услуг безопасности блокчейна и криптовалютного комплаенса. Мы создаем продукты и сервисы, которые помогают клиентам проводить аудит кода (включая смарт-контракты, блокчейн и кошельки), перехватывать атаки в режиме реального времени, анализировать инциденты, отслеживать незаконные средства и выполнять обязательства AML/CFT на протяжении всего жизненного цикла протоколов и платформ.

BlockSec опубликовал множество статей по безопасности блокчейна на престижных конференциях, сообщил о нескольких атаках нулевого дня на приложения DeFi, заблокировал множество взломов, спасая более 20 миллионов долларов, и защитил миллиарды криптовалюты.

Sign up for the latest updates
Потеряно ~9,4 млн $: эксплойты Injective и Aquifer | BlockSec Weekly
Security Insights

Потеряно ~9,4 млн $: эксплойты Injective и Aquifer | BlockSec Weekly

За неделю (31.08–06.09.2026) четыре инцидента безопасности принесли убытки ~$9.4М в Injective, Solana, Ethereum и Flow EVM: Injective (~$4.8М), Aquifer (~$2.47М), Notional Finance V1 (~$1.73М), Ankr FLOW (~$410K).

За пределами смарт-контракта: операционная безопасность доменов и DNS в Web3
Security Insights

За пределами смарт-контракта: операционная безопасность доменов и DNS в Web3

Аудит контрактов не проверяет сам контракт. Мы провели 800 SEAL-проверок DNS и регистраторов для 100 доменов из TVL Top 100 DefiLlama — лишь один домен прошёл все. Каких четырёх мер защиты не хватает большинству и почему это критично для пользователя.

Поверхности атаки Web3: обзор тестирования на проникновение

Поверхности атаки Web3: обзор тестирования на проникновение

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

Best Security Auditor for Web3

Validate design, code, and business logic before launch. Aligned with the highest industry security standards.

BlockSec Audit