- PVSM.RU - https://www.pvsm.ru -

Уязвимости EOS Blockchain на ZeroNights 2018

image

В рамках данной статьи будут рассмотрены несколько реальных уязвимостей в EOS blockchain (одном из конкурентов Etherum) и то, как они были встроены в конкурс New-Generation Secure Slot Machine [1] на ZeroNights 2018. Если вам интересно познакомиться с тем, как обстоят дела с безопасностью в этой сети blockchain, то welcome под кат.

Вступление

Все началось с того, что недавно, во время аудита смарт-контрактов Etherium на безопасность, один наш знакомый скидывает нам статейку [2] про уязвимости в смарт-контрактах в сети EOS. Нас это сильно заинтересовало, и мы решили разобраться в уязвимостях более детально.

Всё это в итоге и привело к созданию конкурса на ZeroNights 2018 под названием «Однорукий бандит» с уязвимостями в смарт-контракте.

Начнем непосредственно с рассмотрения сети блокчейн EOS, как с ним работать, и как у него все устроено внутри. Статей, описывающих технологию, в Интернете много, поэтому, скорее всего, всех технических деталей не будет, но общий смысл мы постараемся передать так, чтобы и обычный пользователь смог получить элементарное представление о механизмах работы blockchain EOS.

Описание технологии EOS

EOS.io – блокчейн нового поколения от компании Block.one [3], основанный на концепции PoS (Proof of stake [4]).

Из описания самих создателей сети: «EOS — это бесплатное программное обеспечение сети блокчейн с открытым исходным кодом, который предоставляет разработчикам и предпринимателям платформу для создания, развертывания и запуска высокопроизводительных децентрализованных приложений (DAPP).»

Если в двух словах попытаться объяснить концепцию, то её хорошо отражает выдержка из статьи [4] на Википедии:

Идея proof-of-stake (PoS) заключается в решении проблемы proof-of-work (PoW), связанной с большими тратами электроэнергии. Вместо вычислительных мощностей участников, имеет значение количество криптовалюты, находящейся у них на счету. Так, вместо использования большого количества электроэнергии для решения задачи PoW, у участника PoS ограничен процент возможных проверок транзакций. Ограничение соответствует количеству криптовалюты, находящейся на счету у участника

Сеть совсем новая, и первый запуск главной сети (mainnet) состоялся 10 июня 2018 года. Основной крипто-валютой является EOS, а главный портал для разработчиков developers.eos.io [5]

Блокчейн EOS.io поддерживает приложения, созданные пользователями с использованием кода WebAssembly (WASM – нового веб-стандарта с широкой поддержкой крупных компаний, таких как Google, Microsoft, Apple и других.

На данный момент наиболее свежим инструментарием для создания приложений, которые компилируют код в WASM, является clang / llvm с их компилятором C / C ++.

Для лучшей совместимости разработчики рекомендуют использовать EOSIO CDT (Contract Development Toolkit) – набор утилит от самих разработчиков для удобной и корректной работы над созданием смарт контрактов.

Предыдущий компилятор eosiocpp уже deprecated и не поддерживается, поэтому всем рекомендуется переходить на новый (на момент написания статьи) EOSIO CDT 1.5.

В отличие от эфира

Эфир в своей концепции использует PoW (Proof of Work), что требует дорогостоящих вычислений и награду получает тот, кто первый решил математическую задачу. То есть те, кто решал параллельно, но не успел решить, попусту затратили электроэнергию. В этой ситуации майнеры воюют между собой за более совершенные технологии и оборудование. Чтобы быстрее генерировать блоки и, тем самым, зарабатывать.

В отличие от Эфира в сети ЕОС по концепции PoS создателя нового блока выберет система, и определяется это по количеству личного состояния – доли от общего количества криптовалюты. Таким образом, у кого больше состояние, у того больше шансы быть выбранным системой. Но в отличие от PoW (эфира) вознаграждение за генерацию нового блока отсутствует в принципе, и доход майнеров составляют исключительно комиссии с транзакций.

Вывод? Криптовалюты на базе PoW могут быть в 1000 раз более энергоэфективными.

Разворачиваем среду разработки

Так, с теорией вроде покончили, переходим к практике. А на практике всё выглядит гораздо интереснее. С документацией на момент, когда пытались разобраться летом и начать что-то делать для ZeroNights 2018, было всё совсем плохо, а основной портал для разработки глючил, был наполовину пуст и иногда даже не работал.

Тестовые сети еще толком не были запущены, поэтому пришлось разворачивать свою ноду. Кстати, в отличие от мнения в интернете, завести ее оказалось не так сложно. Пользуясь официальной документацией, мы запустили ее из докера developers.eos.io/eosio-nodeos/docs/docker-quickstart [6]

Расскажем о основных утилитах, программах для работы с блокчейном EOS, с которыми пришлось иметь дело в момент работы над конкурсом:

  • Nodeos [7] – собственно, служба самой ноды EOSIO; можно конфигурировать и настраивать различные плагины, например CORS, history и другие.
  • Cleos [8] – консольная утилита для работы с нодой, вызовов методов контрактов и взаимодействия с кошельком, ключами, доступами. Самый частый инструмент при работе с EOS.
  • Keosd [9] – консольный кошелек, или точнее тоже служба кошелька, хранилище приватных ключей.
  • Eosio.cdt [10] – Contract Development Toolkit, так называемый набор утилит разработчика для отладки и компиляции контрактов, генерации ABI-файлов и не только.
  • Eos.js [11] – Библиотека Javascript API для удобной работы с нодой и контрактами через веб, встраивается на сайт.
  • Scatter [12] – десктоп кошелек для надежного хранения ваших ключей аккаунтов. Существует веб-библиотека scatter.js, которая взаимодействует с десктопным кошельком Scatter по веб-сокетам и тем самым помогает работе с DAPP приложениями в браузере.

Уфф!.. Да, программок много, разобраться в них тоже не совсем легко. Описание всего этого заслуживает отдельного поста и выходит за рамки данной статьи. Но давайте представим, что мы установили ноду на свой сервер и даже научились при помощи cleos вызывать методы контракта, если бы он у нас был.

Да, самое главное. Надо бы нам набросать сам смарт-контракт. Писать мы его будем на C++ и, чтобы сделать хоть что-то толковое, пришлось прочитать немало документации.

Для понимания контрактов везде приводят пример контракта Hello [13]. Основным файлом является hello.cpp и весь контракт описан в нем

#include <eosiolib/eosio.hpp>
using namespace eosio;

class hello : public eosio::contract {
  public:
      using contract::contract;

      /// @abi action 
      void hi( account_name user ) {
         print( "Hello, ", name{user} );
      }
};

EOSIO_ABI( hello, (hi) )

Если в двух словах постараться объяснить, то тут – всё просто. Подгружаем библиотеку eosio.hpp, затем создаем класс (он же контракт) hello и унаследуем класс contract. Создаем void метод hi и в параметры заносим переменную user c типом account_name, он же uint64_t. В методе выводим “Hello, ” и имя, которое мы укажем при вызове метода. Последняя строчка, где находится EOSIO_ABI –это вспомогательный макрос, который принимает наш класс и общедоступные методы из этого класса, а также участвует в формировании файла .abi, где указываются все общедоступные методы контракта.

Изучаем уязвимости

Итак, в рамках той статьи [2], описывалось несколько уязвимостей – давайте их сейчас рассмотрим подробнее.

Numerical Overflow – численное переполнение [14]

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

Что это может дать? К примеру, есть проверка на какой-то числовой параметр, допустим, int Number < 0, и известно, что int у – знаковое число, и если произойдет переполнение числа, то знак числа при больших значениях изменится на отрицательный. Тем самым, проверка будет пройдена переполнением. И тут, конечно, всё зависит от критичности данной проверки.

К примеру, в той же статье про уязвимости есть реальный кейс [15], где злоумышленники смогли повлиять на параметр balance, тем самым обманув систему. В комментариях к коду более подробно описан механизм взаимодействия с контрактом:

// Структура аккаунтов для вывода баланса
typedef struct acnts {
    account_name name0;
    account_name name1;
    account_name name2;
    account_name name3;
} account_names;

// Структура пакетной отправки денег (каждому отправить по 1 EOS)
// Скорее всего, этот метод могли вызвать публично, 
// тем самым повлияв на параметр «баланс»
void batchtransfer(
	symbol_name symbol, 
	account_name from, 
	account_names to, 
	uint64_t balance
){
    // Проверка права исполнителя
    require_auth(from);

    // Инициализация переменной аккаунта
    account fromaccount;

    // Проверка существования отправителя и получателей
    require_recipient(from);
    require_recipient(to.name0);
    require_recipient(to.name1);
    require_recipient(to.name2);
    require_recipient(to.name3);

    // Проверка, лежит ли баланс в пределах допустимого диапазона. 
    // Код функции is_balance_within_range не виден (
    eosio_assert(is_balance_within_range(balance), "invalid balance");

    // Проверка, больше ли нуля значение переменной «баланс» 
    // К примеру, «баланс» 1111111111111111 больше 0, и проверка будет пройдена
    eosio_assert(balance > 0, "must transfer positive balance");

    // Инициализация переменной amount и умножение на 4
    // Вот тут и происходит переполнение, и amount становится отрицательным
    int64_t amount = balance * 4;  

    // Поиск в таблице аккаунта from, откуда следует вычитать
    int itr = db_find_i64(_self, symbol, N(table), from);

    // Проверка, найден ли аккаунт
    eosio_assert(itr >= 0, "wrong name");

    // Добавляение в переменную fromaccount найденного аккаунта
    db_get_i64(itr, &fromaccount, (account));

    // Проверка, больше ли отправленного баланс из таблицы 
    // Например, в игре баланс 0.1 EOS
    //  и он будет больше, чем отрицательное значение amount
    eosio_assert(fromaccount.balance >= amount, "overdrawn balance");


    // Функция вычитает 
    sub_balance(symbol, from, amount);

    // Функция отправляет деньги 4 аккаунтам
    add_balance(symbol, to.name0, balance);
    add_balance(symbol, to.name1, balance);
    add_balance(symbol, to.name2, balance);
    add_balance(symbol, to.name3, balance);
}

Взлом скорее всего был осуществлен следующим образом. Злоумышленник предварительно создал 4 аккаунта и вызвал метод batchtransfer напрямую, приблизительно так:

cleos push action contractname batchtransfer '{"symbol ":"EOS", "from":”attacker”, "to":{ “name0”:”acc0”, “name1”:”acc1”, “name2”:”acc2”, “name3”:”acc3”}, "balance":"111111111111111111 EOS"}' -p attacker@active

image

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

Autherization check, проверка на авторизацию

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

Отсутствие проверки прав вызова метода

При отправке на контракт денег (EOS) можно указать в специальном макросе, что будет происходить дальше и что делать. Скажем, при получении денег будет вызываться некий алгоритм, например, запускаться рулетка или еще что-нибудь, а также проверка:

if( code == self || code == N(eosio.token) || action == N(onerror) ) { 
     TYPE thiscontract( self ); 
         switch( action ) { 
         EOSIO_API( TYPE, MEMBERS ) 
     }
}
// Отсутствует проверка на action == N(transfer)

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

Конкурс на ZeroNights 2018

Идея конкурса родилась сама по себе: раз всё связано с играми и тремя уязвимостями, следовательно, будем делать игру на механизме смарт-контракта в блокчейне EOS.io. Игра должна быть максимально простой, но интересной.

Игровой автомат «Однорукий бандит»! Всегда удивляли люди, жаждущие легкой наживы — помните, халявы в мире не бывает, или почти не бывает. Тут, кстати, она вполне есть, вернее, появится, когда в ход пойдут уязвимости.

Фронтенд

Фронтенд игры решили сделать модным, красивым и трехмерным. Спасибо vtornik23 [16], за то, что не отказался поучаствовать и помог нам сделать полностью фронтенд на Unity3d движке.

image

Трехмерный игровой аппарат «однорукий бандит»; отправив на него 1 ЕОС и дернув за шикарный рычаг, игрок получает возможность запустить колесо фортуны и сорвать куш!

Уязвимости контракта

По задумке игры, выигрышем считалось выпадение трёх матрёшек ZeroNights, что в числовом коэффициенте будет либо 777, либо 0.Шансы на выигрыш приравнивались к 0.02%, и некий невнимательный программист попытался усложнить алгоритм рандома, добавив в него всего лишь умножение (multiplication overflow) на количество присланных денег, и поленился обдумывать условия детальнее, поэтому просто написал if (result == 777 || result < 1 ), что дает возможность подсунуть отрицательное значение.

     int rnd = random(999);
     int result = rnd * price.amount;
     uint64_t prize = 0;
 
     print("Result:", result);
 
     // BINGO 777 or 000 !!! ~ 0.02% 
     if(result == 777 || result < 1 ) {
         prize = 100;
         sendtokens(from);
     }

Сам смарт-контракт выложен на гитхаб [17], так что все желающие могут его повнимательнее рассмотреть со всех сторон и определить остальные уязвимости. О них уже написано чуть выше, так что сложностей в их поиске быть не должно.

Правила участия

Правила участия очень просты: необходимо было попытаться выиграть или взломать механизмы системы. При выпадении 3 матрешек – Джек-пот!!! Система начисляет 100 единиц крипто-валюты. Если участник получает джек-пот 3 раза подряд, он становится победителем и получает призы от организаторов — фирменные худи, значки, разнообразный мерч.

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

Результаты конкурса

В итоге конкурс, на наш взгляд, прошел идеально. Были запланированы награды для 3 человек, и как раз троим удалось справиться с конкурсом до назначенной даты окончания. Конкурс проводился 2 дня, в течение которых участники должны были решить таск. Официальное награждение и вручение подарков было на закрытии конференции на главной сцене ZeroNights 2018.

Основной упор делался на познавание технологии блокчейн ЕОС, и нами была оставлена пара подсказок, одну из которых так никому не удалось найти. Эту загадку мы оставим на потом…

Отзывы участников

Алексей [18] (1 место)

ZeroNights одна из моих любимых конференций, начиная с самой первой, в Петербурге я не пропустил ни одну. Всегда дает заряд энтузиазма на полгода точно, а там весной PHDays :). Последние 3 года я занимаюсь блокчейн разработкой. В этом году блокчейн добрался и до ZeroNights (в прошлом правда вроде тоже, было на хаквесте, но я его пропустил). Первым делом, после регистрации на конференции я пошел посмотреть, что и как там с блокчейном. Думал будет, что-то на подобие как на PHDays, какой-нибудь кривой рандом или race condition на эфире. Но тут оказался EOS, с которым у меня было небольшое знакомство на первом хакатоне EOS, но оно было не продолжительным, и к тому же все настройки для разработки были утеряны. Боевой настрой упал, и я пошел ждать начала конференции. Но любопытство взяло верх, все-таки что же там с EOS-ом не так!

Stanislav Povolotsky [19] (2 место)

Для меня это был долгий, но интересный конкурс. И он стал замечательной возможностью познакомиться поближе с архитектурой блокчейна EOS. Конкурс начался с удивления, что в сеть EOS (mainnet) просто так не попасть — только за $$$. После подсказки, что контракт развёрнут в тестовой сети, регистрации в этой сети, настройки scatter и просмотра истории транзакций для игрового контракта — стало сразу понятно, как нужно обманывать слот-машину (автор контракта при тестировании делал это несколько раз). Но уверенность в том, что так быстро и просто удастся справиться с конкурсом быстро улетучилась, как только сеть не одобрила все мои транзакции с параметрами, идентичными выигрышной транзакции.

Ирина [20] (3 место)

До участия в конкурсе представляла работу смарт-контрактов только в теории, поэтому было очень интересно «встретиться с ними вживую», увидеть исходный код, опробовать инструменты (и в очередной раз убедиться, что python лучше всего)). Задание получилось действительно очень захватывающим. Спасибо!

И в заключение

Не скажем, что все справились легко. Для кого-то это были сложные 2 дня, и только под конец счастливчикам удалось победить, используя недостатки любого блокчейна — если информация попала в блокчейн, то доступна каждому, и если кто-то уже что-то взломал, то и другой может посмотреть его путь.

Благодарим всех участников и тех, кто помогал в организации конкурса.
До встречи на ZeroNights 2019, вас будут ждать новые приключения!

Автор: 5aava

Источник [21]


Сайт-источник PVSM.RU: https://www.pvsm.ru

Путь до страницы источника: https://www.pvsm.ru/c-3/302707

Ссылки в тексте:

[1] конкурс New-Generation Secure Slot Machine: https://2018.zeronights.ru/activities/%D0%B1%D0%B5%D0%B7%D0%BE%D0%BF%D0%B0%D1%81%D0%BD%D1%8B%D0%B9-%D0%B8%D0%B3%D1%80%D0%BE%D0%B2%D0%BE%D0%B9-%D0%B0%D0%B2%D1%82%D0%BE%D0%BC%D0%B0%D1%82-%D0%BD%D0%BE%D0%B2%D0%BE%D0%B3%D0%BE/

[2] статейку: https://github.com/slowmist/eos-smart-contract-security-best-practices/blob/master/README_EN.md

[3] Block.one: https://block.one/

[4] Proof of stake: https://ru.wikipedia.org/wiki/%D0%94%D0%BE%D0%BA%D0%B0%D0%B7%D0%B0%D1%82%D0%B5%D0%BB%D1%8C%D1%81%D1%82%D0%B2%D0%BE_%D0%B4%D0%BE%D0%BB%D0%B8_%D0%B2%D0%BB%D0%B0%D0%B4%D0%B5%D0%BD%D0%B8%D1%8F

[5] developers.eos.io: https://developers.eos.io

[6] developers.eos.io/eosio-nodeos/docs/docker-quickstart: https://developers.eos.io/eosio-nodeos/docs/docker-quickstart

[7] Nodeos: https://developers.eos.io/eosio-nodeos/docs/

[8] Cleos: https://developers.eos.io/eosio-nodeos/docs/cleos-overview

[9] Keosd: https://developers.eos.io/keosd/docs/

[10] Eosio.cdt: https://github.com/EOSIO/eosio.cdt

[11] Eos.js: https://github.com/EOSIO/eosjs

[12] Scatter: https://get-scatter.com/

[13] Hello: https://github.com/EOSIO/eos/tree/master/contracts/hello

[14] численное переполнение: https://ru.wikipedia.org/wiki/%D0%A6%D0%B5%D0%BB%D0%BE%D1%87%D0%B8%D1%81%D0%BB%D0%B5%D0%BD%D0%BD%D0%BE%D0%B5_%D0%BF%D0%B5%D1%80%D0%B5%D0%BF%D0%BE%D0%BB%D0%BD%D0%B5%D0%BD%D0%B8%D0%B5

[15] реальный кейс: https://bihu.com/article/995093

[16] vtornik23: https://habr.com/users/vtornik23/

[17] гитхаб: https://github.com/5aava/SecureSlot

[18] Алексей: https://twitter.com/bitaps_com

[19] Stanislav Povolotsky: https://twitter.com/StanPov

[20] Ирина: https://vk.com/irina4711

[21] Источник: https://habr.com/post/433552/?utm_source=habrahabr&utm_medium=rss&utm_campaign=433552