ОРИГИНАЛЬНАЯ СТАТЬЯ: Binance Smart Chain Token Bridge Hack
ПЕРЕВЕДЕНО для xss.pro by Marcus Aurelius
Предыстория
6 октября 2022 года bridge BSC Token Hub (далее BSC), принадлежащий крупнейшей криптовалютной бирже Binance, был взломан. Это был один из крупнейших криптовалютных взломов в истории. BSC обеспечивает взаимодействие между блокчейном Binance Beacon Chain, используемым Binance для децентрализованного управления (стейкинг, голосование), и Binance Smart Chain, блокчейном, совместимым с EVM, используемым для создания различных децентрализованных приложений. Хакеры вывели 2 миллиона BNB (криптовалюта Binance) из bridge-протокола, причем 1 BNB на тот момент стоил 293 доллара. В общей сложности было украдено 586 миллионов долларов.
Технические аспекты
Блокчейн-мосты используются для передачи данных и активов между разнородными блокчейнами. Они выступают в качестве посредников при отправке транзакций, поэтому доверие к транзакции, отправленной с блокчейна А на блокчейн В, зависит от моста между А и В. Чтобы доверять транзакции, предоставленной блокчейном А, мост должен ее подтвердить. В зависимости от логики моста существует несколько способов проверки транзакций, но все они зависят от способа записи и хранения данных в блокчейне, то есть от древовидной структуры представления данных.
Каждый узел двоичного дерева представляет собой конкатенацию хэшей двух его дочерних узлов. Конечные узлы дерева, соответствующие транзакциям, добавленным в блокчейн, называются листьями, а верхние корневые узлы - корнями. Такая древовидная структура представления данных называется деревом двоичного поиска и позволяет легко проверить легитимность (авторство) и целостность данных, записанных в любом из узлов дерева. Зная хэш проверяемых данных и значения промежуточных узлов, использованных при вычислении хэша корня, можно выполнить доказательство Меркла: начиная с нижней части узла, проверить правильность каждого последующего хэша, вплоть до корня. Любое асхождение будет свидетельствовать о том, что данные в узле были подделаны.
Двоичное дерево хэшей
Для проверки транзакций мост BSC использует сбалансированное дерево AVL - разновидность дерева двоичного поиска. Для каждого узла этого дерева высота двух его ветвей отличается не более чем на 1. Алгоритм проверки вызывается в функции handlePackage основного смарт-контракта CrossChain, который обрабатывает переводы токенов между блокчейнами.
Объявление функции handlePackage в главном смарт-контракте BSC
Эта функция также содержит модификатор onlyRelayer, который означает, что только релейщик может вызвать эту функцию.
Структура запроса на подтверждение транзакции
Релейщики в мостах обрабатывают специально отформатированные пакеты данных, поступающие с блокчейна А, извлекают из них необходимые параметры и транслируют их в сеть для передачи на блокчейн В. Чтобы зарегистрироваться в качестве релейщика, необходимо внести 100 токенов BNB и настроить устройство, подключенное к блокчейну, в соответствии с конфигурационным файлом. После регистрации ретранслятор начинает разбирать данные в таблице событий endBlock каждого блока сети и выбирать из нее все события IBCPackage.
Структура запроса на проверку транзакции
Параметр value имеет четыре атрибута, разделенных символом "::":
- Первый атрибут - это имя цепи назначения; в данном примере это "bsc".
- Второй атрибут - CrossChainID цепочки назначения; в данном примере это "2".
- Третий атрибут - идентификатор канала; в данном примере это "8".
- Четвертый атрибут - последовательность; в данном примере это "19".
Имя параметра | Размер | Значение |
prefix | 1 byte | 0x00 |
source chain CrossChainID | 2 bytes | Transaction source blockchain ID |
destination chain CrossChainID | 2 bytes | Transaction destination blockchain ID |
channelID | 1 byte | IBCPackage event channel ID |
sequence | 8 bytes | IBCPackage event sequence number |
Далее ретранслятор отправляет эти параметры, транзакционные данные, признак подтверждения транзакции (prove) и высоту блока в специальный запрос RPC, который вызывает функцию handlePackage контракта CrossChain:
RPC-запрос к функции handlePackage
Доказательство Меркла
В функции handlePackage вызывается функция validateMerkleProof библиотеки MerkleProof; используя метод staticcall по адресу 0x65, вызывается предварительно скомпилированный контракт iavlMerkleProofValidate. Этот контракт представляет собой библиотеку, написанную на языке Go, которая имеет ряд зависимостей (методов) кросс-цепочечного фреймворка Cosmos, реализующих функциональность доказательства Меркла.
Библиотека MerkleProof
Эти зависимости вызываются в функции Run контракта iavlMerkleProofValidate в строках 8, 9 и 16:
Функция Run прекомпилированного контракта iavlMerkleProofValidate
Вызванный метод op.Proof.ComputeRootHash() вычисляет хэш корня дерева AVL для листа дерева, содержащего проверяемую транзакцию. Далее метод op.Proof.Verify(root) сравнивает хэш корня дерева AVL с хэшем, вычисленным на предыдущем шаге. Если сравниваемые хэши отличаются, метод op.Proof.Verify(root) вернет ошибку, и передача транзакции с блокчейна A на блокчейн B будет отменена. Если хэши одинаковые, вызывается метод op.Proof.VerifyItem(op.key, value), в котором проверяется наличие хэша данных транзакции в дереве AVL. Если хэш найден, транзакция считается действительной и выполняется.
Уязвимость процесса проверки транзакций
Уязвимость процесса проверки транзакции связана с тем, как вычисляется и проверяется хэш корня дерева для транзакции. Функция ComputeRootHash вызывает метод pwl.Leaf.Hash():
Вызов функции Hash в функции computeRootHash
Функция Hash правильно вычисляет корневой хэш в том случае, если левый лист в цепочке доказательств Меркла для проверяемой транзакции равен нулю, то есть не определен. Если же левый лист определен, то хэш корня дерева вычисляется без учета правого листа. Другими словами, если левый лист в цепочке доказательств Меркла для проверяемой транзакции определен, то значение корневого хэша дерева не будет зависеть от наличия правого листа в цепочке доказательств.
Уязвимость в вычислении корневого хэша
Этот недостаток позволяет злоумышленникам записать полезную нагрузку в правый лист и успешно пройти проверку значения хэша корня дерева при условии, что хэш полезной нагрузки рассчитан правильно.
Сценарий атаки
Перед началом атаки злоумышленники внесли 100 BNB на контракт Relayer Hub, чтобы зарегистрироваться в качестве ретранслятора моста BSC.
Они использовали легитимную транзакцию, которая была использована для перевода 0,05 BNB с моста BSC два года назад. Они изменили полезную нагрузку, указав в качестве получателя адрес злоумышленника, и изменили сумму до 1 млн. БНБ:
Оригинальная транзакция | Транзакция злоумышленников | |
| Полезная нагрузка | 0x00000000000000000000000000000000000 00000000000000000038d7ea4c68000f86da0 424e420000000000000000000000000000000 0000000000000000000000000009400000000 0000000000000000000000000000000087b1a 2bc2ec50000944e656459ed25bf986eea1196 bc1b00665401645d94a10123c15a63135fe94 5a54232bae7fac8177056845fd12999 | 0x00000000000000000000000000000000000 0000000000000000000000000000000f870a0 424e420000000000000000000000000000000 0000000000000000000000000009400000000 000000000000000000000000000000008ad3c 21bcecceda100000094489a8756c18c0b8b24 ec2a2b9ff3d4d447f79bec94489a8756c18c0 b8b24ec2a2b9ff3d4d447f79bec846553f100 |
Содержимое полезной нагрузки представляет собой структуру, закодированную в формате RLP:
Структура представления кодировки RLP
Далее злоумышленники изменили содержимое переменной доказательства, добавив в дерево AVL правый лист с хэшем полезной нагрузки, и добавили пустой внутренний узел для балансировки дерева AVL:
Оригинальная структура доказательства транзакции | Структура доказательства транзакции злоумышленниками |
| RangeProof{ LeftPath: PathToLeaf{ 0:proofInnerNode{ Height: 1 Size: 3 Version: 110217392 Left: 0C10F902D266C238A4CA9E26FA9BC36483CD3 EBEE4E263012F5E7F40C22EE4D2 Right: } 1:proofInnerNode{ Height: -1 Size: 2 Version: 110217392 Left: E4FD47BFFD1C06E67EDAD92B2BF9CA63631978676288A2AA99F95C459436EF63 Right: } } InnerNodes: Leaves: proofLeafNode{ Key: 0000010038020000000000000002 ValueHash: 11056C6919F02D966991C10721684A8D1542E44003F9FFB47032C18995D4AC7F Version: 110217392 } (rootVerified): true (rootHash): E09159530585455058CF1785F411EA44230F39334E6E0F6A3C54DBF069DF2B62 (treeEnd): true } | RangeProof{ LeftPath: PathToLeaf{ 0:proofInnerNode{ Height: 1 Size: 3 Version: 110217392 Left: 0C10F902D266C238A4CA9E26FA9BC36483CD3 EBEE4E263012F5E7F40C22EE4D2 Right: } 1:proofInnerNode{ Height: -1 Size: 2 Version: 110217392 Left: E4FD47BFFD1C06E67EDAD92B2BF9CA63631978676288A2AA99F95C459436EF63 Right: DA657C1FFB86C684EB3E265361EF0FA4F9DFA670B45F9F91C5EB6AD84B21A4D1 } } InnerNodes: empty-PathToLeaf Leaves: proofLeafNode{ Key: 0000010038020000000000000002 ValueHash: 11056C6919F02D966991C10721684A8D1542E44003F9FFB47032C18995D4AC7F Version: 110217392 } proofLeafNode{ Key: 00000100380200000000010DD85C ValueHash: 2C3A561458F8527B002B5EC3CAB2D308662798D6245D4588A4E6A80EBDFE30AC Version: 1 } (rootVerified): true (rootHash): E09159530585455058CF1785F411EA44230F39334E6E0F6A3C54DBF069DF2B62 (treeEnd): true } |
Из-за уязвимости содержимое добавленного правого листа не влияло на корневой хэш, поэтому мошенническая транзакция была успешно проверена. После проверки транзакции контракт CrossChain вызвал функцию для перевода 1 млн BNB с BSC на адрес злоумышленников. Далее злоумышленники попытались повторить транзакцию, но следующие 15 попыток были неудачными из-за неверного значения packageSequence. Однако на 16-й попытке им удалось найти правильное значение packageSequence и получить еще 1 млн BNB на свой адрес.
Далее, опасаясь замораживания и блокировки активов в БСК, злоумышленники начали выводить деньги с моста. Для отмывания они использовали Venus Finance DeFi: выпускали токены vBNB в обмен на BNB и использовали vBNB в качестве залога для займа BUSD, стейблкоина Binance. Затем, используя мосты Stargate и Anyswap, злоумышленники конвертировали BUSD в стабильные монеты USDT и USDC в нескольких блокчейнах: Ethereum, Avalanche, Fantom, Polygon, Arbitrum и Optimism.
Цепочка отмывания украденных средств
Реакция Binance и криптосообщества
После выявления атаки компания Binance приостановила и форкнула блокчейн BSC, не позволив злоумышленникам вывести более 400 миллионов долларов. После этого Tether, владелец стейблкоина USDT, заблокировал USDT-адрес злоумышленников, не позволив им отмыть часть украденных средств. Адреса злоумышленников были внесены в черный список:
Адрес злоумышленника добавлен в черный список BSC
Устранение уязвимостей
Первоначально метод проверки дерева AVL предполагал, что для проверяемой транзакции может быть определен только правый или только левый лист дерева. Однако проверка одновременного присутствия правого и левого листьев в алгоритме проверки дерева AVL изначально отсутствовала, чем и воспользовался злоумышленник.
Вскоре после взлома было внесено исправление в метод доказательства AVL кросс-цепочечного фреймворка Cosmos:
Изменения после устранения уязвимости в методе доказательства AVL фреймворка Cosmos
В случае одновременного присутствия левого и правого листа дерева AVL проверка транзакции будет отклонена с соответствующей ошибкой.
Заключение
Взлом моста Binance Smart Chain Token Hub является примером использования уязвимости в стороннем компоненте, который мост использует для определения того, можно ли доверять транзакции и выполнять ее или отклонить. Хотя уязвимость была в стороннем компоненте, она нанесла большой финансовый ущерб мосту BSC. По всей видимости, до того, как она была устранена, эта уязвимость присутствовала и в других протоколах DeFi, использующих метод доказательства AVL в рамках межцепочечного фреймворка Cosmos. Однако, исходя из суммы средств, похищенных из BSC, можно предположить, что злоумышленники с самого начала охотились за джекпотом, зная, что после устранения уязвимости у них не будет второго шанса.