Очередная критическая уязвимость zero-day в кошельке Argent-X StarkNet

Эта ошибка позволяет любому вредоносному сайту полностью контролировать кошелек и совершать операции без разрешения пользователя.
Абстракция учетной записи
• Дек 7, 2022
6 мин. чтения
Очередная критическая уязвимость zero-day в кошельке Argent-X StarkNet

TL;DR

В кошельке Argent-X сети StarkNet обнаружена еще одна критическая уязвимость, на этот раз на стороне приложения. После разблокировки кошелька данная уязвимость позволяет любому сайту без участия пользователя похитить все средства со счетов в кошельке Argent-X.
Эта ошибка позволяет любому вредоносному сайту, без согласия пользователя, полностью контролировать кошелек, считывать всю информацию о счете и совершать операции без его одобрения.

Судя по всему, данная уязвимость существовала как минимум с мая 2022 года, и все пользователи Argent-X в сети StarkNet все это время находились в зоне риска.

Мы предупредили команду Argent об этой проблеме пару недель назад, и в течение 18 часов они выпустили исправление для Google Chrome. С тех пор прошло достаточно времени, чтобы подавляющее большинство пользователей получили обновление.
Эта уязвимость не позволяет отличить правомерные транзакции от вредоносных, но, к счастью, компания Argent утверждает, что никакие средства не были потеряны.

В этой заметке мы расскажем о том, как обнаружили эту уязвимость, а также продемонстрируем важность безопасного механизма взаимодействия dApp

Для тех, кто хочет выполнить описанные ниже действия и воспроизвести взлом в домашних условиях, можно скачать любую версию Argent-X до 5.0.7 (например — эту).

Опыт

Как мы увидели это всего две недели назад уязвимость zero-click была обнаружена и о ней сообщил Yoav Gaziel из нашей команды в Braavos. Он считает, что контракт на обслуживание аккаунта Argent-X имел критический недостаток в системе безопасности. Но ошибка была именно в коде контракта. Итак, предположим, что контракт Argent-X пройдет аудит (еще раз) и не будет содержать ошибок. Достаточно ли этого для того, чтобы кошелек считался защищенным? А как же на счет кода клиента кошелька? Недавно мы, сотрудники компании Braavos, решили немного потестировать его.

Ниже приводится описание этого насыщенного событиями дня и вечера.

Сообщения, сообщения

Вернемся на целый год назад: когда мы писали свое первое приложение (dApp) в сети Starknet, мы использовали единственный доступный тогда кошелек — Argent-X. (Сегодня можно и нужно использовать кошелек Braavos).
При проверке некоторых сообщений, отправленных внутри нашего dApp, мы увидели кучу сообщений, которые мы не отправляли и которые, судя по всему, исходили от самого кошелька Argent-X. В то время мы не придали этому особого значения.

Но, вспомнив об этом мы подумали, что надо бы взглянуть на фреймворк для обмена сообщениями Argent-X. Скачать и скомпилировать код кошелька не составило труда — за хорошую работу можно поблагодарить команду Argent. Одна вещь, на которую мы сразу обратили внимание, — использование Manifest V2. Мы избавим вас от подробностей, но скажем лишь, что каталог расширений Google Chrome призывает всем расширениям перейти на Manifest V3, который, по мнению Google, является более безопасным.

Теперь перейдем к рассмотрению системы обмена сообщениями Argent-X.

Несколько слов о работе кошелька Argent-X: это расширение для браузера, работающее в изолированной среде (изолированном от просматриваемой веб-страницы). Связь между dApp и кошельком осуществляется через сообщения, в виде фрагмента кода, внедряемого на каждую посещаемую вами веб-страницу. Пока все довольно стандартно.

Однако при реализации такой схемы необходимо помнить о безопасности.

Вы ведь не хотите допускать проникновения вредоносных сообщений неизвестного происхождения.

Изучив код кошелька Argent-X, мы довольно быстро нашли используемые им типы событий:

Мы погрузились в самое интригующее сообщение — MiscenalleousMessage Не только из-за опечатки (ничего страшного, никто не умеет писать это слово), но и потому, что, по нашему опыту, разработчики обычно помещают «хорошие вещи» в эти общие классы.

Инструктаж background/miscellaneousMessaging.ts, Наше внимание сразу же привлекло одно сообщение (о том, какое именно, мы расскажем дальше). Итак, мы скомпилировали кошелек Argent-X и установили его в браузер Chrome. Сразу же открылась вкладка браузера с предложением настроить кошелек, и после обычного ввода пароля кошелек автоматически сгенерировал аккаунт в testnet (тестовая сеть Goerli).

Затем мы открыли консоль и взломали этот кусок кода:

msg = { type: “RESET_ALL” }

extId = document.getElementById(«argent-x-extension»)?.getAttribute(«data-extension-id»)

window.postMessage({ …msg, extensionId: extId }, window.location.origin)

Здесь мы посылаем сообщение RESET_ALL событие на кошелек Argent-X с неавторизованной веб-страницы. Подобный взлом должен пресекаться средствами защиты кошелька.

Итак, мы выполнили этот фрагмент кода в консоли браузера и открыли кошелек Argent-X из панели инструментов.

Boom 💥

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

Напомним, что только что произошло: мы отправили со случайной веб-страницы на кошелек Argent-X сообщение RESET_ALL, и оно прошло.

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

Чтение всех аккаунтов

Следующее, что мы решили исследовать, — может ли неавторизованная веб-страница прочитать всю информацию о вашем аккаунте.

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

Просматривая код в background/accountMessaging.ts, мы увидели следующее GET_ACCOUNTS которое мы планировали использовать. Наше внимание привлек вызов sendToTabAndUi() с результатом GET_ACCOUNTS_RES . Мы поняли, что пользовательский интерфейс Argent-X, которому необходимо получить информацию о счете, скорее всего, будет отображаться пользователю при просмотре всплывающего окна Argent-X. Но зачем отправлять это на вкладку? Это похоже на недостаток безопасности.

Мы переустановили свежий кошелек Argent-X, а затем взломали этот фрагмент кода в консоли:

function print_event(e) {

console.log(e)

}

window.addEventListener(«message», print_event)

msg = { type: “GET_ACCOUNTS” }

extId = document.getElementById(«argent-x-extension»)?.getAttribute(«data-extension-id»)

window.postMessage({ …msg, extensionId: extId }, window.location.origin)

Еще раз Boom 💥 :

В результате этого потока любой сайт (новостной, правительственный и т.д.) может прочитать все ваши адреса Argent-X. Но подождите, бумажник был разблокирован. Может быть, заблокированный кошелек предотвратит этот недостаток? Мы заблокировали кошелек из пользовательского интерфейса и повторили попытку.
Результат тот же. Даже заблокированный кошелек возвращает на любой сайт список всех ваших счетов.
Заглянув в манифест Argent-X, мы увидели, что они внедряют свой код расширения на любой сайт. http, https, local… Любой из них может сделать именно это.

Святой Грааль — исполнение сделок

Здесь мы сократим историю и кратко опишем, как разворачивались события в последующие несколько часов.

Ищем в файле background/transactions/transactionMessaging.ts,
первое сообщение EXECUTE_TRANSACTION.

Это и есть священный Грааль.
Нам потребовалось несколько минут, чтобы понять, какой формат транзакции требуется, но вскоре мы разобрались:

msg = { type: “EXECUTE_TRANSACTION”, data:{transactions:{contractAddress:”0x049D36570D4e46f48e99674bd3fcc84644DdD6b96F7C741B1562B82f9e004dC7″,entrypoint:”transfer”,calldata:[“0x07074236724072a4949b7B170E5D5825b8195Ec278ED45E4BE9975186758EDa7”,”10000000000″,”0″]}} }

Это сообщение запрашивает перевод 1e-9 ETH на некоторый произвольный адрес (в данном случае это был наш собственный адрес в сети Goerli)

Мы отправили это сообщение обычным способом, но знаете, что… хотя в ответ мы получили EXECUTE_TRANSACTION_RES ответ от кошелька, транзакция вроде бы не состоялась.
Открыв пользовательский интерфейс кошелька, мы сразу поняли, почему.

Кошелек принял сообщение, но по-прежнему просил пользователя подтвердить транзакцию. Сможет ли эта линия защиты помешать хакеру осуществить эту транзакцию?

Если посмотреть на ответ, который прислал кошелек, то кажется, что там есть какой-то параметр, называемый actionHash.
Этот actionHashдолжен что-то обозначать.

Поступая естественным образом, мы погрузились в background/actionMessaging.ts, и там увидели это сообщение:

Мы вызвали это сообщение с указанными выше actionHash, и…

💥 БУМ БУМ БУМ БУМ 💥

Видите этот значок «1»?
Это несанкционированная, незапрошенная транзакция по переводу ETH, которую мы сгенерировали.

Отметим, что данный поток возможен, когда пользователь работает в Интернет с разблокированным кошельком. После разблокировки кошелька он остается разблокированным в течение длительного времени, зачастую до тех пор, пока пользователь не закроет браузер.

Собираем все воедино

Глубоко вздохнув, и просидев час мы взломали небольшой POC — javascript-страницы, которая делает все то, что мы только что описали, с добавлением сверху — используя сообщение CONNECT_ACCOUNT можно переключиться на любой счет в кошельке (включая счета в основной сети).

Было уже довольно поздно, но мы понимали, что не можем сидеть ничего не предпринимая. Мы связались с нашими товарищами из Braavos, проинструктировали их и попросили попробовать POC. Они были шокированы, но довели дело до конца. Это сработало на всех их машинах.

Пока один из нас взламывал POC, другой просматривал git-историю кошелька Argent-X, пытаясь оценить, как давно это происходит, и оказалось, что с самого первого дня. Подобной уязвимости подвержены все кошельки Argent-X, по крайней мере, с мая, когда Argent-X ввела поддержку мейннета StarkNet.

Далее мы следовали протоколу Responsible Disclosure, как и неделей ранее, когда раскрыли ошибку zero-click контракта и сообщили об этом команде Argent.

Хотя из-за этой разведки мы пропустили фантастическую игру между Испанией и Коста-Рикой (7:0!! Что??) и Бельгией и Канадой (1:0).
Мы рады, что нам удалось помочь экосистеме StarkNet и гарантировать, что средства пользователей больше не находятся под угрозой.

Motty Lavie

Motty Lavie

Узнавайте первыми

Подпишитесь сейчас и получайте ежемесячные обновления и интересные новости о Braavos и экосистеме Starknet.