Анализ уязвимостей компилятора Solidity: влияние, примеры и стратегии реагирования

Анализ уязвимостей компилятора Solidity и стратегии противодействия

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

Большинство разработчиков и специалистов по безопасности обычно сосредотачиваются на безопасности кода приложений, но могут упустить из виду безопасность самого компилятора. На самом деле, компилятор, будучи компьютерной программой, также может содержать уязвимости. Уязвимости, возникающие в компиляторе, могут представлять серьезный риск безопасности в некоторых случаях. Например, в процессе компиляции и анализа выполнения кода на JavaScript браузер может подвергнуться атакам из-за уязвимости в движке анализа JavaScript, что может позволить злоумышленнику выполнить удаленный код, когда пользователь посещает вредоносные веб-страницы, в конечном итоге получая контроль над браузером жертвы или даже операционной системой. Исследования показывают, что ошибки в компиляторе Clang C++ также могут привести к серьезным последствиям, таким как удаленное выполнение кода.

Компилятор Solidity не является исключением; согласно предупреждениям о безопасности команды разработчиков Solidity, в нескольких различных версиях компилятора Solidity существуют уязвимости.

Уязвимость компилятора Solidity

Роль компилятора Solidity заключается в преобразовании кода смарт-контрактов, написанного разработчиками, в машинный код инструкций Ethereum Virtual Machine (EVM). Эти инструкции EVM упаковываются в транзакции и загружаются в Ethereum, а затем выполняются EVM.

Необходимо различать уязвимости компилятора Solidity и уязвимости самого EVM. Уязвимости EVM - это безопасностные уязвимости, возникающие в процессе выполнения инструкций виртуальной машины. Поскольку злоумышленники могут загружать произвольный код в Ethereum, этот код в конечном итоге будет выполняться в каждом клиентском P2P-программе Ethereum. Если в EVM есть уязвимости безопасности, это повлияет на всю сеть Ethereum, что может привести к отказу в обслуживании (DoS) и даже к полному контролю сети со стороны злоумышленника. Однако, поскольку сам EVM спроектирован относительно просто и основной код не обновляется часто, вероятность возникновения вышеуказанных проблем относительно низка.

Уязвимость компилятора Solidity относится к уязвимостям, возникающим при преобразовании Solidity в код EVM. В отличие от сценария, когда браузер компилирует и выполняет Javascript на клиентском компьютере пользователя, процесс компиляции Solidity осуществляется только на компьютере разработчика смарт-контрактов и не выполняется в сети Ethereum. Поэтому уязвимости компилятора Solidity не влияют напрямую на саму сеть Ethereum.

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

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

Анализ уязвимости компилятора Solidity и меры по ее устранению

Пример уязвимости компилятора Solidity

В следующем представлены несколько реальных случаев уязвимостей компилятора Solidity, которые демонстрируют конкретные формы, причины и последствия уязвимостей компилятора Solidity.

SOL-2016-9 HighOrderByteCleanStorage

Уязвимость существует в более ранних версиях компилятора Solidity (>=0.1.6 <0.4.4).

Рассмотрите следующий код:

солидность контракт C { uint32 a = 0x12345678; uint16 b = 0x1234;

функция f() публичная { a = a + 1; }

функция run() public view возвращает (uint16) { верните b; } }

Переменная storage b не была изменена, поэтому функция run() должна возвращать значение по умолчанию 0. Однако на самом деле в коде, сгенерированном уязвимой версией компилятора, функция run() вернет 1.

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

Причина возникновения вышеупомянутого исключения заключается в том, что EVM использует стековую виртуальную машину, каждый элемент в стеке имеет размер 32 байта (, то есть размер переменной uint256 ). С другой стороны, каждое хранилище на низком уровне (storage) также имеет размер 32 байта. Однако язык Solidity поддерживает различные типы данных, такие как uint32 и другие, которые меньше 32 байт. При обработке переменных такого типа компилятор должен правильно выполнить операции очистки старших битов (clean up), чтобы гарантировать правильность данных. В вышеупомянутой ситуации, когда сложение приводит к переполнению целого числа, компилятор неправильно очистил старшие биты результата, что привело к тому, что старший бит 1 был записан в storage, в конечном итоге перезаписав переменную a и изменив значение переменной b на 1.

SOL-2022-4 ВстроеннаяАссемблернаяПамятьПобочныеЭффекты

Уязвимость существует в компиляторах версий >=0.8.13 <0.8.15. Компилятор Solidity не просто переводит язык Solidity в код EVM, но и проводит глубокий анализ потока управления и данных, реализуя различные процессы оптимизации компиляции для сокращения объема генерируемого кода и оптимизации расхода газа в процессе выполнения. Такие операции оптимизации распространены в компиляторах различных высокоуровневых языков, но из-за сложности рассматриваемых случаев они также подвержены ошибкам или уязвимостям.

Рассмотрим следующий код:

солидность контракт C { функция f() публичная чистая возвращает (uint256) { сборка { mstore(0, 0x42) } уинт256 х; ассемблер { x := mload(0) } вернуть x; } }

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

Эта стратегия оптимизации сама по себе не имеет проблем, но в конкретной реализации кода компилятора Solidity такие оптимизации применяются только внутри одного блока ассемблера. В приведенном выше примере запись и доступ к памяти 0 находятся в двух разных блоках ассемблера, а компилятор анализирует и оптимизирует только отдельный блок ассемблера. Поскольку в первом блоке ассемблера после записи в память 0 нет никаких операций чтения, эта инструкция записи считается избыточной и будет удалена, что приведет к ошибке. В версии с уязвимостью функция f( вернет значение 0, в то время как на самом деле приведенный выше код должен вернуть правильное значение 0x42.

) SOL-2022-6 AbiReencodingHeadOverflowWithStaticArrayCleanup

Уязвимость затрагивает компиляторы версии >= 0.5.8 < 0.8.16. Рассмотрим следующий код:

солидность контракт C { функция f###bytes calldata data( внешняя чистая возвращает )bytes memory( { bytes4) память a = [bytes4[1]data(]; вернуть abi.encode)a(; } }

В нормальных условиях приведенный код должен возвращать переменную a как "aaaa". Но в уязвимой версии он будет возвращать пустую строку "".

Причиной этой уязвимости является то, что Solidity неправильно очищает некоторые данные при выполнении операции abi.encode с массивами типа calldata, что приводит к изменению соседних данных и, как следствие, к несоответствию закодированных и декодированных данных.

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

Уязвимость была адаптирована в известную задачу по безопасности на соревновании 0ctf 2022, демонстрируя влияние уязвимости компилятора на смарт-контракты в реальных условиях разработки.

![Анализ уязвимостей компилятора Solidity и меры по их устранению])https://img-cdn.gateio.im/webp-social/moments-c97428f89ed62d5ad8551cdb2ba30867.webp(

Рекомендации по безопасности

В отношении угроз и мер по борьбе с уязвимостями компилятора Solidity можно дать следующие рекомендации:

Для разработчиков:

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

  • Улучшите юнит-тесты. Большинство ошибок на уровне компилятора приводят к тому, что результаты выполнения кода не соответствуют ожиданиям. Такие проблемы сложно выявить при ревью кода, но они легко проявляются на этапе тестирования. Поэтому повышение покрытия кода может максимально предотвратить такие проблемы.

  • Старайтесь избегать использования встроенного ассемблера, а также сложных операций, связанных с многомерными массивами и сложными структурами для кодирования и декодирования abi. Избегайте слепого использования новых возможностей языка и экспериментальных функций, если нет явной необходимости. Большинство уязвимостей связано с такими операциями, как встроенный ассемблер и кодировщики abi. Компилятор более подвержен ошибкам при обработке сложных языковых возможностей. С другой стороны, разработчики также могут столкнуться с заблуждениями при использовании новых возможностей, что может привести к проблемам безопасности.

Для безопасников:

  • При проведении безопасного аудита кода Solidity не следует игнорировать потенциальные риски безопасности, которые могут быть вызваны компилятором Solidity. Соответствующий пункт проверки в Smart Contract Weakness Classification)SWC( это SWC-102: Устаревшая версия компилятора.

  • В процессе внутренней разработки безопасности настоятельно рекомендуется команде разработчиков обновить версию компилятора Solidity и рассмотреть возможность внедрения автоматической проверки версии компилятора в процесс CI/CD.

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

Некоторые полезные ресурсы:

  • Команда Solidity регулярно публикует уведомления о безопасности:

  • Список ошибок, регулярно обновляемый официальным репозиторием Solidity:

  • Список ошибок компилятора для всех версий:

  • На странице Code контракта на Etherscan в правом верхнем углу треугольный восклицательный знак может указывать на наличие у текущей версии компилятора уязвимостей безопасности.

Итог

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

![Анализ уязвимостей компилятора Solidity и меры реагирования])https://img-cdn.gateio.im/webp-social/moments-84f5083d8748f2aab71fd92671d999a7.webp(

ETH-3.92%
Посмотреть Оригинал
На этой странице может содержаться сторонний контент, который предоставляется исключительно в информационных целях (не в качестве заявлений/гарантий) и не должен рассматриваться как поддержка взглядов компании Gate или как финансовый или профессиональный совет. Подробности смотрите в разделе «Отказ от ответственности» .
  • Награда
  • 7
  • Поделиться
комментарий
0/400
fork_in_the_roadvip
· 07-19 10:48
Компиляторы тоже могут иметь уязвимости? Зачем паниковать!
Посмотреть ОригиналОтветить0
gas_fee_therapistvip
· 07-19 08:57
Этот уязвимость написана довольно ясно~
Посмотреть ОригиналОтветить0
GateUser-75ee51e7vip
· 07-18 18:19
Кар баг всегда лучше, чем майнинг.
Посмотреть ОригиналОтветить0
MetaverseHobovip
· 07-16 19:40
Компилятор тоже ошибся? Эта волна испугала.
Посмотреть ОригиналОтветить0
DeepRabbitHolevip
· 07-16 19:39
Ужасно, так много уязвимостей, а вы все еще играете в Закрытая позиция?
Посмотреть ОригиналОтветить0
GasWastervip
· 07-16 19:37
Есть ли у компилятора уязвимости? yyds
Посмотреть ОригиналОтветить0
ForkTonguevip
· 07-16 19:23
Деньги на карту - это самый надежный способ.
Посмотреть ОригиналОтветить0
  • Закрепить