
Где моя крипта, чувак?
Эта история началась в пятницу, 21 июля 2023 года. При попытке воспользоваться хорошо защищённым криптовалютным кошельком его владелец обнаружил, что все средства, хранившиеся в кошельке, просто исчезли. Это не было случайностью — он стал жертвой довольно изощрённой кражи. Средства были отправлены на адреса злоумышленников 12 июля, тогда как аппаратный кошелёк не использовался в течение нескольких дней. (Подробности ниже)
Создание и использование пострадавшего кошелька было необычайно строгим:
- Сгенерирован на ноутбуке в изолированной сети (air-gapped) на ОС Linux с самокомпилируемым программным обеспечением
- Использована мнемоническая фраза BIP39 из 24 слов
- Мнемоника безопасно введена в аппаратные кошельки Ledger и Trezor
- В наличии хорошая PIN-кодировка и физическая защита аппаратных кошельков
- Мнемоническая сид-фраза никогда не касалась компьютеров вне изолированной сети
- Резервная копия мнемонической фразы была хорошо защищена.
Молодец. Все красиво сделал :). Не помогло.
Где крипта моего друга?
Жертва обратилась к своим знакомым с аналогичными протоколами генерации и управления ключами, и нашлась вторая жертва! У второй жертвы также было похищено содержимое криптовалютного кошелька в тот же период времени — биткоин (BTC) обеих жертв был похищен в одну и ту же минуту on-chain. Потерпевшие поняли, что это не случайность. Они стали жертвами некоего взлома.
Пострадавшие обнаружили, что похищены не только биткоины (BTC). Злоумышленники также похитили Ethereum и другие криптовалюты из тех же кошельков. Жертвы поняли, что это могло произойти только в результате утечки закрытых ключей от их основных кошельков. Форсирование аппаратных кошельков с целью авторизации некорректных переводов или взлом отдельных закрытых ключей суб-аккаунтов оказали бы более ограниченное воздействие.
Подобная кража, затронувшая сразу двух человек, несмотря на все меры предосторожности, должна быть очень маловероятной. Ещё хуже то, что эти двое были не единственными пострадавшими. Общедоступные биткойн-транзакции, связанные с кражей, сливали средства из множества различных кошельков, вероятно, примерно с тысячи различных владельцев кошельков только в Биткойне.
Так что же творится? Неужели кто-то нашел уязвимость в аппаратном кошельке, которую можно использовать удалённо, применил её в широких масштабах и ждал несколько месяцев, прежде чем коллективно выполнить on-chain транзакции по сливу? Хуже того, может быть, уязвим один из базовых криптографических примитивов? Может, здесь замешана магия квантовых вычислений? 😱
Напряжение нарастало, и начался поиск источника компрометации.
Наша крипта пропала, но как?!
По итогам общения обе жертвы поняли, что их пострадавшие кошельки были сгенерированы на схожем airgap сетапе, хоть и с разницей в несколько лет. На тот момент проблема казалась трудноопределимой и могла указывать на разные источники. Жертвы решили начать с самого начала — с шагов по созданию кошелька, с первых использованных команд и далее по нарастающей.
Исходным инструментом, участвовавшим в создании обоих кошельков, был Libbitcoin Explorer в версии 3.x и его бинарная команда bx. Проект Libbitcoin с открытым исходным кодом существует уже очень давно (с 2011 года!), а bx содержит всё необходимое для создания автономного кошелька в одном автономном бинарном файле.
Несмотря на то, что bx является специализированным инструментом, о котором большинство пользователей кошельков даже не слышали, он пользуется определённой популярностью и ему посвящён небольшой раздел в приложении книги "Mastering Bitcoin". Другими словами, он представляется вполне адекватным для использования инструментом.
Краткий пример процесса создания кошелька в оболочке Linux:
# generate 256 bits of entropy, turn it into BIP39 mnemonics bx seed -b 256 | bx mnemonic-new
<output of secret BIP39 mnemonic words>
Приведённая выше команда выдает мнемоническую фразу BIP39 из 24 слов, сопоставимую с теми, что приписаны кошелькам жертв. Этот закрытый ключ является основой всей защиты кошелька.
Может ли быть проблема связана с двоичной командой bx, которую использовали жертвы? Они убедились, что подсистема /dev/random Random Number Generator (RNG)(Генератор случайных чисел) в ноутбуках на Linux обладает достаточной энтропией, но, возможно, этого оказалось недостаточно? Может ли это быть серьезная проблема конфигурации системы или вирус?
К этому моменту мы позвали несколько друзей с опытом работы в области информационной безопасности, чтобы они помогли нам разобраться в ситуации и изучить соответствующие пути кода генерации кошельков 🕵️♂️.
По мере того как всё большее количество людей присматривалось к этому кейсу, обрисовались первые признаки серьезной проблемы.
Чем больше глаз, тем больше багов?
Команда решила, что логично будет начать поиск с просмотра исходного кода bx команды bx seed.
Выполнение bx seed вызывает функцию new_seed(size_t bit_length) в libbitcoin-explorer src/utility.cpp, которая вызывает функцию pseudo_random_fill(data_chunk& out) в библиотеке libbitcoin-system:
console_result seed::invoke(std::ostream& output, std::ostream& error) { const auto bit_length = get_bit_length_option();
// These are soft requirements for security and rationality.
// We use bit vs. byte length input as the more familiar convention.
if (bit_length < minimum_seed_size * byte_bits ||
bit_length % byte_bits != 0)
{
error << BX_SEED_BIT_LENGTH_UNSUPPORTED << std::endl;
return console_result::failure;
}
const auto seed = new_seed(bit_length);
...
}
data_chunk new_seed(size_t bit_length)
{
size_t fill_seed_size = bit_length / byte_bits;
data_chunk seed(fill_seed_size);
pseudo_random_fill(seed);
return seed;
}
Только псевдослучайные? Ну ладно, генератор псевдослучайных чисел (PRNG) не обязательно так уж плох, если он является криптографически защищенным генератором псевдослучайных чисел — (Cryptographically Secure Pseudo Random Number Generator, CSPRNG). Возможно, всё в порядке, но давайте посмотрим внимательнее.
Мы следуем по пути вызова: pseudo_random::fill(data_chunk& out) -> pseudo_random::next() -> pseudo_random::next(uint8_t begin, uint8_t end) -> std::mt19937& pseudo_random::get_twister()
Подождите-ка. mt19937, twister— здесь используется PRNG Mersenne Twister (ГПСЧ Вихрь Мерсенна)? 🤔 В этот момент раздаются первые тревожные звоночки. PRNG Мерсенна не является CSPRNG, т.е. криптостойким, поэтому его не должно быть в любом коде, генерирующем секреты. Одно из уязвимых мест вихря Мерсенна заключается в том, что его внутреннее состояние может быть изменено до обратного злоумышленником, знающим несколько сотен результатов, что ставит под угрозу секретность остальных результатов того же потока, которые злоумышленнику неизвестны (если упрощённо).
Однако если ГПСЧ заново сидится перед каждой генерацией кошелька, извлекается только один результат, и этот результат хранится в секрете, будет ли слабая конструкция MT19937 достаточно фатальной, т.е. подверженной удалённой краже, если всё остальное сделано хорошо?
Прочёсывание кодового файла pseudo_random.cpp продолжилось, и нам не пришлось углубляться в детали pseudo_random::get_twister(), чтобы понять суть проблемы.
// Use the clock for seeding.
const auto get_clock_seed = []() NOEXCEPT
{
const auto now = high_resolution_clock::now();
return static_cast<uint32_t>(now.time_since_epoch().count());
};
// This is thread safe because the instance is thread static.
if (twister.get() == nullptr)
{
// Seed with high resolution clock.
twister.reset(new std::mt19937(get_clock_seed()));
}
Что за чёрт! Слабый алгоритм ГПСЧ, который сидится всего 32 битами системного времени, используется для генерации долгосрочных закрытых ключей кошельков, в которых хранится криптовалюта? 😧
Наша группа расследователей даже прошлась по нему дважды — не мог же bx использовать для генерации закрытых ключей ЭТО?
Команда, искавшая уязвимость, не могла поверить, что дело в этом, и поставила простой эксперимент для проверки данной гипотезы. Как и во всех хороших экспериментах, переменные окружающей среды находились под контролем экспериментаторов, а в данном случае именно переменная time была наиболее значимой для понимания рассматриваемой проблемы. Для проверки нашей теории мы использовали бинарник bx официальной версии 3.2.0 в сочетании с библиотекой libfaketime, запустив отдельные исполнения в абсолютно идентичных условиях синхронизации часов:
$ wget https://github.com/libbitcoin/libbitcoin-explorer/releases/download/v3.2.0/bx-linux-x64-qrcode -O ~/.local/bin/bx
$ chmod +x ~/.local/bin/bx
$ sudo apt install libfaketime
$ export LD_PRELOAD=/usr/lib/x86_64-linux-gnu/faketime/libfaketime.so.1 FAKETIME_FMT=%s FAKETIME=0
$ bx seed -b 256 | bx mnemonic-new
milk sad wage cup reward umbrella raven visa give list decorate bulb gold raise twenty fly manual stand float super gentle climb fold park
$ bx seed -b 256 | bx mnemonic-new
milk sad wage cup reward umbrella raven visa give list decorate bulb gold raise twenty fly manual stand float super gentle climb fold park
💥 В одно и то же время получался один и тот же "случайный" кошелёк! Невероятно!
Безопасная и надёжная утилита не выводила бы одну и ту же мнемоническую сид-фразу при таких обстоятельствах. Это было первое неопровержимое доказательство того, что код генерации секрета bx seed в официальном релизе использует для энтропии кошелька поломанную псевдослучайную функцию, базирующуюся на времени.
Копнув глубже, команда убедилась, что в коде используется стандартный вариант ГПСЧ Mersenne Twister MT19937, который по своей конструкции работает только с 32 битами начального сидинг ввода, а не расширенный вариант MT19937-64 с 64 битами сидинга. Таким образом, этот ГПСЧ может иметь не более 2^32 стартовых позиций в качестве верхней границы, независимо от того, сидится ли он /dev/random или временем.
Другими словами, при выполнении bx seed -b 256 для запроса 256 бит неугадываемой энтропии в результате выдаётся 32 бита времени высокоточных часов, пропущенных через блендер (точнее: твистер, он же вихрь 🌪️) и расширенных до 256 бит без добавления новой информации. Если бы это были реальные энтропийные данные, то количество возможных вариаций ключа росло бы экспоненциально с размером, поэтому разница между безопасным ожидаемым результатом (256 бит) и реальным результатом (32 бита) имеет астрономические масштабы!
Любой желающий может повторно вычислить и найти первоначально использованную энтропию жертвы после максимум около 4,29 млрд попыток, если он знает определённые характеристики, по которым можно определить успешное обнаружение криптовалютного кошелька. В данном случае это проверка производных адресов кошельков, которые в прошлом были замечены в получении средств на публичном блокчейне. Чтобы представить эту цифру в перспективе: перебор этого ключевого пространства занимает максимум несколько дней на среднем игровом ПК. И, к сожалению, это способен сделать любой человек, обладающий достаточными навыками программирования.
В плане безопасности криптовалютных кошельков это довольно катастрофическая ситуация.
Пятница 21 июля закончилась осознанием того, что всё стало очень сложно. Мало того, что несколько друзей безвозвратно потеряли полный контроль над ключами и средствами из своих кошельков, так ещё и у нашей группы возникла серьезная проблема с раскрытием информации.
Не первый взлом: слабая энтропия в кошельке Cake
На ранней стадии оценки возможных уязвимостей, которые могут привести к подобной проблеме, один из членов нашей команды вспомнил о ситуации со слабой энтропией в кошельке Cake Wallet.
Насколько нам известно, в мае 2021 года команда разработчиков Cake Wallet объявила о наличии уязвимости в энтропии их кошельков, в связи с чем все пользователи должны были изменить свои мнемонические фразы.
Дальнейшее расследование этой ситуации, проведенное пользователем Reddit Pure-Cricket7485, выявило ощутимые недостатки в стратегии источника энтропии кошелька Cake Wallet.
В частности: Cake Wallet использовал для генерации сидов кошельков небезопасную функцию Dart’s Random():
Uint8List randomBytes(int length, {bool secure = false}) {
assert(length > 0);
final random = secure ? Random.secure() : Random();
final ret = Uint8List(length);
for (var i = 0; i < length; i++) {
ret[i] = random.nextInt(256);
}
return ret;
}
Это настоящая проблема, учитывая, что Dart’s Random() может вернуться к 0 или системному времени
Random::Random() {
uint64_t seed = FLAG_random_seed;
if (seed == 0) {
Dart_EntropySource callback = Dart::entropy_source_callback();
if (callback != nullptr) {
if (!callback(reinterpret_cast<uint8_t*>(&seed), sizeof(seed))) {
// Callback failed. Reset the seed to 0.
seed = 0;
}
}
}
if (seed == 0) {
// We did not get a seed so far. As a fallback we do use the current time.
seed = OS::GetCurrentTimeMicros();
}
Initialize(seed);
}
Один из пользователей Reddit сравнил этот подход с Mersenne Twister, что заставило нас задуматься, нет ли у bx аналогичного недостатка.
Даже не второй взлом: Использование Mersenne Twister в кошельке Trust Wallet
Через несколько дней после начала процесса раскрытия информации мы узнали об уязвимости в Trust Wallet, опубликованной в конце апреля 2023 года. Прочитав прекрасный отчёт Ledger Donjon об обнаружении и исследовании CVE-2023-31290, мы были несколько шокированы, насколько эта уязвимость близка к уязвимости в bx! А публикация Trust Wallet подтверждает, что атаки на кошельки их пользователей происходили уже в декабре 2022 года. Почему же деньги из кошельков, сгенерированных bx, стали исчезать только в середине 2023 года?
Здесь есть в чём покопаться, поэтому начнём с деталей уязвимости.
Уязвимая версия Trust Wallet и bx имеют один и тот же базовый фатальный недостаток, заключающийся в генерации энтропии кошелька на основе непригодного алгоритма MT19937 Mersenne Twister. bx дополнительно использует для сида MT19937 некоторую информацию о часах, но на практике это не имеет большого значения для удалённой оффлайн атаки перебором. Это всё те же ограниченные 32 бита ключевого пространства, которые злоумышленники могут прочесать вдоль и поперёк.
Насколько нам известно, Trust Wallet создает только 12-словные кошельки на основе мнемоники BIP39, что фиксирует требования к энтропии (и, соответственно, к использованию ГПСЧ) на уровне 128 бит. bx более гибкий и генерирует 128-, 192-, 256-битные выходы (и другие). Больше вариантов — больше пространства для поиска, но пересекаются ли кошельки, сгенерированные с помощью bx seed -b 128, с кошельками, созданными Trust wallet?
Оказывается, нет — из-за нюанса в использовании ГПСЧ. Алгоритм MT19937 Mersenne Twister, используемый Trust Wallet и bx, одинаков, но выход потребляется несколько по-разному.
При заполнении массива энтропии данными ГПСЧ Trust Wallet потребляет один выход MT19937 размером 32 бита, берёт для заполнения массива младшие 8 бит этого выхода и выбрасывает остальные 24 бита через & 0x000000ff в wasm/src/Random.cpp:
// Copyright © 2017-2022 Trust Wallet. //
[...]
void random_buffer(uint8_t* buf, size_t len) {
std::mt19937 rng(std::random_device{}());
std::generate_n(buf, len, [&rng]() -> uint8_t { return rng() & 0x000000ff; });
return;
}
В силу используемых в libbitcoin-system механизмов C++ для pseudo_random::fill(), его код выполняет то же самое усечение 32 бит -> 8 бит, но с точностью до наоборот — забирая из ГПСЧ 8 старших битов.
В результате все выходные данные bx seed, насколько нам известно, находятся в совершенно ином ключевом пространстве, чем то, которое генерирует код Trust Wallet, а значит, и мнемоники BIP39 также отличаются.
Чтобы подтвердить это, проведём небольшой эксперимент. В отчёте Ledger Donjon приведён конкретный пример кошелька, который описывается следующим образом:
[...]
RNG seed: 0x8ec170a8
Mnemonic:
sorry slush already pass garden decade grid drip machine cradle call put
[...]
Если пересчитать RNG seed 0x8ec170a8 с помощью кода bx для 12-словной мнемоники BIP39, то получится следующее:
local chef load churn future essence type leave program weird ancient owner
Это подтверждает, что процесс генерации кошельков действительно отличается.
В конце Ledger Donjon приводит следующую строку, которую, на наш взгляд, можно считать предвестием:
“В ходе нашего расследования мы также заметили, что среди уязвимых адресов есть несколько штук, которые были сгенерированы задолго до релиза Trust Wallet. Это, вероятно, означает, что данная уязвимость существует и в других реализациях кошелька, что не может не вызывать беспокойства…”
Однако мы подозреваем, что эти другие кошельки, замеченные Ledger Donjon, не были сгенерированы версиями bx 3.x, если только Ledger Donjon не экспериментировал с выводами ГПСЧ и не натолкнулся на другую модель использования. Наличие других пострадавших кошельков в 12-словном диапазоне Trust Wallet позволяет предположить, что существуют и иные инструменты генерации кошельков, использующие MT19937. Из-за нехватки времени мы пока не проводили дальнейшее исследование.
Если сравнивать нашу задачу по раскрытию информации по bx с задачей Ledger’а по Trust Wallet, то можно отметить, что в том случае раскрытие информации осуществлялось одному коммерческому и, очевидно, хорошо финансируемому производителю кошельков. Судя по публикациям, Trust Wallet имел прямые каналы связи с отдельными пользователями, которые он мог использовать для выборочного информирования их об уязвимостях, связанных с их кошельками. Кроме того, их кошельки были уязвимы лишь в течение ограниченного времени, и проблема была обнаружена относительно быстро, при этом многие пользователи продолжали использовать приложение как обычно.
В своем отчёте команда Trust Wallet называет общую сумму потерянных средств в 170 000 долларов США:
“Несмотря на все наши усилия, было совершено два эксплойта, в результате чего общая сумма потерянных средств на момент атаки составила около 170 000 долларов США.”
Как мы поняли, Trust Wallet решил материально стимулировать пользователей, чтобы они перевели свои средства в безопасное место, а также возместить потерянные средства пострадавшим от кражи. Кроме того, за скоординированное раскрытие информации они выплатили Ledger Donjon значительное вознаграждение в размере 100 000 долларов США.
К нашему случаю, к сожалению, большинство этих факторов неприменимы. Учитывая некоммерческий характер проекта Libbitcoin, отсутствие прямых каналов связи с конечными пользователями, многолетнюю историю существования пострадавших кошельков и, вероятно, широкое распространение импортируемых/экспортируемых ключей и мнемоник BIP39, ситуация для исследования безопасности и поиска проблем здесь гораздо сложнее.
С учётом уже опубликованных отчётов Trust Wallet и Ledger Donjon, компрометации всех закрытых ключей bx seed и продолжающейся активной эксплуатации кошельков bx, нам стало ясно, что долгое согласованное раскрытие информации никому не поможет. Если мы хотим дать владельцам пострадавших bx кошельков шанс сохранить свои средства, то нам нужно стремиться к тому, чтобы публикация заняла несколько дней, а не месяцев. В данной ситуации время на стороне злоумышленников, а не жертв.
Хорошо зарекомендовавшие себя программы обеспечения безопасности, такие как Google Project Zero, придерживаются аналогичной политики в отношении активно эксплуатируемых уязвимостей:
“[...] если Project Zero находит доказательства того, что уязвимость активно эксплуатируется против реальных пользователей, то вместо 90-дневной политики вводится 7-дневная политика раскрытия информации. [...]”
Текущие кражи в разных блокчейнах - некоторые факты
Ввиду ограниченности по времени мы решили сфокусироваться на Биткойне как основной сети. Из-за гибкости вывода bx seed могут быть затронуты средства во всех монетах, совместимых с мнемоникой BIP39 (или даже просто с сидом BIP32). При наличии основной работы и ограниченных временных и вычислительных ресурсов проделывать последовательность шагов по 100 раз не представлялось возможным. Поэтому данный обзор посвящен только Биткойну и даёт лишь частичное представление о действиях в рамках кражи. Приведённые цифры можно рассматривать как нижнюю границу общего объема пострадавшей денежной массы.
Для Биткойна можно выделить два основных кластера транзакций, которые, по нашему мнению, были злонамеренными:
1.) Крупная кража 2023-07-12:
| Дата | транзакция | адрес получателя | примерный объём | замечено |
|---|---|---|---|---|
| 2023-07-12 10:41 | 593e11588a2529ed.. | 3GMQRwh8Yz1WVftL.. | ~5.0538 | 14 адресов |
| 2023-07-12 10:41 | 81cfe97cc16a4939.. | 3LwDzjA1xH8amCHu.. | ~9.744 | > 300 адресов |
| 2023-07-12 10:41 | a22b33a9a4ca0de2.. | 3D2mKf28exn26v7B.. | ~9.744 | > 1200 адресов |
Мы приписываем их одному хорошо подготовленному вору, который похитил около ~29,65 BTC на сумму до 850 000 долларов США, и это только в биткоинах (по курсу на 8/2023). Мы считаем, что, скорее всего, в тот же день этот взломщик совершил кражу и других монет. На момент написания поста эти средства никуда не переместились с этого нового места.
- Существует и вторая, более ранняя модель движения средств, которая, как мы полагаем, также является кражей:
Это поведение началось 3 мая 2023 г. и состояло из множества небольших операций по зачистке кошельков, которые продолжались до 15 июля. В общей сложности, по нашим оценкам, сумма этих зачисток составила около 0,33 BTC. Средства были ещё раз перемещены на другие адреса, что отличает эту модель поведения от описанной выше.
Мы не видели сообщений о похищении средств с этих адресов. Теоретически этот особый пользователь мог законно перемещать свои средства из множества различных кошельков (см. дальнейший анализ), или же он всё-таки тоже является злоумышленником. Адреса, приведённые ниже, связаны между собой тем, что были отмечены в одной и той же транзакции, что указывает на то, что это один субъект или группа.
Адреса предполагаемого злоумышленника:
- bc1qdmpx2th8h7l4j0z93sxnrlpuaxfvkkfxlv7n2c (0.1382 BTC)
- bc1qpnq4q7dcgvpuz8z9dy3d3jp4vuumw0hte4d32n (0.0700 BTC)
- bc1qf9q85mt73sr0vzlqkvy75pyal3g5w2ca7zx3cv (0.0045 BTC)
- bc1q5rcm7gcl3n50q93xcwwz5una7xy89unlqhy075 (0.1147 BTC)
Вот список некоторых известных монет с подтверждёнными случаями воровства (до публикации данного расследования):
- Bitcoin (BTC)
- Ethereum (ETH)
- Ripple (XRP)
- Dogecoin (DOGE)
- Solana (SOL)
- Litecoin (LTC)
- Bitcoin Cash (BCH)
- Zcash (ZEC)
Вероятно, речь идет о гораздо большем количестве монет, поскольку дополнительные затраты на проведение данной атаки на другие монеты в затронутых кошельках ограничиваются временем на R&D, необходимым для идентификации и вывода средств. Мы надеемся, что есть шанс поймать злоумышленников за счёт отслеживания по конкретным монетам их идентификации и методов работы, но пока нам нечего сообщить по этому поводу.
В целом, по нашим оценкам, в рамках этого взлома было перемещено криптовалютных активов на сумму более 900 тыс. долларов США (по курсу 8/2023), хотя некоторые из пострадавших кошельков могли быть опустошены посредством других уязвимостей.
Поиск кошельков — мотивация и ограничения
Основная причина оперативного поиска пострадавших кошельков Bitcoin заключалась в оценке общего ущерба с точки зрения объема похищенных и оставшихся средств, а также в техническом подтверждении того, что уязвимость реальна и является единственным объяснением первоначальной кражи. У нас была надежда, что если мы обнаружим значительные остатки средств, то будет небольшой, но ненулевой шанс каким-то образом сообщить об этом законному владельцу, чтобы он смог спасти их. На этапе планирования мы рассматривали варианты передачи предупреждения владельцу кошелька через централизованную биржу, используемую для прямых депозитов, поиска владельцев кошельков с публичными адресами или обнаружения других каналов обратной связи. В крайнем случае, мы могли бы продлить запланированное время раскрытия информации на время поиска вариантов.
В конечном итоге, по состоянию на дату публикации расследования мы не обнаружили в анализируемых диапазонах сети Bitcoin существенных остатков средств, превышающих порог в $5000, связанных с кошельками, сгенерированными на версиях bx3.x. Вполне возможно, что на аналогичных кошельках того или иного типа с несколько иными путями создания остаются и более крупные суммы средств, однако в ходе проведенного анализа мы их пока не обнаружили. Также возможно, что некоторые владельцы кошельков используют дополнительные пароли BIP39, которые обеспечивают умеренную или сильную защиту в зависимости от стойкости пароля и других факторов, что значительно затрудняет обнаружение.
Мы хотели бы внести ясность относительно наших действий в рамках расследования:
- Двум первоначально описанным жертвам удалось вернуть небольшую часть содержимого собственного кошелька, которую не успели украсть, используя свой обычный сетап кошелька, который был у них до кражи.
- Мы не перемещали никаких средств ни в одном из кошельков неизвестных жертв, ни одну монету.
- Мы не знакомы и никак не связаны со злоумышленником(-ами), похитившим эти средства.
В общем, никто из участников нашей группы не перемещал средства, законным владельцем которых он не является, и не знает личностей похитителей.
Насколько нам известно, даже при идеальных гипотетических обстоятельствах перемещение чужих средств во избежание их хищения злоумышленниками может обернуться юридическим кошмаром. Даже при самом идеальном сценарии — когда есть одна жертва, чёткие доказательства права собственности, хорошо установленные личности с обеих сторон, единая юрисдикция и надёжные каналы связи — это сопряжено с большим риском.
В нашей ситуации обстоятельства были существенно хуже, чем в идеальном случае, по большинству возможных критериев. Поэтому любой сценарий, связанный с перемещением чего-либо ценного, чем мы не владели до раскрытия информации, представлялся нам шквалом юридических проблем. (Помните: мы не юристы, и это не юридическая консультация; мы также не финансисты, поэтому это также не финансовая консультация).
Тем читателям, которые готовы пойти на авантюру и выступить в роли "белого рыцаря", предлагаем рассмотреть следующую дилемму: любой злоумышленник может вычислить закрытые ключи владельца, что сводит на нет ценность любых криптографических подписей с этими ключами. В любом сценарии, предусматривающем перевод средств владельца кошелька на другой кошелёк, который контролируете вы, как вы узнаете, что человек, появившийся в вашем директе (или на пороге вашего дома) с просьбой вернуть "свои" средства, является их законным владельцем? Особенно если нет централизованной структуры, такой как крупная биржа, которая бы поддержала это заявление, предоставив хотя бы какие-то доказательства?
Вот такая непростая ситуация! И это даже не затрагивает других проблем, таких как соблюдение законов о борьбе с отмыванием денег (AML), налоговой отчётности и множества других аспектов.
Если вы являетесь одним из пострадавших от кражи: нет, у нас точно нет ваших средств и мы не знаем, как их вернуть. Извините!
Если вас это утешит, мы приложили все усилия, чтобы дать нашим пострадавшим друзьям и всем остальным жертвам хотя бы какое-то объяснение и понимание того, почему вообще это оказалось возможно, и помочь им избежать повторных проблем из-за той же уязвимости.
Поиск кошельков – реализация
bx seed -b 128bx seed -b 192 (по умолчанию)bx seed -b 256Мы использовали общедоступный список всех биткойн-адресов, которые исторически были замечены в сети Bitcoin, и построили на этом наборе данных фильтр bloom с очень низким коэффициентом ложных срабатываний. Используя этот фильтр, мы смогли быстро прошерстить адреса, найти и отбросить множество неиспользуемых кошельков, соответствующие производные аккаунты которых никогда не были замечены в сети, не прибегая к затратному поиску на фулл-ноде Bitcoin.
Мы рассматривали только те кошельки, где был использован первый адрес. Стандарт предписывает сканировать первые 20 адресов, но вычисление адресов является узким местом в нашем поиске. Также мы учитывали только кошельки, использующие стандартные пути деривации, указанные в BIP44, BIP49 и BIP84. В общей сложности мы обнаружили более 2600 различных активно используемых криптовалютных кошельков Bitcoin, основанных на слабой энтропии bx seed.
Большинство этих кошельков, группа из более чем 2550, имеют странно схожий паттерн использования: небольшие депозиты примерно в одни и те же даты в 2018 году. Мы считаем, что это результат использования автоматического инструмента bx и что у этих кошельков может быть один и тот же владелец. Мы не уверены, в чем заключался тот эксперимент, однако все они находятся в диапазоне сид вывода 256-бит и имеют тип адреса BIP49 (префикс '3'), что позволяет немного отличить их от других адресов.
Исключив это большое количество особых кошельков, мы выявили менее 50 кошельков с более индивидуальным паттерном использования, который мы связываем с тем, что владельцы — реальные люди. Они распределены по указанным диапазонам и типам адресов. Мы знаем, что наше исследование неполное, поскольку мы не обнаружили всех кошельков, попавших под зачистку, которая наблюдалась на блокчейне.
В этом наборе кошельков нам удалось найти и идентифицировать оба кошелька первоначальной жертвы. Это даёт нам уверенность в том, что наше расследование причин кражи на верном пути.
На значительной части обнаруженных индивидуальных кошельков не было BTC до 2023 года, поэтому злоумышленники не могли вывести с них деньги. Однако мы по-прежнему считаем их пострадавшими по двум причинам:
- Любые новые депозиты на них подвергаются риску немедленного автоматического хищения (как описано в статье Ledger Donjon).
- Доступ к закрытым ключам кошельков позволяет восстановить все производные адреса на всех монетах, связав с ними предыдущие действия владельца кошелька, что существенно влияет на конфиденциальность.
Другие подтвержденные жертвы этой кражи
Через несколько дней после крупной кражи, произошедшей 12 июля 2023 г., на Reddit появилась информация от одного из пострадавших:
Пользователь Reddit u/0n0t0le представляет собой интересный пример, поскольку он потерял ~0,25 BTC, но смог переместить и сохранить более 1,05 BTC, которые злоумышленники на тот момент не нашли или не забрали.
В этом случае 1) мы смогли подтвердить, что пользователь хранил свои средства на уязвимом кошельке в диапазоне bx seed.
Обратите внимание, что мнемонические секреты BIP39, как и другие закрытые ключи кошельков, обычно не содержат чётких указаний на программное обеспечение, с помощью которого они были сгенерированы. Поскольку формат предназначен для экспорта и импорта между различными программами для работы с кошельками, пользователи могут не совсем чётко или правильно помнить, где был создан тот или иной кошелёк. Это может привести к появлению большого количества путаной информации о другом потенциально затронутом ПО кошельков и затруднить поиск фактов.
Пожалуйста, рассматривайте это как предварительное указание на возможные проблемы. Вероятно, что в некоторых из публичных краж уже использовалась уязвимость генератора случайных чисел в версиях до bx 3.0.0, но пока мы этого не подтвердили.
Мы планируем провести дополнительные исследования.
Основные уроки
- Используйте для своих кошельков фразы-пароли BIP39, в идеале — сложные пароли, основанные на энтропии из отдельного источника.
- Доверяйте генерацию кошельков только тщательно проверенному ПО.
- Документируйте каждый сетап при генерации кошелька, это может оказаться очень важно для вас в будущем.
Удачи :)
На главную