Минимум $5,69 млн уже вывели из криптокошельков, где сид-фразы когда-то сгенерировали на слабом источнике случайности. В центре истории — уязвимость CryptoJS: проблема сидела не в блокчейне и не в BIP39, а в генераторе псевдослучайных чисел внутри популярной JavaScript-библиотеки. Для разработчиков это неприятное, но полезное напоминание: криптография чаще ломается не на алгоритмах, а на энтропии, которую кто-то решил «добыть» через почти случайный JavaScript.
О схеме подробно пишет The Hacker News со ссылкой на исследование Coinspect, которая связала кражи с функцией CryptoJS.lib.WordArray.random(). Именно она, по данным компании, использовалась как источник энтропии при создании фраз восстановления в ряде кошельков. Сейчас подтверждены пять приложений: RRWallet и Milo уже заброшены и исправлений для них не ожидается; Bexo Wallet, по словам Coinspect, исправил баг в версии 20.1.0, но по состоянию на 6 августа обновленные сборки еще не были доступны в сторах; NanChat признал проблему для версий до 1.3.0 и выпустил фикс в 1.3.0; Bitcoin Libre устранил дефект еще в версии 4, опубликованной в июле 2024 года. При этом сам Coinspect отдельно оговаривает: список, скорее всего, неполный, потому что часть старых мобильных сборок и расширений уже исчезла из магазинов приложений.
Технически история выглядит почти издевательски. Вместо пространства перебора в 2^128 или 2^256 вариантов у атакующих оставалось примерно 2^39 и 2^47 — порядок, который можно перебирать на обычном железе, а не в фантазиях про дата-центр спецслужб. Coinspect воспроизвела цепочку атаки целиком: перебор выходов слабого генератора, преобразование их в BIP39-фразы, вывод адресов и сверка с публичными блокчейн-данными. Важно и другое: наличие самого пакета CryptoJS в зависимостях еще не делает приложение уязвимым. Эксплуатируемой эта история становится только тогда, когда конкретная функция реально используется для генерации чувствительных значений вроде сид-фраз, приватных ключей или токенов восстановления доступа.
Контекст у этой истории особенно показательный. Слабый генератор попал в CryptoJS еще 12 лет назад, а в июне 2014 года в ветке 3.x закрепился вариант Multiply-With-Carry, который инициализировался от Math.random(). Затем библиотека уже пыталась исправиться: версии 3.2.0 и 3.2.1 переключили генерацию на нативную криптографическую случайность, но в 3.3.0 слабый код вернули назад, потому что предыдущее изменение сочли ломающим совместимость. Только релиз 4.0.0 в феврале 2020 года окончательно вернул нормальный источник случайности. Отсюда и главная ирония: проект мог однажды обновиться до безопасной версии, а потом в рамках той же мажорной ветки снова стать уязвимым. Одним из путей распространения дефекта Coinspect называет ferrumnet/bip39 — React Native-форк, который заменил нативную генерацию случайности из upstream bip39 на CryptoJS.
В деньгах и датах картина тоже уже довольно четкая. Первая волна вывода средств прошла 27 мая и, по оценке Coinspect, принесла злоумышленникам около $3,14 млн с 431 аккаунта. Вторая длилась с 30 мая по 13 июля и добавила еще $2,55 млн; в нее вошел, в частности, вывод примерно 2,18 млн USDT с одного Tron-адреса 4 июля. В более позднем анализе исследователи отслеживали уже 2114 идентифицированных сидов и связанные с ними адреса в сетях Bitcoin, Ethereum, Tron, Rootstock и Polygon. Суммарный подтвержденный ущерб — $5 690 922, и это нижняя граница, а не окончательный счет. Основатель Coinspect Джулиано Риццо сообщил о проблеме 10 июня, исправление отправили через два дня, но технические детали компания придержала почти на два месяца, чтобы успеть уведомить вендоров и собрать больше адресов, которые уже могли попасть под риск.
Для пользователей неприятная часть в том, что обновление приложения само по себе уже ничего не лечит. Если фраза восстановления была сгенерирована через уязвимый путь, она остается предсказуемой и после апдейта, и после импорта в аппаратный кошелек, и после любых последующих PBKDF2- и хеш-операций: добавить недостающую энтропию постфактум нельзя. Поэтому уязвимость CryptoJS — это не история про «срочно обновитесь и живите дальше», а история про полноценную миграцию: создать новый сид надежным способом и перевести на него активы. Для команд, которые делают кошельки, урок еще жестче. Нужно проверять не только package.json, но и реальные точки генерации секретов, особенно в мобильных форках, React Native-обвязках и давно забытых зависимостях. Отдельный сигнал рынку: на 6 августа публичное предупреждение выпустил только NanChat, а с Bexo ситуация выглядела туманно даже после заявления о фиксе — в App Store на iPhone оставалась версия 18.3.5, а в Google Play был виден апдейт без публичного номера версии.
Самый неудобный вопрос здесь не про CryptoJS как таковой, а про экосистему вокруг него. Уязвимость CryptoJS успела прожить годы, пережить частичное исправление, откат этого исправления и расползтись по форкам, которые потом исчезли из поля зрения вместе со старыми сборками. Для рынка криптокошельков это уже не просто баг, а тест на зрелость процессов: умеют ли команды отзывать уязвимые релизы, честно признавать компрометацию сид-фраз и доводить миграцию до конца, а не ограничиваться строчкой в changelog. Детали инцидента собраны в материале .