Подробный анализ уязвимостей компилятора Solidity и стратегии их предотвращения

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

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

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

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

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

Роль компилятора Solidity заключается в преобразовании кода смарт-контракта, написанного разработчиками, в машинный код (EVM) для виртуальной машины Ethereum. Эти инструкции 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, демонстрирующие их конкретные формы, причины и последствия.

SOL-2016-9 HighOrderByteCleanStorage

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

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

солидность контракт C { uint32 a = 0x1234; uint32 b = 0; функция f() публичная { a += 1; } функция run() публичный просмотр возвращает (uint) { вернуть 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. Рассмотрим следующий код:

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

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

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

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

) SOL-2022-6 AbiReencodingHeadOverflowWithStaticArrayCleanup

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

солидность контракт C { функция f###uint8( calldata a[4] общедоступная чистая возвращает )string memory( { вернуть строку)abi.encode(a(); } }

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

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

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

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

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

На основе анализа модели угроз уязвимостей компилятора Solidity и рассмотрения исторических уязвимостей предлагаются следующие рекомендации для разработчиков и специалистов по безопасности.

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

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

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

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

Для сотрудников безопасности:

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

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

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

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

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

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

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

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

Итоги

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

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

ETH1.3%
Посмотреть Оригинал
На этой странице может содержаться сторонний контент, который предоставляется исключительно в информационных целях (не в качестве заявлений/гарантий) и не должен рассматриваться как поддержка взглядов компании Gate или как финансовый или профессиональный совет. Подробности смотрите в разделе «Отказ от ответственности» .
  • Награда
  • 5
  • Поделиться
комментарий
0/400
ZenZKPlayervip
· 07-30 05:45
Уязвимость переполнения немного беспокоит
Посмотреть ОригиналОтветить0
OffchainWinnervip
· 07-30 00:55
Писать код, совершенно не обращая внимания на ошибки.
Посмотреть ОригиналОтветить0
OnchainSnipervip
· 07-30 00:47
С компилятором проблемы? Я с этим уже сталкивался.
Посмотреть ОригиналОтветить0
UnluckyValidatorvip
· 07-30 00:44
Компиляторы имеют уязвимости, пугают людей.
Посмотреть ОригиналОтветить0
LiquiditySurfervip
· 07-30 00:41
Скажи, чтобы разработчик быстро исправил уязвимость
Посмотреть ОригиналОтветить0
  • Закрепить