Как хакеры взломали систему управления криптокошелька Tornado Cash?
Разберем техническую сторону кибератаки на криптокошелек Tornado Cash.
Вкратце - для взлома системы управления кошелька злоумышленники использовали функцию CREATE, CREATE2 и самоуничтожение. Они создали смарт-контракт, идентичный оригиналу, но заложили в него функцию самоуничтожения, которая осталась незамеченной. Получив согласие системы, хакер удаляет оригинальный контракт и размещает свой контракт по тому же адресу. Поскольку этот адрес уже был принят системой управления кошелька, он получил полный контроль над контрактом управления.
Прежде всего управление криптокошелька Tornado Cash работает таким образом, что участники подают свои предложения, а другие участники голосуют за одобрение или отклонение предложения. Чтобы принять участие в управлении, необходимо заблокировать определенные токены (в данном случае токены TORN).
После голосования или создания предложения токены полностью блокируются до момента выполнения или отклонения предложения. Предложение должно быть представлено в виде верифицированного смарт-контракта, и если DAO одобрит его, код будет исполнен через активацию в контракте управления.
Пошагово это выглядит так:
1) Хакер блокирует свои токены TORN, чтобы участвовать в управлении:
2) Мошенник закачивает контракт, который совпадает с существующим и принимается системой управления кошельком как свой родной, в него злоумышленник внес функцию emergencyStop, которая выполняет функцию самоуничтожения и удаляет оригинальный контракт:
3) Когда это предложение было принято юзерами, злоумышленник активирует самоуничтожение оригинального контракта, а затем разворачивает свой контракт на том же адресе:
4) После активации, контракт закачанный хакером передает ему большинство голосов пользователей, и мошенник забирает себе все токены:
Но как это все стало возможным в распиаренной системе защиты Tornado Cash ?
Давайте разберемся в шаге 2, о котором говорилось выше. Как они могли развернуть новый контракт с другим кодом на тот же адрес?
Для того чтобы ответить на этот вопрос, необходимо знать самую основную концепцию - процесс генерации адреса смарт-контракта при использовании опкодов CREATE и CREATE2.
CREATE
Когда мы используем опкод CREATE для развертывания контракта, адрес, генерируемый для этого контракта, зависит от адреса создателя и nonce отправителя (временного номера транзакции адреса отправителя).
Вот как генерируется адрес, используя адрес и nonce отправителя:
адрес контракта = последние 20 байт sha3(rlp(sender,nonce))
Адреса смарт-контрактов также имеют nonce. Но он отличается от EOA nonce тем, что увеличивается только тогда, когда контракт развертывает другой контракт, а не каждый раз, когда контракт вызывает некоторые другие функции контракта, которые мы называем «внутренней транзакцией». И после EIP 161 nonce начинается с 1, а не с 0 во вновь развернутом контракте.
CREATE2
В CREATE2 нет необходимости в nonce. Генерация адреса зависит от 4 параметров.
- 0xFF - константа, которая используется для предотвращения коллизий с CREATE
- адрес отправителя - адрес контракта деплойера
- соль - произвольные данные в формате байта, отправляемые создателем
- код создания - код создания развертываемого контракта
Мы можем вычислить адрес еще до развертывания вот так
Таким образом, для развертывания контракта на том же адресе, что и раньше, нам нужно выполнить 2 условия:
1. Адрес развертывателя должен быть одинаковым как в CREATE, так и в CREATE2.
2. В CREATE nonce должен быть тем же самым, а в CREATE2 код создания контракта должен быть тем же самым.
Теперь давайте посмотрим, как хакер объединил возможности CREATE и CREATE2 для активации самоуничтожения:
1) Атакующий разворачивает контракт «развертыватель».
2) Из этого контракта Deployer они развертывают другой контракт, но с CREATE2, назовем его «Sender».
3) Из контракта отправителя они развертывают контракт предложения, используя опкод CREATE.
Схематично это выглядит примерно так:
4) Как только руководство проголосовало и приняло предложение, злоумышленник использует самоуничтожение в контрактах «Предложение» и «Отправитель» и снова развернул контракт «Отправитель» по тому же адресу. Затем, как и раньше, используется контракт отправителя для развертывания вредоносного контракта по адресу ранее уничтоженного контракта предложения:
CREATE и CREATE2.
CREATE зависит от адреса отправителя и nonce, каждый раз, когда контракт развертывает другой контракт, nonce увеличивается на 1.
CREATE2 зависит от адреса отправителя, соли и кода создания. Это означает, что nonce не обязательно должен быть одинаковым, но код создания контракта должен быть одинаковым.
Для развертывания вредоносного контракта на том же адресе, что и адрес контракта-предложения, невозможно использовать CREATE2, поскольку код изменился, как и код создания. Это означает, используется метод развертывания по умолчанию, которым является CREATE.
Но здесь возникает другая проблема: после развертывания контракта предложения nonce контракта отправителя увеличился бы на 1, и при развертывании вредоносного контракта будет сгенерирован другой адрес контракта.
Именно по этой причине хакер уничтожил и контракт отправителя: самоуничтожение также сбрасывает nonce адреса в 0.
Таким образом, после развертывания контракта отправителя с помощью CREATE2 nonce снова будет равен 1, а адрес уже будет прежним. А это приводит к развертыванию вредоносного контракта по тому же адресу, где ранее был развернут контракт с принятым предложением.
Будьте внимательны и берегите свои токены.
Купить крипту можно здесь