Продолжение статьи «Сканирование Live Ethereum контрактов на ошибку «Unchecked-Send». Часть 1».


Почти год назад (в то время как Ethereum был в своем «пограничном» выпуске), популярный лотерейный контракт EtherPot [9] также пострадал от той же ошибки. Более ранняя версия BTCRelay также показала эту ошибку [7]. Несмотря на то, что в предыдущем аудите безопасности была обнаружена опасность, сначала было применено неправильное исправление [8].


Обнаружение ошибки «unchecked-send» на живом blockhain


Насколько распространены эти ошибки? Прислушиваются ли к предупреждениям? Применяются ли лучшие практики? Мы отвечаем на эти вопросы эмпирически, анализируя данные block-chain Ethereum, а также репозиторий кода Solidity, найденный на etherscrape.com. Для этого мы разрабатываем простой инструмент анализа программ, который проверяет контракт на block-chain и использует эвристику для проверки того, используется ли один из наиболее эффективных методов защиты. В листинге 2 показана первая техника защиты, как рекомендовано в документации Ethereum, которая должна проверять возвращаемое значение send и выдавать исключение. Чтобы обнаружить использование этого метода, мы используем грубое приближение: мы просто ищем, игнорируется ли возвращаемое значение send или нет.

Листинг 4 иллюстрирует вторую технику защиты, рекомендованную в руководстве UMD, которая непосредственно проверяет, заполнен ли callstack, отправив тестовое сообщение. Чтобы обнаружить эту технику, мы снова используем приблизительную аппроксимацию: мы просто проверяем, отправляется ли сообщение в дополнение к команде send .

Если ни один из этих эвристических индикаторов не присутствует, мы делаем вывод, что ни одна из рекомендаций по лучшей практике не соблюдается. Мы реализуем эти эвристики, используя простое сопоставление шаблонов с компилированным байт-кодом EVM. Более подробно о том, как мы это делаем, содержится в Приложении [12].


Сколько контрактов уязвимо?


Начнем с проверки эвристики в репозитории Etherscrape исходного кода Solidity. По состоянию на 20 марта 2016 года ретрансляция Etherscrape содержала 361 контрактную программу Solidity, 56 из которых содержали инструкцию send. Из этих программ контрактов мы предполагаем, что большинство (не менее 36 из 56) не используют ни один из методов защитного программирования.

Даже если контракт не использует ни одну из защитных технологий, он может или не может иметь реальной уязвимости. Мы вручную проверили контракты Solidity, чтобы подтвердить наличие уязвимости. Для наших целей мы рассматриваем контракт уязвимым, если его состояние может измениться, даже если команда send не cработает (поэтому мы рассмотрим уязвимый код в листинге 5). Мы подтвердили, что уязвимость присутствует в подавляющем большинстве, 32 из 36 из этих контрактов.


Точно так же наша эвристика не гарантирует правильного применения защитного программирования. Возьмем, к примеру, «WeiFund», децентрализованный open-source crowdfunding DApp. Этот контракт имеет две функции: refund () и payout (), которые обманывают нашу эвристику. Ниже приводится выдержка из refund.


function refund(uint _campaignID, uint contributionID) public {
...
  receiver.send(donation.amountContributed);
  donation.refunded = true;
...
  if(c.config != address(0))
  WeiFundConfig(c.config).refund(_campaignID, donation.contributor, 
  donation.amountContributed);  
}

В этом коде сообщение отправляется по адресу WeiFundConfig (c.config), чтобы вызвать метод refund но только при определенных условиях. Если c.config — это нулевое значение, то контракт действительно уязвим для атаки callstack. При проверке * ни одна из программ Solidity, которые прошли нашу эвристическую проверку, фактически не применяла рекомендуемую наилучшую практику тестирования callstack напрямую. *

Затем мы обратим наше внимание на составленные контракты на живом block-chain Ethereum. Мы посмотрели снимок от 20 марта 2016 года (временная метка: 1184243). Этот моментальный снимок содержит в общей сложности 13645 цепочки блоков, которые, по-видимому, генерируются компилятором Solidity, из которых только 1618 (11,8%) включали команду send.

Из них подавляющее большинство, похоже, не использует ни одного из методов защитного программирования.


Как насчет проблемы рекурсивной гонки в TheDAO? Самый захватывающий смарт-контракт в эти дни, TheDAO [11], страдает от совершенно отдельной ошибки, которая заключается в том, что она не «безопасна для повторного использования» [13] . Это еще один (связанный, но отчетливый) вид небезопасного программирования, который также ожидался в предыдущих проверках безопасности [6] , но, по-прежнему, вероятно, многие контракты сегодня небезопасны. Будущая работа заключалась в том, чтобы сделать инструмент, который также может обнаружить такую ??ошибку.


Куда это все пошло не так?


Мы не ожидаем, что программирование на смарт-контрактах будет совершенно простым, по крайней мере, пока. Однако удивительно, что эта конкретная форма ошибки настолько распространена, несмотря на что она была так давно описана при развитии экосистемы Эфириума.

В докладе в 2015 году [6] была приведена эта рекомендация разработчикам Ethereum:"

В настоящее время примеры программирования, представленные в документации, недостаточны для распространения передовых методов написания безопасных контрактов и решения проблемы с газовым механизмом. Вводные учебники на C ++ часто пропускают
проверку ошибок для удобства чтения, что и привело к многочисленным ошибкам безопасности. Примеры Ethereum должны преподавать лучшие привычки. Рекомендация: предоставить еще больше примеров тщательного программирования защитных контрактов."

Нам известен только один официальный ответ на этот вопрос, который заключается в том, чтобы добавить предупреждение в официальную документацию Solidity, упомянутую ранее [3], повторенную ниже:"Есть некоторая опасность при использовании send: Передача завершается с ошибкой, если глубина стека вызовов составляет 1024 (это всегда может быть вызвано вызывающим), и также терпит неудачу, если у получателя заканчивается газ. Поэтому, чтобы обеспечить безопасную передачу эфира, всегда проверяйте возвращаемое значение send или даже лучше: используйте шаблон, в котором получатель изымает деньги."


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


  • Обновление:
    неадекватность документации Solidity также была подробно проиллюстрирована Питером Весенесом. [16]


Кроме того, предупреждение, похоже, часто не учитывается. Поэтому мы считаем, что необходимо предпринять дополнительные превентивные меры.



Как может помочь Etherscrape?


Мы полагаем, что использование инструментов статического анализа, даже грубых, таких как описанное в этом сообщении, может помочь улучшить качество интеллектуальных контрактов.На Etherscrape мы интегрируем инструменты анализа, подобные этому, в наш общедоступный веб-сервис, и мы добавим ссылку на страницу инструмента, когда она будет готова. Это упростит просмотр кода интеллектуального контракта, выделив места, где могут возникнуть ошибки. Мы предполагаем, что пользователи такого смарт-контракта (например, потенциальные инвесторы в TheDAO или его предложениях) могут легко использовать такие инструменты, как проверку здравомыслия, прежде чем депонировать свои деньги. Даже нетехнические инвесторы могут привлекать разработчиков к ответственности за объяснение того, как они реагировали на проблемы, отмеченные в коде.

Etherscrape также помогает, анализируя публичный block-chain и контролируя распространенность этой ошибки, что может помочь при принятии решения о том, например, сколько средств выделять на исследования и разработку инструментов статического анализа. Кроме того, компиляторы, такие как solc, могут интегрировать такие анализы, предоставляя предупреждение программисту, когда ошибка кажется вероятной.


Рекомендуемая литература



Комментарии (0)