8 декабря 2023 OpenZeppelin выпустил важное предупреждение о безопасности. Оказывается, при совместном использовании стандарта ERC-2771 и функций типа Multicall есть риск подмены адреса! Я глубоко изучил эту уязвимость и хочу поделиться своими находками.
Злоумышленник обменял 5 WETH на примерно 3,455,399,346 TIME
Затем сконструировал хитрые параметры calldata и вызвал [Forwarder].execute
Эта функция запустила multicall в контракте TIME, а оставшийся calldata использовался для burn() - уничтожения токенов в пуле!
Как же это работает?
В этой атаке сошлись звёзды: ERC-2771, Multicall и тщательно составленные данные. Контракт TOKEN наследует ERC2771Context, и тут начинается самое интересное!
ERC-2771 даёт виртуальный msg.sender, позволяя делегировать транзакции третьей стороне. При этом настоящий адрес добавляется в calldata. Когда [Forwarder] вызывает контракт, _msgSender() проверяет эти данные и обрезает последние 20 байт как "настоящий" msg.sender.
Multicall же превращает один вызов в несколько, экономя газ. Он принимает массив вызовов и выполняет их через delegatecall().
И вот тут ловушка! Атакующий смещает calldata так, что первые 4 байта новых данных соответствуют функции burn(), а параметр - огромная сумма токенов. В строке 0x20 добавляется адрес TIME-ETH пула ликвидности, который становится "ожидаемым" msg.sender!
Корень проблемы
В ERC-2771 [Forwarder] никогда не планировался для работы с multicall! Атакующий добавил параметры _msgSender() во внешний вызов multicall. Внутри функции multicall некоторые функции тоже добавляли эти параметры, что позволило имитировать вызов от ЛЮБОГО адреса!
Меры защиты
Новая версия OpenZeppelin Multicall теперь сохраняет длину контекста ERC-2771 и учитывает её при каждом дочернем вызове.
ThirdWeb пошёл дальше и запретил любым контрактам вызывать multicall, предотвращая использование [Forwarder].
Эта уязвимость - яркий пример того, как комбинация безопасных по отдельности компонентов может создать катастрофическую брешь! Всем разработчикам стоит помнить: всегда проверяйте взаимодействие между различными стандартами.
На этой странице может содержаться сторонний контент, который предоставляется исключительно в информационных целях (не в качестве заявлений/гарантий) и не должен рассматриваться как поддержка взглядов компании Gate или как финансовый или профессиональный совет. Подробности смотрите в разделе «Отказ от ответственности» .
Анализ принципа уязвимости подмены произвольного адреса в ERC2771 Multicall от SharkTeam
8 декабря 2023 OpenZeppelin выпустил важное предупреждение о безопасности. Оказывается, при совместном использовании стандарта ERC-2771 и функций типа Multicall есть риск подмены адреса! Я глубоко изучил эту уязвимость и хочу поделиться своими находками.
Анализ атаки
Рассмотрим одну из атак:
Схема атаки гениально проста:
Как же это работает?
В этой атаке сошлись звёзды: ERC-2771, Multicall и тщательно составленные данные. Контракт TOKEN наследует ERC2771Context, и тут начинается самое интересное!
ERC-2771 даёт виртуальный msg.sender, позволяя делегировать транзакции третьей стороне. При этом настоящий адрес добавляется в calldata. Когда [Forwarder] вызывает контракт, _msgSender() проверяет эти данные и обрезает последние 20 байт как "настоящий" msg.sender.
Multicall же превращает один вызов в несколько, экономя газ. Он принимает массив вызовов и выполняет их через delegatecall().
И вот тут ловушка! Атакующий смещает calldata так, что первые 4 байта новых данных соответствуют функции burn(), а параметр - огромная сумма токенов. В строке 0x20 добавляется адрес TIME-ETH пула ликвидности, который становится "ожидаемым" msg.sender!
Корень проблемы
В ERC-2771 [Forwarder] никогда не планировался для работы с multicall! Атакующий добавил параметры _msgSender() во внешний вызов multicall. Внутри функции multicall некоторые функции тоже добавляли эти параметры, что позволило имитировать вызов от ЛЮБОГО адреса!
Меры защиты
Новая версия OpenZeppelin Multicall теперь сохраняет длину контекста ERC-2771 и учитывает её при каждом дочернем вызове.
ThirdWeb пошёл дальше и запретил любым контрактам вызывать multicall, предотвращая использование [Forwarder].
Эта уязвимость - яркий пример того, как комбинация безопасных по отдельности компонентов может создать катастрофическую брешь! Всем разработчикам стоит помнить: всегда проверяйте взаимодействие между различными стандартами.