Witamy w przewodniku dla początkujących dotyczącym audytu inteligentnych kontraktów! Jednym z najlepszych sposobów rozpoczęcia audytu inteligentnych kontraktów jest przyjrzenie się kilku typowym typom luk w inteligentnych kontraktach.
Byłoby pomocne, jeśli masz już podstawową wiedzę na temat języka programowania Ethereum Solidity. Jak przyjrzymy się niektórym kodom napisanym przez programistów Noob solidity.
Atak ponownego wejścia
Scenariusz w świecie rzeczywistym:
Wyobraź sobie, że masz 50 czekoladek. Masz niegrzeczną młodszą siostrę, której pozwoliłeś zabrać od siebie tylko 2 czekoladki na raz. Nie chcesz też dawać jej więcej niż 10 czekoladek dziennie, bojąc się, że zachoruje na próchnicę. Aby to zapewnić, każdego wieczoru liczysz, ile czekoladek zostało przy Tobie. Czy myślisz, że to zadziała? A może zostaniesz zhakowany przez swoją młodszą siostrę?
Niestety to nie zadziała! Twoja siostra domyśla się, że nie wiesz, ile masz czekoladek, aż do wieczora. Tak więc już następnego dnia twoja młodsza siostra odwiedza cię 6 razy przed wieczorem i za każdym razem bierze 2 czekoladki! To właśnie nazywamy atakiem na ponowne wejście.
Tutaj aktualizujesz liczbę czekoladek, które masz wieczorem, zamiast aktualizować liczbę za każdym razem, gdy twoja siostra bierze od ciebie 2 czekoladki. Tak samo dzieje się z inteligentnym kontraktem. Inteligentna umowa zakłada określoną równowagę, podczas gdy atakujący jest faktycznie zajęty wielokrotnym wycofywaniem pewnej ilości krypto z umowy.
Przykład kodu w świecie rzeczywistym:
Ten kod należy do inteligentnej umowy o nazwie Unbanked. Każdy może wypłacić ether z umowy bez konta bankowego, o ile saldo msg.sender (tj. osoba dzwoniąca withdraw
funkcja ) jest większa lub równa żądanej kwocie wypłaty.
function withdraw(uint _amount) { require(balances[msg.sender] >= _amount); msg.sender.call.value(_amount)(); balances[msg.sender] -= _amount;
}
Zauważ, że istnieje słowo kluczowe call używane do wysłania wymaganej ilości etheru do msg.sender
. Atakujący może to wykorzystać, tworząc umowę o nazwie Złodziej, w której wywołuje funkcję wycofania w a fallback()
funkcjonować. A fallback()
Funkcja w Solidity to specjalna funkcja, która jest wykonywana, gdy ether jest wysyłany do inteligentnego kontraktu.
Oznacza to, że atakujący jest w stanie: rekursywnie wywołaj funkcję wycofania. Tak więc przed aktualizacją inteligentnego kontraktu salda msg.sender
w ostatnim wierszu kodu atakujący już wielokrotnie wycofywał ether. Można tego uniknąć, jeśli salda zostaną zaktualizowane przed użyciem słowa kluczowego call, a zatem po a sprawdza-efekty-interakcje wzór.
Wpływ:
Połączenia pierwszy w historii atak z powrotnym wejściem wydarzyło się w 2016 roku na DAO (Decentralized Autonomous Organisation), co zaowocowało włamaniem o wartości około 50 milionów dolarów. Aby odwrócić ten hack, społeczność Ethereum podzieliła łańcuch bloków Ethereum, co dało początek ETC (Ethereum Classic) i ETH (Ethereum).
Przepełnienie i niedomiar arytmetyczny
Scenariusz w świecie rzeczywistym:
Zagrajmy w grę myślową. Składa się on z zakręcenia kołem, a zwycięzca jest wyłaniany na podstawie największej liczby, jaką uda mu się uzyskać za kręceniem kołem. Koło jest oznaczone na całej długości od 256 do -256.
Reguła gry jest taka, że wskaźnik dla wszystkich graczy na początku każdego spinu jest ustawiony na 0. A gracz może kręcić tylko w kierunku liczb ujemnych. Jak wygrasz tę grę?
Dobrą strategią, aby wygrać tę grę za każdym razem, byłoby kręcenie kołem z taką mocą, aby koło obracało się do -256, a następnie obracało się do 256 za jednym zamachem. Jest to możliwe, ponieważ 256 przychodzi zaraz po -256 na kole. To jest to, co nazywamy niedomiarem arytmetycznym. A przepełnienie arytmetyczne jest po prostu na odwrót.
Przykład kodu w świecie rzeczywistym:
An niedomiar lub przepełnienie dzieje się, gdy operacja arytmetyczna osiąga minimum lub maksimum.
function withdraw(uint _amount) public { require(balances[msg.sender] - _amount > 0); address payable to = payable(msg.sender); to.transfer(_amount); balances[msg.sender] -= _amount;
}
Połączenia _amount
parametr funkcji wycofania jest liczbą całkowitą bez znaku. Wartość mapowania sald (która jest jak słownik w Pythonie lub para klucz-wartość w C++ lub Javie) jest również liczbą całkowitą bez znaku.
mapping(address => uint256) public balances
Wymagane oświadczenie sprawdza, czy salda msg.sender
jest pozytywny czy nie. Ale to stwierdzenie zawsze będzie prawdziwe, nawet jeśli kwota jest większa niż salda msg.sender
. Dzieje się tak, ponieważ zarówno balances
i _amount
zmienne są typu unsigned integer, a ich wynik arytmetyczny (po niedopełnieniu) również będzie liczbą całkowitą bez znaku!
Jak zapewne pamiętasz, liczba całkowita bez znaku jest zawsze dodatnia. Oznacza to, że atakujący może wypłacić nieograniczoną ilość Etheru z inteligentnej umowy! Możesz znaleźć szczegółowy przykład i kod implementacji tej luki tutaj.
Inną ważną rzeczą, na którą należy zwrócić uwagę, jest to, że operacja arytmetyczna między powiedzmy dwiema liczbami całkowitymi bez znaku jest również liczbą całkowitą bez znaku. Może to być niebezpieczne, jeśli zostanie to przeoczone w inteligentnych kontraktach, ponieważ może to spowodować niepożądane naruszenia bezpieczeństwa!
function votes(uint postId, uint upvote, uint downvotes) { if (upvote - downvote < 0) { deletePost(postId) }
}
Jak mogłeś zauważyć w powyższym przykładzie, instrukcja if jest zupełnie bezcelowa, ponieważ upvote - downvote
zawsze będzie pozytywne. A post zostanie usunięty, nawet jeśli downvotes
jest większa upvotes
. Aby uniknąć takich ataków, zaleca się używanie wersji kompilatora Solidity większej niż 0.8.0.
Wpływ:
Moneta o nazwie Moneta PoWH została uruchomiona w 2017 roku. Chociaż była to gra Ponzi, sama została zhakowana z powodu błędu przepełnienia arytmetycznego, który spowodował utratę około 866 ETH lub 950,000 XNUMX USD w tym czasie. Możesz o tym szczegółowo przeczytać tutaj.
Musisz przeczytać: Lekcje z ataku na Tinymana, największy DEX na Algoranda
Atak Denial of Service
Scenariusz w świecie rzeczywistym:
Wyobraź sobie, że jesteś na Uniwersytecie Technologicznym Bitcoin. Wszystko wydaje się w porządku, z wyjątkiem tego, że dla wszystkich jest wspólny stół jadalny. I niestety jest niewiele osób z innej klasy, którym zawsze udaje się zająć stół w jadalni przed kimkolwiek z Twojej klasy.
W praktycznym scenariuszu odmawiają wszystkim podstawowych usług, co skutkuje stratą cennego czasu. Nazywamy to atakiem typu „odmowa usługi”.
Przykład kodu w świecie rzeczywistym:
W grze o nazwie Król Eterukażdy może zostać królem. Ale zasada zostania królem jest taka, że osoba powinna zdeponować więcej eteru niż obecny król. Można to zrobić, dzwoniąc pod numer claimThrone()
funkcja kontraktu King of Ether, w którym osoba wysyła eter bezpośrednio do poprzedniego króla i staje się nowym królem.
function claimThrone() external payable { require(msg.value > balance, "Need to pay more to become the king"); (bool sent, ) = king.call{value: balance}(""); require(sent, "Failed to send Ether"); balance = msg.value; king = msg.sender; }
Jak można się domyślić, ten kod jest podatny na atak DoS, ale jak? W tym celu musisz zrozumieć, że w Ethereum istnieją dwa rodzaje adresów - pierwszy to adres osoby zewnętrznej posiadane konto lub po prostu adres portfela, a drugi to adres umowy. Teraz eter może być wysłany z dowolnego z tych typów adresów.
Jeśli tak, eter zostanie wysłany na adres kontraktu, kontrakt stanie się królem. Załóżmy jednak, że ta nowa umowa nie ma fallback()
funkcja, która jest niezbędna, jeśli umowa chce zaakceptować ether. Następnie, jeśli pojawi się nowa osoba i spróbuje zadzwonić claimThrone()
funkcja, to zawsze zawiedzie!
Zauważ, że dzieje się tak również częściowo dlatego, że claimThrone()
funkcja jawnie sprawdza, czy transfer eteru się powiódł, czy nie w drugiej wymaganej instrukcji. Możesz znaleźć cały kod i wykonać na nim atak DoS tutaj.
Możliwe jest również, że kod jest podatny na atak DoS, jeśli kod zawiera pętlę na tablicy o dużych rozmiarach. Dzieje się tak, ponieważ limit gazu w takich przypadkach może zostać przekroczona. Możesz o tym przeczytać tutaj.
Wpływ:
Gra o nazwie Rządowy, który najwyraźniej był schematem Ponziego, utknął z 1100 eterem, ponieważ do przetworzenia wypłaty potrzebna była duża ilość gazu.
Niebezpieczna losowość
Scenariusz w świecie rzeczywistym:
Był kiedyś człowiek o imieniu Hesky, któremu zawsze towarzyszyła jego małpa Pesky. Hesky prowadził gry loteryjne i osiągał dobre zyski. Pewnego dnia Alice zauważyła, że Hesky uważnie wpatruje się w swoją małpę Pesky. Potem zobaczyła, jak pisze coś na kawałku papieru i zapieczętował to w kopercie. Zaciekawiona postanowiła zbadać dalej.
Później tego wieczoru Alicja zobaczyła, że zwycięzca loterii został wyłoniony przez publiczne otwarcie zapieczętowanej koperty. Po obserwowaniu go przez kilka dni, Alice zorientowała się, że Hesky zdecydował o wygranej na loterii, patrząc na gesty Pesky'ego (na przykład, jeśli małpa podrapała się w głowę, Hesky zapisał 10)! Teraz Alicja miała formułę, aby wygrać każdą loterię i musiała tylko kupić los na loterię z odpowiednim numerem!
Hesky założył, że jego „przypadkowy” sposób decydowania o zwycięzcy loterii nigdy nie zostanie wymyślony, ale rzeczywiście się mylił.
Przykład kodu w świecie rzeczywistym:
W tym przykładzie liczba losowa jest generowana na podstawie skrótu kombinacji numeru bloku i jego znacznika czasu bloku. ten skrót jest następnie przypisywany do zmiennej odpowiedzi. Teraz każdy, kto odgadnie tę (pozornie) losową liczbę, zostanie nagrodzony 1 eterem. Czy uważasz, że jest to nie do zhakowania?
function guess(uint _guess) public { uint answer = uint( keccak256(abi.encodePacked(blockhash(block.number - 1), block.timestamp)) ); if (_guess == answer) { (bool sent, ) = msg.sender.call{value: 1 ether}(""); require(sent, "Failed to send Ether"); } }
Nie! Atakujący może nadal odgadnąć tę losową liczbę, po prostu kopiując i wklejając kod, aby wygenerować wartość przypisaną do zmiennej odpowiedzi i przekazać tę samą zmienną odpowiedzi do guess()
funkcjonować!
guessTheRandomNumber.guess(answer);
Możesz znaleźć pełny kod tutaj. Aby uniknąć tego ataku, zaleca się użycie weryfikowalnej funkcji losowej, takiej jak Łańcuch VRF.
Wpływ:
Około 400 ETH zostało utraconych w wyniku ataku na Loteria Smart Billions kontrakt. Co zaskakujące, nawet sama loteria kontraktowa była schematem Ponziego (ała!).
Manipulacja czasem
Scenariusz w świecie rzeczywistym:
Satoshi uwielbia jeść ciasteczka. Uwielbia wszystkie rodzaje ciasteczek, które robi jego mama. Ale jego matka jest bardzo surowa i uważa, że jedzenie zbyt wielu ciasteczek nie jest dla niego dobre. Tak więc jego mama ustala zasadę, że dostanie ciasteczka dopiero o 8:XNUMX.
Tego samego dnia o 7:45 Satoshi biegnie do matki i prosi o ciasteczka. Jego matka pyta: „Która godzina?”
"Jest 8 godzina!" - on odpowiada.
"Dobra. Potem wyjmij ciasteczka z mojej szafki.
I w ten sposób Satoshi był w stanie skutecznie manipulować czasem o 15 minut, aby mógł dostać swoje ciasteczka! Co za głodny ciasteczek!
Przykład kodu w świecie rzeczywistym:
Znacznik czasu bloku można manipulować przez około 15 sekund przez górnika. W ten sposób górnik może ustawić korzystny znacznik czasu i uwzględnić swoją transakcję w tym samym bloku, który wydobywa. Funkcja play()
należy do kontraktu Game o nazwie G-Dot.
function play() public { require(now > 1640392200 && neverPlayed == true); neverPlayed = false; msg.sender.transfer(1500 ether);
}
Ten kontrakt nagradza gracza, który jako pierwszy wywoła funkcję gry, 1500 Eter. Ale jak widać, funkcja play może zostać wywołana tylko wtedy, gdy now lub block.timestamp transakcji zawierającej wywołanie play()
funkcja jest większa niż czas epoki 1640392200.
Górnik może łatwo manipulować tym znacznikiem czasu i dołączyć swoją transakcję wywołania play()
funkcjonować w tym samym bloku tak, że on sam jest pierwszym graczem. W ten sposób masz gwarancję, że górnik wygra grę!
Wpływ:
Block.timestamp został użyty do wygenerowania liczb losowych w Rządowy i tym samym był podatny na ataki manipulacji czasem.
Skontaktuj się z QuillAudits
QuillAudits to bezpieczna platforma audytów inteligentnych kontraktów zaprojektowana przez QuillHash
Technologie.
Jest to platforma audytowa, która rygorystycznie analizuje i weryfikuje inteligentne kontrakty w celu sprawdzenia luk w zabezpieczeniach poprzez skuteczny przegląd ręczny za pomocą narzędzi do analizy statycznej i dynamicznej, analizatorów gazów oraz asymulatorów. Ponadto proces audytu obejmuje również szeroko zakrojone testy jednostkowe oraz analizę strukturalną.
Przeprowadzamy zarówno audyty smart kontraktów, jak i testy penetracyjne w celu znalezienia potencjału
luki w zabezpieczeniach, które mogą zaszkodzić integralności platformy.
Jeśli potrzebujesz pomocy w audycie inteligentnych kontraktów, skontaktuj się z naszymi ekspertami tutaj!
Aby być na bieżąco z naszą pracą, dołącz do naszej społeczności:-
Twitter | LinkedIn | Facebook | Telegram
Post Przewodnik dla początkujących dotyczący audytu inteligentnych kontraktów: część 1 pojawiła się najpierw na Blog Quillhash.
Źródło: https://blog.quillhash.com/2022/01/19/beginners-guide-to-smart-contract-auditing-part-1/
- "
- &
- 000
- 2016
- 7
- O nas
- Konto
- adres
- Wszystkie kategorie
- już
- Chociaż
- analiza
- Audyt
- autonomiczny
- Początek
- BEST
- Bitcoin
- blockchain
- Bug
- kupować
- wezwanie
- Etui
- Wykrywanie urządzeń szpiegujących
- klasyczny
- kod
- Moneta
- połączenie
- wspólny
- społeczność
- zawiera
- umowa
- umowy
- cookies
- mógłby
- Tworzenie
- Crypto
- Aktualny
- DAO
- dzień
- Zdecentralizowane
- Denial of Service
- detal
- Dex
- na dół
- z łatwością
- jeść
- ETH.
- Eter
- ethereum
- Ethereum blockchain
- Ethereum Klasyczny
- przykład
- Wykorzystać
- w porządku
- i terminów, a
- Darmowy
- funkcjonować
- gra
- Games
- GAS
- Generować
- GitHub
- będzie
- dobry
- poprowadzi
- siekać
- hacki
- haszysz
- głowa
- tutaj
- W jaki sposób
- HTTPS
- badać
- IT
- Java
- przystąpić
- skok
- król
- język
- duży
- Linia
- długo
- poszukuje
- loteria
- mężczyzna
- milion
- mama
- z naszej
- organizacja
- Papier
- Wzór
- Zapłacić
- Ludzie
- kawałek
- Platforma
- Grać
- gracz
- ponzi
- Schemat Ponzi
- power
- wygląda tak
- Programiści
- Programowanie
- publiczny
- rewers
- przeglądu
- Nagrody
- reguły
- Satoshi
- bezpieczeństwo
- zestaw
- mądry
- inteligentna umowa
- Inteligentne kontrakty
- So
- solidność
- coś
- Spin
- dzielić
- rozpoczęty
- Zestawienie sprzedaży
- Strategia
- udany
- Z powodzeniem
- tech
- Testy
- Przez
- czas
- narzędzia
- transakcja
- nieobsługiwany
- uniwersytet
- Nowości
- wartość
- Luki w zabezpieczeniach
- wrażliwość
- Wrażliwy
- Portfel
- Co
- Koło
- KIM
- wygrać
- Praca
- pisanie