Zaloguj się do usług
bezpieczeństwa
19 sierpnia 2020
[Raport CERT OPL] Ethereum – niebezpieczne kontrakty

Kryptowaluty... Marzenie dla wielu - jedni widzą w nich potencjalnie wielkie zyski z gry na kryptowalutowej giełdzie, inni - z... wykradania owych zysków tym uczciwym. Tym, jak temu zapobiec, zajmuje się nasz CERTowy magik, Adam Pichlak. Swoimi spostrzeżeniami rokrocznie dzieli się na łamach Raportu CERT Orange Polska. Nie inaczej było w edycji 2019. Oczywiście jak zawsze zapraszamy do lektury całego raportu, który znajdziecie tutaj.


Ethereum jest platformą przetwarzania rozproszonego opartego na technologii blockchain. W odróżnieniu od Bitcoina, Ethereum umożliwia tworzenie zaawansowanych inteligentnych kontraktów, które wykonywane są w wirtualnej maszynie Etherum (EVM – Ethereum Virtual Machine). W przypadku Bitcoina udostępniony jest prosty język (Scrypt) umożliwiający definiowanie wielopodpisów i proste operacje związane z obsługą wypłat z portfeli. Te dwie technologie reprezentują oddzielne funkcjonalności – Bitcoin stworzony został przede wszystkim jako cyfrowy pieniądz i jest specjalnie „okrojony” do kilkudziesięciu instrukcji, czego podstawowa zaletą jest bezpieczeństwo, Ethereum stworzono jako platformę programistyczną, której możliwości są znacznie większe. Platforma ta za wykonaną pracę górników płaci kryptowalutą Ether, która jest wymienna na tzw. paliwo. Płaci się nim za moc obliczeniową używaną podczas wykonywania kontraktów (każda instrukcja „kosztuje”określoną ilość paliwa). Opłaty paliwem są jednocześnie zabezpieczeniem przed atakami DoS czy przeciążeniem sieci Ethereum (np. poprzez nieskończone pętle), a cena paliwa jest zmienna w zależności od użycia sieci. Możliwości danego kontraktu ograniczone są wyłącznie wyobraźnią programisty (i ograniczeniami środowiska), a to zwiększa prawdopodbieństwo wystąpień luk bezpieczeństwa. W artykule tym przestawimy jeden z najczęstszych błędów w tworzeniu kontraktów. Postaramy się odpowiedzieć na pytanie, w jaki sposób dochodzi do kradzieży środków. Zaprezentujemy także metody obrony.

Decentralized Autonomous Organization

Jednym z najpopularniejszych ataków w historii Ethereum był zorganizowany w 2016 roku atak na kontrakt The DAO (organizacji Decentralized Autonomous Organization). Doprowadził on do kradzieży środków w wysokości ok. 60 mln dolarów, a sam kontrakt posiadał ich ok. 150 mln. Atakujący wykorzystali podatność w kontrakcie pozwalającą na ponowne wejście w ten kontrakt przed zaktualizowaniem salda. Przykład takiej podatności opiszemy w tym artykule. Zdarzenie to doprowadziło do hard forka (jest to rozgałęzienie łańcucha bloków na odrębne gałęzi, co często doprowadza do powstania nowej kryptowaluty), w którym zwrócono środki poszkodowanym. Miało to jednak efekt uboczny – nie każdemu w społeczności się to spodobało, bo blockchain bez względu na wszystko powinien być niezmienny. Powstały więc dwa oddzielne łańcuchy i nowa kryptowaluta - Ethereum Classic (ETC), której cena, ze względu na istnienie przejętych środków w blockchain i zmniejszenie ilości programistów ją rozwijających, została mocno obniżona.

Kontrakty

Jedną z zalet Ethereum (jak się czasami okazuje, również wad) jest możliwość definiowania inteligentnych kontraktów w Blockchain, a następnie odpowiednia interakcja z nimi. Kontrakty te pozwalają obsługiwać środki wpływające na konto, regulować zasady ich wpłacania czy wypłacania według woli programisty. Kontrakty mogą również odwoływać się do kontraktów zewnętrznych – już wcześniej zdefiniowanych w Blockchain i to tutaj często dochodzi do nadużyć.

Przykładowy kontrakt napisany w Solidity został przedstawiony poniżej.

Kontrakt ten posiada:

  • Konstruktor (linia 5), który wywoływany jest podczas definiowania kontraktu. Tutaj zapisywany jest w zmiennej „owner” adres portfela, tworzącego ten kontrakt (w tym przykładzie nie jest wykorzystywany).
  • Funkcja rezerwowa z modyfikatorem „payable” (linia 9), która domyślnie jest wywoływana podczas odbioru środków przez kontrakt.
  • Funkcja „deposit” (linia 13), która wywoływana jest przez funkcje z poprzedniego punktu. Funkcja ta zapisuje w tablicy asocjacyjnej „balances” środki osób, które wpłacają je do kontraktu (można założyć, że jest to instytucja akumulująca środki swoich klientów np. bank) – kontrakt oczywiście jest mocno zminimalizowany na potrzeby tej publikacji. W Solidity tablice asocjacyjne deklaruje się jak poniżej:

mapping(address => uint256) balances;

oznacza to, że tablica „balances” posiada indeksy typu „address”, których wartości są zmiennymi 256-bitowymi typu „int” bez znaku. Struktura „msg” zawiera dane wysyłającego komunikat do kontraktu.

  • Funkcja „withdraw” (linia 18), która pozwala na wydawanie zdeponowanych środków osobie wpłacającej wcześniej środki na ten kontrakt. Funkcja „require” przerwie działanie kontraktu, jeśli warunek nie zostanie spełniony.

Jak wspomniano wcześniej kontrakty w Ethereum mogą wywoływać te zdefiniowane w blockchain i ich funkcje. Rodzi to problem z wielobieżnością – podczas wywoływania kontraktu można doprowadzić do wielokrotnego wykonywania funkcji wypłacającej, która w danym czasie nie zaktualizowała informacji o stanie salda.

Przyjrzyjmy się teraz przykładowemu podatnemu kontraktowi:

Kontrakt ten różni się od poprzedniego funkcją „withdraw”. W liniach 23, 24 widać, że dane przechowujące ilość zebranych wcześniej środków, aktualizowana jest po wykonaniu instrukcji zwracającej środki. Widać również zastosowanie niebezpiecznej funkcji zwracającej środki w linii 23.

Atakujący mógłby stworzyć kontrakt, który zasiliłby podatny kontrakt małą sumą – np. 1 Ether, a w momencie wypłaty podatny kontrakt za pomocą funkcji w linii 23 odwołałby się z powrotem do kontraktu atakującego, wywołując funkcję rezerwową odpowiedzialną za obsługę przyjmowania płatności (payable – listing poniżej – linia 20). Następnie funkcja ta odwołałaby się jeszcze raz do kontraktu podatnego w momencie kiedy jeszcze nie doszło do aktualizacji środków dla atakującego. Spowodowałoby to lawinowe wypłaty dla atakującego.

Poniżej przykład exploita wykorzystujący tę podatność:

W kontrakcji Ethery reprezentowane są jako najmniejsza możliwa jednostka czyli wei – 1 Ether to 10^18 wei.

Kontrakt ten posiada dwie funkcje przyjmujące płatności (linie 17 i 20). Funkcja „deposit” przyjmuje początkowe Ethery, aby móc je wysłać do podatnego kontraktu – wykonuje się to w funkcji „send”. Następnie atakujący wysyła ponownie środki do swojego kontraktu (exploita), aby wywołać funkcje rezerwową obsługującą płatności (linia 20). Od teraz rozpoczyna się rekurencyjne wywołanie funkcji „withdraw” kontraktu „Vulnerable”, która wypłacając środki wchodzi ponownie do kontraktu atakującego.

Metody obrony

Podstawową metodą obrony jest przede wszystkim aktualizowanie salda przed wykonaniem transferu środków, wykorzystanie muteksów (tzn. semaforów binarnych, w których kod wykonuje się atomowo – czyli tylko raz w danym czasie) lub korzystanie z bardziej bezpiecznych funkcji transferu środków. Poniższy przykład wykorzystuje wszystkie z tych 3 możliwości, choć wystarczyłoby zastosować tylko jedną z nich.

Podsumowanie

Przytoczony przykład pokazuje jedynie jedną z wielu możliwych podatności. Kontrakty jako „programy” trawione są tymi samymi problemami z bezpieczeństwem co zwykłe aplikacje. Może w nich dochodzić do przekroczenia zakresu liczbowego, błędów logicznych, podatności związanych liczbami zmiennoprzecinkowymi (lub w kodzie, który implementuje niejako te liczby, ponieważ język Solidity nie wspiera liczb zmiennoprzecinkowych), w sieci zaobserwowano nawet istnienie „przynęt” na atakujących – są to kontrakty pozornie podatne, ale przy próbie exploitacji takiego kontraktu dochodzi do utraty środków.

Jak widać, wraz z nowymi funkcjonalnościami w świecie kryptowalut powstają kolejne możliwości dla atakujących. Osoby tworzące kontrakty w Ethereum muszą brać pod uwagę możliwość wystąpienia krytycznego błędu bezpieczeństwa, aby odpowiednio zabezpieczyć kod i środki. Przykład najpopularniejszego ataku – na kontrakt The DAO - pokazuje, czym może skończyć się brak uwagi w tym aspekcie.

Adam Pichlak


Zgłoś incydent

Załącz plik

(jeśli zgłaszasz przypadek phishingu, zapisz mail (przesuń go z programu pocztowego na pulpit komputera lub wybierz opcję plik/zapisz jako), a następnie załącz)

Nie jestem człowiekiem
Jeśli zgłoszenie dotyczy bezpieczeństwa dzieci, zgłoś je również pod http://www.dyzurnet.pl