![](https://habrastorage.org/getpro/habr/upload_files/a25/cfe/411/a25cfe411b9e58433b89894b418cef32.png)
Введение
Эксперт в области информационной безопасности Гехард Вагнер получил рекордное вознаграждение в размере 2000000$ от команды блокчейн проекта Polygon. Найденная исследователем уязвимость угрожала потерей 850000000$. Вознаграждение стало рекордным в истории DeFi. Гехард Вагнер обратил внимание на баунти-программу, которую Polygon запустил сентябре. Он отметил, что Polygon использует для защиты транзакций между своими сетями и Ethereum систему защиты Plasma, которую, по его мнению, сложно надежно реализовать. Вагнер подробно рассказал о том, как обнаружил уязвимость в «мосте» Plasma Bridge. С помощью ошибки в коде злоумышленник мог бы вывести сумму, в 223 раза превышающую первоначальную стоимость токенов. Внесение каждых $200 тыс. могло бы принести потенциальному хакеру $44,6 млн. В случае эксплуатации уязвимости потери протокола могли составить $850 млн. Вагнер предположил, что проблема возникла «из-за использования стороннего кода без его полного понимания».
Вагнер отметил, что ранее уже работал над системами защиты от мошенничества, в частности над «Плазмой» и знал основные сложности, с которыми сталкиваются разработчики. Поэтому сразу начал проверять опубликованные контракты, чтобы найти тот, который блокирует средства на L1, когда пользователи переводят средства в сеть Polygon и из нее. Он быстро нашел нужный контракт Deposit Manager Proxy, и на момент написания он содержал токены стоимостью около миллиарда долларов США.
Далее разберем найденную уязвимость.
Что такое Polygon и Plasma
![](https://habrastorage.org/getpro/habr/upload_files/2f2/77f/a92/2f277fa925876730864780ff629ebdb6.png)
Прежде чем анализировать ошибку, важно понять, как работает Plasma и Polygon, по крайней мере, на высоком уровне.
Сначала немного терминологии, кто знаком может пропустить:
Дерево Меркла - полное двоичное дерево, в листовые вершины которого помещены хеши от блоков данных, а внутренние вершины содержат хеши от сложения значений в дочерних вершинах. Корневой узел дерева содержит хеш от всего набора данных, то есть хеш-дерево является однонаправленной хеш-функцией.
Решения второго уровня/слоя (L2-решения) — это проекты, приложения и технологии инфраструктурного программного обеспечения, развернутые поверх базовых блокчейнов.
Сайдчейны — технология, позволяющие токенам и другим цифровым активам одного блокчейна безопасным образом использоваться в другом блокчейне и затем (в случае необходимости) быть возвращенными в оригинальный блокчейн. Изначально концепция сайдчейнов (side chain) была описана в 2014 году в «белой бумаге» (white paper), который написали разработчики компании Blockstream. Сайдчейн (side chain) представляет собой отдельный блокчейн с двусторонней привязкой к родительскому блокчейну. Это обеспечивает взаимозаменяемость активов. Родительский блокчейн обычно называется «основной (главной) цепью» (master chain), дополнительные цепи — сайдчейнами (side-chain). Пользователь родительского блокчейна должен сначала отправить монеты на выходящий адрес, где они «запираются» участниками так называемой «федерации», что призвано исключить возможность их траты в другом месте. По завершении транзакции ее участники получают подтверждение, однако для дополнительной безопасности это происходит после некоторого периода ожидания. После этого эквивалентное количество монет переводится в сайдчейн (side chain), и у пользователя появляется возможность их потратить. При отправке монет из сайдчейна (side chain) в основной блокчейн происходит обратный процесс.
Fraud proof - модель безопасности для решений второго уровня, в которых для повышения скорости из транзакций формируются пакеты и отправляются в Ethereum за одну транзакцию. Они считаются валидными, но могут быть оспорены по определенным правилам. Этот метод увеличивает количество возможных транзакций при сохранении достаточного уровня безопасности.
Plasma — решение второго уровня для масштабирования сети Ethereum, изначально предложенное Джозефом Пуном и Виталиком Бутериным. Технология предусматривает использование смарт-контрактов и деревьев Меркла для создания неограниченного числа дочерних цепей — копий родительской сети Ethereum. Они разгружают основной блокчейн, открывая возможность осуществления быстрых и недорогих транзакций. По принципу работы Plasma похожа на Lightning Network: набор умных контрактов, позволяющий множеству сайдчейнов фиксировать свое состояние в сжатом виде в корневом блокчейне. Plasma применяет экономические стимулы, включая наказание отвергнутого сетью создателя блока для предотвращения мошенничества. В июне 2020 года работающая над масштабированием Ethereum компания OMG Network запустила бета-тестирование OMG Network V1, базирующейся на спецификациях последней версии Plasma — More Viable Plasma (MoreVP). MoreVP масштабирует Ethereum, группируя транзакции и отправляя их через набор смарт-контрактов. Сгруппированные данные проходят верификацию и валидацию в децентрализованной сети хранителей. Группирование позволяет увеличить пропускную способность сети до тысяч транзакций в секунду и значительно сократить их стоимость. Это отдельный блокчейн, который привязан к основной цепочке Ethereum и использует fraud proofs (Optimistic rollups) для разрешения споров.
Matic Network (теперь Polygon) — решение второго уровня с поддержкой фреймворка Plasma и децентрализованной сети PoS-валидаторов. Проект использует сайдчейны (side chain) для офчейн-вычислений. В начале 2021 года состоялся ребрендинг проекта. Сменилось название на Polygon, изменилась стратегия в сторону создания мультичейн-системы, похожей на Polkadot. Команда проекта работает над созданием решений второго уровня на базе Ethereum. Polygon - это протокол и платформа для создания и подключения блокчейн-сетей, совместимых с Ethereum. Платформа предлагает «надежный двусторонний канал транзакций» между Polygon и Ethereum. Этот «блокчейн-мост» использует сеть под названием Plasma для аутентификации и обработки вывода средств. Именно в контракте на Plasma, доверенном лице управляющего депозитами, был обнаружен недостаток.
Архитектура Polygon Network
В архитектуре протокола содержатся 3 абстрактных слоя:
Polygon Ethereum смарт-контракты - набор смарт-контрактов на Ethereum, которые обрабатывают:
Стейкинг (Staking) для поддержания Proof-of-Stake;
Управление делегированием, включая общие ресурсы валидатора;
Plasma контракты для MoreVP, включающие checkpoints/snapshots для сайдчейна (side chain).
Heimdall (Proof of Stake layer) – PoS валидатор, который работает на основе Ethereum-контрактов для реализации PoS механизма в Polygon. Он отвечает за проверку блоков, продюсер (block producer).
Bor (Block producer layer) – ответственный за объединение транзакций в блоки. В настоящее время это базовая реализация Geth с некоторыми изменениями, внесенными в алгоритм консенсуса.
![](https://habrastorage.org/getpro/habr/upload_files/36a/4d3/3ac/36a4d33ace5e5baf31323269629597f4.png)
На приведенной ниже диаграмме показан поток пользовательских средств через Polygon network (source):
Пользователь вносит средства в контракт моста в L1 (основной сети Ethereum);
После подтверждения транзакции депозита в сети Ethereum, токены доступны в сети Plasma, чтобы пользователи могли их передавать;
Когда пользователь решает вывести средства обратно на L1, токены необходимо сжечь в Plasma. Нужно дождаться контрольной точки. Затем контрольная точка отправляется в мастер-чейн (master chain);
Когда контрольная точка пройдена, выпускаются Exit NFT (ERC721);
Далее необходимо подождать 7 дней;
После 7-дневного периода проверки средства могут быть выведены обратно пользователю на L1.
Такой механизм типичен для fraud proof систем, поскольку они предполагают, что определенные действия являются действительными, и отменяют их только в том случае, если другой пользователь может доказать, что они были недействительными.
![](https://habrastorage.org/getpro/habr/upload_files/55c/9d8/b78/55c9d8b782c36d635ca16d153c9c78bb.png)
Разбор уязвимости
![](https://habrastorage.org/getpro/habr/upload_files/7a9/2c7/806/7a92c7806cae195bb3f1664706c7d5c8.png)
Логика вывода средств обратно в блокчейн Ethereum реализована, в частности, в контракте ERC20PredicateBurnOnly. Выход можно запустить, вызвав функцию startExitWithBurntTokens из данного контракта. В контракте WithdrawManager, также отвечающего за вывод средств, в функции verifyInclusion в строке 115 происходит проверка, включена ли квитанция в дерево. Уязвимость в том, как Polygon WithdrawManager проверяет включение и уникальность в предыдущих блоках.
![](https://habrastorage.org/getpro/habr/upload_files/169/309/5b7/1693095b7a857f242bc8f3b590d24f70.png)
Одним из параметров, который содержит доказательство выхода, является branchMask. branchMask должна быть уникальной, так как она используется для генерации идентификатора выхода (Exit ID). Важное свойство - одна транзакция есть один идентификатор выхода, но это свойство может быть нарушено. Если проверки пройдены, то идентификатор выхода (Exit ID) создается на основе метки времени, номера блока контрольной точки и branchMask, см. строку 158.
![](https://habrastorage.org/getpro/habr/upload_files/9f5/d0f/957/9f5d0f9578a7d7290f05848d32bc1d5b.png)
Идентификатор выхода возвращается и сохраняется в приоритетной очереди, которая упорядочивает все выходы в зависимости от их возраста. Когда выход находится в очереди приоритетов не менее 7 дней с момента транзакции, на которую он ссылается, он может быть обработан, и средства возвращаются пользователю в L1.
Идентификатор выхода частично состоит из branch mask, которая поступает непосредственно из пользовательского ввода, что является слабым местом. Если бы было возможно создать альтернативную branch mask, которая каким-то образом способна создать более одного идентификатора выхода, относящегося к одной и той же транзакции. Более пристальный взгляд на функцию в библиотеке MerklePatricaProof показывает, что что-то происходит с путем до того, как будет пройден цикл из строки 40. В строке 35 branch mask передается в функцию _getNibbleArray.
![](https://habrastorage.org/getpro/habr/upload_files/d85/614/b12/d85614b12c9c364840af901cb38521d2.png)
Проверка функции _getNibbleArray показывает, что branch mask (аргумент функции encodedpath) закодирована (Hex Prefix encoded).
![](https://habrastorage.org/getpro/habr/upload_files/85d/09f/205/85d09f205d1215186ff217a95c977235.png)
![](https://habrastorage.org/getpro/habr/upload_files/e11/701/67b/e1170167baf4f72a8246100476cfe3fc.png)
Функция _getNibbleArray проверяет с какого значения начинается переданный набор. Если первая часть байта (цифра) 1 или 3, то учитывается вторая часть первого байта и декодируются все оставшиеся значения, иначе первый байт игнорируется. Давайте рассмотрим на примере и предположим, что branch mask равна 0x00819c. Первый байт отбрасывается, так как он – 0, и его первая часть не равна 1 или 3, а остальные фрагменты расширяются до 0x0801090c.
Также в функции verifyInclusion преобразуется branchMask в uint256.
![](https://habrastorage.org/getpro/habr/upload_files/4c9/984/498/4c99844984e22d1e94b81b54344edb4d.png)
Так как getNibbleArray отбрасывает часть информации, то получаем, что одному значению декодирования getNibbleArray соответствуют несколько uint256 значений. В общей сложности можно создать 14x16 = 224 различных кодировок для одного и того же пути. Злоумышленник может использовать эту проблему для создания альтернативных выходов для одной и той же транзакции и выполнения двойных трат в сети Polygon.
![](https://habrastorage.org/getpro/habr/upload_files/724/c0b/f9d/724c0bf9deb9561126b09aa3e985ae2c.png)
Влияние
![](https://habrastorage.org/getpro/habr/upload_files/f3e/e02/7ee/f3ee027ee2a28a198f9bae81870b54b0.png)
Для успешной атаки существуют требования к капиталу, но они минимальны по сравнению с потенциальным вознаграждением злоумышленника. Давайте рассмотрим пример и поймем, сколько мог бы получить злоумышленник по сравнению со средствами, необходимыми для атаки.
Атакующий:
депонирует токены на сумму 200 000 долларов США в контракт DepositManager;
запускает процесс вывода выхода (withdrawal);
ожидает семидневный период проверки;
запускает вывод, переиспользуя предыдущие данные с модифицированным параметром branch mask.
С помощью описанной выше техники генерируются 223 альтернативных выходных полезных нагрузки, и для каждой из них инициируются выходы. Все выходы получают уникальный идентификатор и добавляются в очередь. После того, как все выходы были запущены и обработаны, злоумышленник получает сумму, в 223 раза превышающую сумму первоначального депозита или токенов на сумму 44,6 миллиона долларов.
Исправление
![](https://habrastorage.org/getpro/habr/upload_files/dd1/c01/32a/dd1c0132a63c7f4ddbc4c94898d1012d.png)
Уязвимость была устранена путем отклонения любой кодировки, которая не начинается с 0x00. Это не очень элегантно, но исправляет ошибку с двойными расходами, жестко кодируя метасимвол кодировки.
https://github.com/maticnetwork/contracts/commit/283b8d2c1a9ff3dc88538820ffc4ea6a2459c040