За прошедшую неделю (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 никогда не затрагивала, выводя такие кошельки за рамки чистого перечисления сидов, а масштаб выводов средств позволяет предположить, что большинство затронутых пользователей не устанавливали такую парольную фразу. Отсюда восстановление, вероятно, проходило в три шага:
- Атакующий ограничивал или перечислял состояния-кандидаты RNG, используя метаданные устройства, время загрузки, предположения RTC/SysTick и вероятную историю вызовов RNG.
- Для каждого состояния-кандидата атакующий выводил кандидатные сиды кошелька и сверял их офлайн с публичными данными кошелька, такими как адреса, xpub'ы или сгенерированные публичные ключи.
- Как только кандидат совпадал с реальным кошельком, атакующий восстанавливал сид, реконструировал приватные ключи и выводил связанные 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].
Стоит подчеркнуть характер этого сбоя: ошибки случайности такого рода невидимы для функционального тестирования, потому что каждый сгенерированный сид сам по себе валиден. Дефект заключается не в каком-либо отдельном выводе, а в источнике, который его произвел: поскольку этот источник предсказуем и воспроизводим, сиды в совокупности попадают в небольшой, перечислимый диапазон. Компоненты генерации ключей вне цепи заслуживают внимания к безопасности первого класса.
Источники
- [1] Оповещение BlockSec Phalcon, связывающее вывод средств из кошелька COLDCARD со слабой случайностью генерации сида
- [2] Coinkite, Технический глубокий разбор проблемы энтропии
- [3] coldcardwatch.com, Coldcard Sweep Watch: отслеживание выведенных адресов на блокчейне и методология
- [4] Galaxy Research, оценка потерь от взлома COLDCARD (~1,596 BTC подтверждено через отчеты жертв)
- [5] libngu, путь STM32 RNG ссылается на глобальный
rng_get()и защищен только#ifndef MICROPY_HW_ENABLE_RNG - [6] Coldcard MicroPython,
rng_get()переключается на Yasmarang, когда аппаратный RNG отключен - [7] Block Engineering, Предсказуемый резервный RNG и 32-битный ресид в прошивке COLDCARD
Начните работу с 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, должны обеспечивать, чтобы резервы пары были привязаны к подлинным, рыночным изменениям баланса, а любая логика, считывающая резервы пула для оценки цены, должна рассматривать их как манипулируемые, а не как авторитетные.
Источники
О компании BlockSec
BlockSec — это полностековый провайдер услуг безопасности блокчейна и криптовалютного комплаенса. Мы создаем продукты и сервисы, которые помогают клиентам проводить аудит кода (включая смарт-контракты, блокчейн и кошельки), перехватывать атаки в режиме реального времени, анализировать инциденты, отслеживать незаконные средства и выполнять обязательства AML/CFT на протяжении всего жизненного цикла протоколов и платформ.
BlockSec опубликовал множество статей по безопасности блокчейна на престижных конференциях, сообщил о нескольких атаках нулевого дня на приложения DeFi, заблокировал множество взломов, спасая более 20 миллионов долларов, и защитил миллиарды криптовалюты.
-
Официальный сайт: https://blocksec.com/
-
Официальный аккаунт в Twitter: https://twitter.com/BlockSecTeam



