Никто не проверяет 42 символа
Атаки, направленные на подмену адресов, отравление адресной книги и перехватчики буфера обмена делают одну и ту же ставку: вы сравниваете только несколько первых и последних символов адреса, и не сравниваете каждый символ. Мы написали небольшую open-source-библиотеку, которая представляет адрес в виде понятного изображения, с дорогой возможностью подделки
3 мая 2024 года кто-то отправил незнакомцу 1 155 WBTC, в тот день около 68 млн долларов.
Сначала он отправил на правильный адрес тестовый платёж в 0,05 ETH и только потом перевёл основную сумму. В промежутке злоумышленник отправил ему небольшую сумму с адреса, который начинался на 0xd9A1 и заканчивался на 853a91, в точности как тот, на который только что ушёл платёж. Когда жертва вернулась в историю, чтобы скопировать получателя, там было две записи вида 0xd9A1…3a91. Она выбрала не ту. (CoinDesk, Blockaid)
У этой истории необычный конец: после недели ончейн-сообщений и предложения вознаграждения в 10 % злоумышленник вернул около 22 960 ETH. Однако большинству жертв злоумышленники средства не возвращают.
Меня зовут Дмитрий, я занимаюсь разработкой кастодиальных и некастодиальных решений для блокчейн. Сейчас мы делаем SoikaWallet, некастодиальный десктопный кошелёк с упором на безопасность с AirGap. Когда мы проверяли собственный экран отправки на устойчивость к этой атаке, честный итог был коротким: наша единственная защита состояла в том, чтобы показать адрес целиком и надеяться, что пользователь его прочитает. Эта статья о том, почему это не работает, особенно для длинных адресов вроде SUI, как мы изучали не только устойчивые алгоритмы для GPU брутфорса, но и исследования восприятия человеком информации, цветов, а также, как эти атаки выглядят вблизи и что привело к особенностям решения, которое мы применили и опубликовали.
Подмена, которая происходит после Ctrl+C
Более старая из двух атак — перехватчик буфера обмена, он же клиппер. Он может запускаться в пользовательской среде хоста пользователя, не требуя повышения прав, следит за буфером обмена и, когда там появляется что-то похожее на криптоадрес, заменяет его адресом злоумышленника. В итоге, вы копируете один адрес, а вставляете другой.
Это не новость и не редкость. CryptoShuffler, о котором писала «Лаборатория Касперского», было украдено таким способом 23 BTC ещё в 2017 году. Unit 42 из Palo Alto Networks в 2018 году описал ComboJack; тот опрашивал буфер обмена каждые полсекунды. В том же году Bleeping Computer разобрал образец, который работал с более чем 2,3 млн адресов. В феврале 2019 года ESET сообщила о первом клиппере в Google Play, замаскированном под MetaMask. В 2023 году «Лаборатория Касперского» насчитала около 16 000 обнаружений в 52 странах троянизированного Tor Browser с клиппером внутри и примерно 400 тысяч долларов, украденных за тот год. 17 июня 2026 года Microsoft и Check Point опубликовали по свежей клипперной кампании. В один и тот же день, и это разные, никак не связанные между собой вредоносы.
У ранних клипперов была слабость: вставленный адрес был совсем не похож на скопированный, и пользователь, взглянувший на первые символы, замечал подмену. Поэтому вредоносы научились подбирать замену, которая выглядит правильно. Тот, что описала Microsoft, подставляет биткоин-адрес, начинающийся с тех же двух символов, что и ваш. Грубо, но идея работала.
Laplas Clipper, который продаётся по подписке с 2022 года, отправляет скопированный вами адрес на свой сервер и получает в ответ двойник. Bleeping Computer проверил его: возвращённый биткоин-адрес совпадал с оригиналом по нескольким первым и последним символам и приходил примерно за пять секунд.
И подмене больше не обязательно происходить в буфере обмена. Она переехала внутрь приложения:
-
В сентябре 2025 года через угнанный фишингом аккаунт мейнтейнера npm были опубликованы вредоносные версии
chalk,debugи ещё шестнадцати пакетов, на которые в сумме приходится больше двух миллиардов загрузок в неделю. Полезная нагрузка исполнялась в браузере, перехватывалаfetch,XMLHttpRequestиwindow.ethereumи переписывала любой адрес, который встречала в данных страницы или в исходящей транзакции. Замену она выбирала из 280 зашитых в код адресов по расстоянию Левенштейна, то есть брала тот, что больше всего похож на оригинал. Её обнаружили за два часа, и она почти ничего не заработала. Важен сам замысел. -
В апреле 2025 года ReversingLabs нашла npm-пакет, который патчил установленные кошельки Atomic и Exodus прямо на диске, так что подлинный интерфейс кошелька перенаправлял исходящие средства. Удаление пакета этого не отменяло.
-
«Доктор Веб» описал поддельные Android-телефоны, которые поставлялись с модифицированным WhatsApp, подменяющим адреса внутри сообщений. На экране отправителя правильный адрес. На экране получателя — адрес вора.
Разная доставка, одно допущение: человек в конце цепочки сравнит несколько символов с каждого края и нажмёт «подтвердить».
Перевод, которого вы не просили (спам переводами от похожих адресов)
Отравлению адресов вредоносное ПО не нужно вовсе. Всё происходит в блокчейне, у всех на виду.
Злоумышленник следит за переводами. Вы платите Алисе. Через несколько минут в вашей истории появляется новая строка с адресом, который начинается и заканчивается так же, как адрес Алисы. В следующий раз, когда вы платите Алисе, вы открываете историю, копируете «её» адрес, а он не её.
Исследователи из Университета Карнеги — Меллона (CMU) в статье на USENIX Security 2025 делят приманки на три вида:
-
Пыль (dust). Крошечная сумма настоящего токена, отправленная с адреса-двойника. В их данных 98,8 % таких переводов меньше 3 долларов.
-
Переводы с нулевой суммой. ERC-20
transferFromс суммой 0 не требует разрешения (allowance), поэтому кто угодно может сгенерировать событиеTransferс вашего адреса на свой двойник. В вашей истории это выглядит как что-то, что отправили вы. -
Поддельные токены. Фальшивый контракт с символом «USDT» может генерировать любые события.
Их цифры за два года по Ethereum и BSC: 270 млн отравляющих переводов, 17,4 млн адресов-целей, 6 633 платежа, ушедших на двойник, потери не меньше 83,8 млн долларов. Случай с WBTC в эту сумму даже не входит: их детектор этот токен не отслеживал. Доля успешных попыток на Ethereum составила около 0,01 %. Один шанс из десяти тысяч — более чем достаточно, когда попытка стоит центы. Chainalysis разобрала кампанию, стоявшую за кражей WBTC: 82 031 засеянный адрес-двойник, и даже после возврата 68 млн долларов оператор остался с прибылью около 1,49 млн долларов, то есть с доходностью больше 1 100 %.
Аттак стало больше, когда газ стал дешевле. После того как обновление Ethereum Fusaka в декабре 2025 года снизило комиссии, Blockaid насчитала 628 000 попыток отравления в ноябре 2025 года и 3,4 млн в январе 2026-го. По оценке Coin Metrics, стоимость пыли стала составлять около 11 % всех транзакций в Ethereum, а самый активный отправитель в одиночку дотянулся до более чем миллиона адресов, потратив примерно 5 тысяч долларов в токенах.
19 декабря 2025 года трейдер вывел с Binance около 50 млн долларов в USDT, отправил тест на 50 USDT на правильный адрес, а через 26 минут отправил 49 999 950 USDT на двойник. Обратите внимание, что сделал этот человек: тестовый перевод. Это стандартный совет. И, по всей видимости, именно тестовый перевод подсказал злоумышленнику, какой адрес имитировать.
«Проверяйте каждый символ» — это не совет
Каждое руководство от производителей кошельков заканчивается одинаково: проверьте адрес целиком. У Trezor написано «внимательно проверяйте каждый символ». Ledger, надо отдать ему должное, добавляет, что «люди не машины: мы бываем ленивы, мы бываем в спешке».
Даже внимательные пользователи, всё равно ошибаются. В айтрекинговом исследовании на CCS 2018 участников прямо просили сверить 64-значную контрольную сумму, в которой от оригинала были сохранены только первые десять и последние десять цифр. 38 % из них пропустили несовпадение. Tan et al. (CHI 2017) смоделировали атакующего, который подбирает начало и конец шестнадцатеричного отпечатка: прошёл 21 % атак. Один из участников описал метод, которым пользуемся мы все: «Сначала я проверил последнюю группу цифр, потом посмотрел наугад… пока не почувствовал, что проверил достаточно».
Кошельки облегчают атакующему задачу тем, что вообще не показывают середину. 0xd9A1…3a91 — это принятое в дизайне соглашение, и авторы из CMU считают, что именно оно объясняет, почему крупнейшая группа отравителей майнила двойники двух уровней: одни для кошельков, которые показывают по три-пять символов с каждого края, другие для эксплореров, которые показывают по шесть-семь. MetaMask сказал это прямо в июне 2026 года, когда выпускал функцию обнаружения отравления: усечение «и есть то самое слепое пятно, которым пользуются мошенники».
Сколько стоит подбор похожего адреса? Open-source-генератор profanity2 заявляет 1 361 млн адресов в секунду на одной RTX 4090. Совпадение первых четырёх и последних четырёх шестнадцатеричных цифр — это 232 попыток. Три секунды. По оценке команды CMU, самая сильная из отслеженных ими групп, подбиравшая до 20 цифр, сожгла на это около 27 000 GPU-дней. И всё равно осталась в плюсе.
Почему бы просто не использовать идентиконы?
Это и была моя первая идея. Кошельки уже рисуют маленькие иконки рядом с адресами. Разве двойник не получит другую иконку?
Часто не получает. Jazzicon в MetaMask, насколько я могу судить по коду, инициализируется значением parseInt(address.slice(2, 10), 16): первые восемь шестнадцатеричных цифр, 32 бита. Двойник, совпадающий по этим восьми цифрам, получает ту же иконку, пиксель в пиксель. Blockies скармливает строку адреса маленькому некриптографическому генератору и берёт цвета из непрерывного диапазона; ещё в 2018 году Остин Гриффит написал майнер Blockies, и близнец, достаточно похожий, чтобы пережить беглый взгляд, нашёлся через 45 минут.
Мы провели собственную версию этого эксперимента: 16,7 млн случайных кандидатов на каждую цель, из них оставляем пять самых похожих.
Есть проблема и глубже, и она касается нас тоже. Tan et al. проверяли и картинки: с randomart из OpenSSH и абстрактными изображениями Vash люди пропускали 10–12 % атак, а с милыми аватарами-единорогами 54 %, худший результат во всей статье. Люди сравнивают общее впечатление от изображения, а атакующий в первую очередь подбирает самые заметные черты. Любую визуальную схему нужно проектировать с учётом этого, и она должна честно говорить, где заканчиваются её возможности.
Что мы сделали
Humanized Hash, коротко hh, конвертирует адрес, публичный ключ или любой хеш в сетку 4 × 4. Ячейка либо пуста, либо содержит одну сплошную фигуру: квадрат, круг или треугольник, направленный в одну из четырёх сторон, одного из четырёх цветов. Выглядит немного как идентикон GitHub, и это было намеренно.
Правила дизайна выросли скорее из работ по юзабилити, чем из криптографии:
-
Мало категориальных значений, никаких градиентов, никакой мелкой текстуры. Hsiao et al. выяснили, что 24 бита, закодированные восемью различимыми цветами, на трудных парах сравнивались с точностью около 85 %; те же 24 бита в 64 мелких цветовых ступенях скатывались до подбрасывания монеты.
-
Равная заметность. У каждой ячейки одинаковый размер и одинаковый набор возможного содержимого, так что нет одной доминирующей черты, которую подделыватель подобрал бы первой. Это урок единорогов.
-
Содержимое, которое можно назвать словами. «Фиолетовый треугольник влево, левый верхний угол» работает по телефону.
-
Хешируются байты, а не строки. Написания одного EVM-адреса с контрольной суммой, в нижнем регистре и в верхнем дают одну и ту же картинку.
Номинально картинка несёт 68 бит, из них 44 — в одних только фигурах. Наш первый черновик был сеткой 3 × 3 с 16 глифами. Он терял около 30 бит, а глифы при 32 пикселях превращались в кашу, так что он отправился в корзину.
Рядом с картинкой стоит шестисимвольный тег вроде W2D-2K6. К тому, зачем он нужен, я ещё вернусь.
Под капотом ничего экзотического: SHA-256, HMAC и PBKDF2, входы с префиксом длины и разделением доменов. Никакой самодельной криптографии. У библиотек нет сторонних зависимостей (SHA-256, deflate и PNG-eнкодер мы написали сами), используется только целочисленная арифметика, и они выдают одни и те же байты на C++, Kotlin, TypeScript, Go и Python, что проверяется общими эталонными векторами. Лицензия MIT. У алгоритма нет версии, и он никогда не изменится, потому что картинка, которую пользователь однажды выучил, должна остаться той же навсегда.
Сколько стоит подделка
Это часть, к которой мы уделили больше всего внимания, поэтому вот как получены цифры. Это расчёты, а не измеренная атака.
Скорость атакующего. Берём 1,36 млрд адресов в секунду, которые profanity2 выдаёт на RTX 4090, и округляем вверх до 1,4 млрд. (CMU намерили 516 млн на более старой RTX 8000, так что это щедро по отношению к атакующему.)
Текст. Совпадение d шестнадцатеричных цифр требует в среднем 16d попыток. По четыре с каждого края: 232, три секунды. По шесть с каждого края: 248, 2,3 дня. По восемь с каждого края: 264, около 420 лет.
Картинка. Прежде чем что-либо генерируется, вход проходит через 16 384 итерации PBKDF2-HMAC-SHA-256. Для нас это около 7 мс на десктопном ядре и от 12 до 33 мс на Pixel 5a, один раз на адрес, с кешированием. Подделыватель платит это на каждой попытке, и по нашей оценке это добавляет около 11 бит работы, что превращает 1,4 млрд попыток в секунду примерно в 700 000.
Дальше вопрос в том, сколько нужно попыток. Идеальная подделка должна совпасть по всем 16 ячейкам: 268. Но люди сравнивают неидеально, поэтому мы оцениваем и небрежную подделку, которой разрешено отличаться от цели в любых двух ячейках: около 252.
Итак: 252 попыток с растяжением — это около 210 GPU-лет, 268 — около 14 миллионов. А подделыватель, которому нужно совпадение и по краям текста, и по картинке, платит произведение двух этих величин.
Теперь оговорки, потому что их две, и обе серьёзные.
Во-первых, GPU можно арендовать тысячами, и массовый атакующий не работает над одной жертвой (rainbow tables). Статья CMU показывает группы, которые проверяют каждого кандидата сразу против примерно миллиона целей, а это делит стоимость на одну жертву на миллион. 210 лет, делённые на миллион, — меньше двух часов. Для картинки, которую может вычислить кто угодно, это не очень хорошо, но это решается с помощью salt (keyed режим).
Во-вторых, люди надёжно различают лишь около 30 бит визуального хеша, а по памяти ещё меньше. Картинка, которая выглядит иначе, доказывает, что адрес другой. Картинка, которая выглядит так же, — сильное свидетельство, но не доказательство. Для этого и нужен тег: шесть символов, которые можно сравнить точно, глазами или голосом.
Картинка, которую может нарисовать только ваша экосистема
Ответ на первую оговорку — второй режим, и, насколько я знаю, ни один кошелёк сегодня не делает этого со своими идентиконами.
В keyed-режиме отпечаток — это HMAC от растянутого дайджеста под 32-байтовым секретом, который принадлежит вашему кошельку. Атакующий, у которого нет ключа, не может вычислить картинку, которую увидите вы. Перебирать не по чему. Его адрес-двойник получит какую-то случайную картинку, никак не связанную с той, которую вы знаете за Алисой, причём не в силу 252 попыток, а по построению.
Отравление и подмена в буфере обмена целятся в один кошелёк за раз, и это ровно тот случай, который здесь закрыт. Ключ выводится из сида кошелька по собственному пути (SLIP-0021), поэтому его не нужно резервировать, а восстановленный кошелёк рисует те же картинки. Приятный побочный эффект: опечатайтесь в парольной фразе BIP-39, и все картинки окажутся незнакомыми.
Universal-режим остаётся для случаев, когда сравнивают две стороны: экран получения, показанный плательщику, платёжный запрос, чат поддержки. Растяжение нужно, чтобы сделать атаку на этот режим настолько дорогой, насколько мы можем себе позволить. Два режима намеренно дают не связанные между собой картинки, а картинки с ключом обведены рамкой, чтобы было видно, на какую из них вы смотрите. Перепутать их можно только в сторону ложной тревоги, но не ложного совпадения.
Четыре цвета, подобранные для тех, кто видит их иначе
Примерно у одного мужчины из двенадцати (5% мужского населения) есть особенности цветовосприятия. Индикатор безопасности, который не работает для 5% пользователей-мужчин, — плохой индикатор, поэтому цвет с самого начала был вторичным каналом: 44 из 68 бит сидят в форме и положении, а они переживают любой тип цветового зрения, оттенки серого и JPEG.
Цвета всё равно нужно было подобрать с умом. Мы следовали методу из работы Petroff «Accessible Color Sequences» (2021): для каждой пары цветов вычислить перцептивное расстояние в CAM02-UCS при нормальном зрении, при протаномалии и дейтераномалии с выраженностью от 10 до 100 % (модель Machado et al. 2009) и при тританопии (Brettel et al. 1997). Оценка палитры — её худшая пара при худшем условии. Нашим порогом был минимум 20 плюс контраст по WCAG не меньше 3:1 и на белом, и на тёмном фоне #121212, потому что у кошельков есть тёмные темы.
Известные палитры не прошли. Первые четыре цвета Okabe-Ito набрали 12,5 при тританопии и всего 2,25:1 на белом. Первые четыре цвета самого Petroff провалились по контрасту. Палитра, которая нравилась мне на глаз, тоже провалилась (16,4 при дейтеранопии). В релиз пошла та самая палитра, подобранная вручную и подправленная оптимизатором в пределах 12 единиц CAM02-UCS от оригинала, пока она не прошла: #7A96C5, #890AF0, #C10445, #D48200. Оценка 24,1. Самая слабая пара — красный против оранжевого при полной дейтеранопии.
Четыре цвета — меньше восьми, которые допускает литература. С таким требованием к контрасту на светлом и тёмном фоне одновременно четыре — это то, что осталось различимым для всех.
Место hh в SoikaWallet
hh — standalone библиотека без зависимостей, но мы делали её для SoikaWallet. SoikaWallet — некастодиальный мультичейн-кошелёк для десктопа (Bitcoin, EVM, Tron, Solana, Sui, TON), написанный на C++, с режимом AirGap: ключи находятся на телефоне, который никогда не выходит в сеть, а транзакции попадают к нему в виде QR-кодов, по NFC или Bluetooth.
Картинка выполняет там две задачи.
Проверка получателя. Картинка отображается рядом с полем получателя на экране отправки и обновляется в момент вставки адреса, а потом появляется снова на экране подтверждения. Сохранённые контакты и строки истории тоже несут её. Если клиппер подменил то, что вы вставили, или вы скопировали отравленную строку, картинка рядом с полем — не картинка Алисы. Для первого платежа кому-то новому кошелёк показывает универсальную картинку, чтобы можно было сравнить её с тем, что получатель видит на своём экране получения.
Не потеряться в собственном кошельке. HD-кошелёк обрастает десятками аккаунтов в разных сетях, и все они — строки вида 0x…. Каждый аккаунт получает свою картинку в списке аккаунтов и на экране получения. Через неделю вы узнаёте «тот, с оранжевым квадратом в углу» так же, как узнаёте иконку приложения. У одного EVM-адреса одна и та же картинка во всех EVM-сетях, потому что сеть в хеш намеренно не подмешивается.
С AirGap появляется третья задача. Телефон сам декодирует запрос на транзакцию и генерирует получателя заново, с той же приватной картинкой и тем же тегом. Офлайн-телефон — это экран, которому вы доверяете. Если что-то на онлайн-машине изменило адрес после того, как вы его проверили, два экрана разойдутся, а сравнить две картинки бок о бок — секунда, тогда как сравнение двух 42-символьных строк на двух устройствах требует терпения, которого нет ни у кого.
Чего hh не делает
Стоит не забывать
-
Не помогает пользователю, который не проверяет. Картинка, которую никогда не сравнивают, никого не защищает. Её место там, где принимается решение, при 64 пикселях и больше, а не в виде 16-пиксельного украшения.
-
Не заменяет проверку текста. Одинаково выглядящие картинки — свидетельство. Тег или полный адрес — уверенность.
-
Вредонос, который контролирует ваш экран или само приложение, рисующее картинку, — за рамками задачи. Если атакующий может рисовать пиксели, он нарисует правильные. Для этого и нужен второй, изолированный от сети экран.
-
Универсальные картинки, атакующий с большим бюджетом, в принципе может перебрать. Внутри кошелька используется keyed-режим.
-
Ничего не даёт при первом платеже на адрес, полученный из скомпрометированного источника. Никакой хеш не скажет вам, что адрес принадлежит Алисе. Он может сказать только, что это тот же адрес, что и в прошлый раз.
Ссылки, реализация
Код лежит на GitHub вместе со спецификацией, моделью угроз, эталонными векторами и дизайн-листами, включая те, где наши идеи провалились.
$ hh_cli 0xd9A1b0B1e1aE382DbDc898Ea68012FfcB2853a91 --out real.pngtag W2D-2K6$ hh_cli 0xd9A1C3788D81257612E2581A6ea0aDa244853a91 --out fake.pngtag X9B-KSW
Если вы делаете кошельки, эксплореры или что угодно ещё, что показывает адрес человеку, не стоит забывать об этом векторе атак. Если вы занимаетесь исследованиями в безопасности, самое полезное, что вы можете для нас сделать, — попробуйте найти пару адресов, картинки которых человек перепутает, и рассказать, сколько времени это заняло. Отчёты присылайте на dev@censync.com.
А если вы просто пользуетесь криптой: перестаньте копировать адреса из истории транзакций. Заведите адресную книгу. Вся бизнес-модель злоумышленника держится на этой строке.
Мы готовим наш кошелек SoikaWallet к публичную релизу. Сайт soikawallet.app, а следить за новостями можнно в Twitter или Telegram, тема на BitcoinTalks
Мой оригинал статьи на английском на Medium
Источники
-
Tsuchiya, Dong, Soska, Christin. Blockchain Address Poisoning. USENIX Security 2025.
-
Chainalysis. Anatomy of an address poisoning scam. 2024.
-
Blockaid. Address poisoning: the growing threat. 2026.
-
Coin Metrics. State of the Network, issue 349. 2026.
-
Cyble. Laplas Clipper. 2022.
-
Aikido. npm debug and chalk packages compromised. 2025. Security Alliance, разбор инцидента.
-
Kaspersky. Copy-paste heist. 2023.
-
Cherubini et al. Towards Usable Checksums. CCS 2018.
-
Tan et al. Can Unicorns Help Users Compare Crypto Key Fingerprints? CHI 2017.
-
Hsiao et al. A Study of User-Friendly Hash Comparison Schemes. ACSAC 2009.
-
Petroff. Accessible Color Sequences for Data Visualization. 2021. Machado, Oliveira, Fernandes. A Physiologically-based Model for Simulation of Color Vision Deficiency. 2009.
ссылка на оригинал статьи https://habr.com/ru/articles/1086804/