Vodiči za prelijevanje i prekomjerno slijetanje na pametnim ugovorima.

Razmotrite sljedeći dijagram:

Razumijevanje prekomjernih i nedovoljnih napada na pametne ugovore

To je normalan kilometraža koji izračunava udaljenost vašeg automobila. Ovaj brojač kilometara kreće se od 000000 – 999999. Zbog toga će se onog trenutka, čim prijeđete na 1.000.000 km, vratiti na 000000.

Ovo može imati ozbiljne ozbiljne posljedice.

Razumijevanje prekomjernih i nedovoljnih napada na pametne ugovore

Razmislite o prijelazu tisućljeća i problem Y2K. Y2K je bila vrsta računalnih bugova koji su prijetili da će izazvati pustoš na prijelazu milenija. Da bi bilo što jednostavnije, mnogi su programi predstavljali četveroznamenkaste godine sa samo posljednje dvije znamenke. Dakle, 1998. pohranjena je kao 98, a 1999. kao 99. Međutim, to bi bilo problematično kada se godina promijeni u 2000. godinu, jer će je sustav spremiti kao 00 i vratiti natrag na 1900..

Oba primjera koja smo vam naveli gore su klasa pogrešaka koja se naziva “prelijevanje cijelog broja”. U ovom ćemo vodiču istražiti štetne učinke prekomjernih i nedovoljnih napada na pametne ugovore.

Pogreška preljeva

Do preljeva dolazi kada se broj poveća za maksimalnu vrijednost. Pretpostavimo da deklariramo varijablu uint8, koja je nepotpisana varijabla i može trajati do 8 bitova. To znači da može imati decimalne brojeve između 0 i 2 ^ 8-1 = 255.

Imajući ovo na umu, razmotrite sljedeći primjer.

uint a = 255;

a ++;

To će dovesti do prelijevanja, jer je maksimalna vrijednost 255.

Solidnost može obraditi do 256-bitnih brojeva. Povećanje za 1 dovelo bi do situacije prelijevanja:

To će dovesti do prelijevanja, jer je maksimalna vrijednost 255.

Čvrstoća može obraditi do 256 bitnih brojeva. Povećanje za 1 dovelo bi do situacije prelijevanja:

0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF

+ 0x0000000000000000000000000000000000001

—————————————-

= 0x000000000000000000000000000000000000

Provjerimo pogrešku prelijevanja jednostavnim ugovorom o prijenosu tokena (kôd preuzet iz GitHub):

mapiranje (adresa => uint256) javni bilansOf;

// NEOSIGURATI

prijenos funkcije (adresa _to, uint256 _value) {

/ * Provjerite ima li pošiljatelj saldo * /

zahtijeva (balanceOf [msg.sender] >= _vrijednost);

/ * Zbrajanje i oduzimanje novih stanja * /

balanceOf [msg.sender] – = _value;

balanceOf [_to] + = _value;

}

// SIGURNO

prijenos funkcije (adresa _to, uint256 _value) {

/ * Provjerite ima li pošiljatelj salda i ima li preljeva * /

zahtijeva (balanceOf [msg.sender] >= _vrijednost && balanceOf [_to] + _value >= saldoOf [_to]);

/ * Zbrajanje i oduzimanje novih stanja * /

balanceOf [msg.sender] – = _value;

balanceOf [_to] + = _value;

}

Pa, što imamo ovdje?

Postoje dvije funkcije “prijenosa” koje se koriste u gore navedenom programu. Jedna provjerava preljev, dok druga ne provjerava. Funkcija sigurnog prijenosa provjerava doseže li saldo maksimalnu vrijednost.

Sada morate imati na umu jedno. Ova funkcija možda neće biti relevantna cijelo vrijeme, posebno u gore navedenom scenariju. Kao programer, moramo razmišljati hoće li vrijednost ikad doseći tako visoku razinu ili samo bespotrebno troše plin.

Pogreška podlijevanja

Sada dolazimo do drugog kraja spektra, pogreške Underflow. To djeluje u potpuno suprotnom smjeru. Sjećate se kako uint8 može poprimiti vrijednosti samo između 0 i 255? Razmotrite sljedeći kod.

unint8 a = 0;

a–;

Upravo smo uzrokovali podlivanje zbog kojeg će a imati najveću moguću vrijednost koja iznosi 255.

Primjena iste logike u solidnim pametnim ugovorima:

0x000000000000000000000000000000000000

– 0x0000000000000000000000000000000000001

—————————————-

= 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF

Kao što vidite, to može dovesti do ozbiljnog pogrešnog prikazivanja podataka.

Provjerimo komad koda i vidjet ćemo kako jednostavan preljev može prouzročiti pustoš u pametnom ugovoru (kod preuzet s GitHub-a):

mapiranje (adresa => uint256) javni bilansOf;

// NEOSIGURATI

prijenos funkcije (adresa _to, uint256 _value) {

/ * Provjerite ima li pošiljatelj saldo * /

zahtijeva (balanceOf [msg.sender] >= _vrijednost);

/ * Zbrajanje i oduzimanje novih stanja * /

balanceOf [msg.sender] – = _value;

balanceOf [_to] + = _value;

}

// SIGURNO

prijenos funkcije (adresa _to, uint256 _value) {

/ * Provjerite ima li pošiljatelj salda i ima li preljeva * /

zahtijeva (balanceOf [msg.sender] >= _vrijednost && balanceOf [_to] + _value >= saldoOf [_to]);

/ * Zbrajanje i oduzimanje novih stanja * /

balanceOf [msg.sender] – = _value;

balanceOf [_to] + = _value;

}

U gore navedenom primjeru koda, haker može iskoristiti prednost manipulateMe jer su dinamički nizovi pohranjeni u sekvencijalnom načinu. Sve što haker treba učiniti je:

Pozovite popBonusCode da se prelije

Izračunajte mjesto pohrane manipulateMe

Izmijenite i ažurirajte vrijednost manipulateMe pomoću modifyBonusCode

Očito je jednostavno ukazati na sve pogreške u izoliranim funkcijama, međutim, zamislite dugi i komplicirani pametni ugovor s tisućama redaka koda. Zaista je lako izgubiti trag o takvoj pogrešci tijekom provjere koda.

Opasnosti od prelijevanja i napada podlijevanja

Vjerojatnije je da će se dogoditi pogreška preljeva od pogreške prelijevanja, jer će biti donekle neizvedivo da netko dobije potreban broj tokena da izazove preljev.

Zamislite situaciju u kojoj držač tokena ima samo X žetona. Pretpostavimo da pokušava potrošiti X + 1 žetona. Ako program to ni ne provjeri, postoji vjerojatnost da će napadač završiti s više tokena od onoga što zapravo ima i dobiti maksimalnu ravnotežu.

Razmotrite sljedeći primjer (preuzeto iz nethembe):

čvrstoća pragme ^ 0,4,22;

žeton ugovora {

mapiranje (adresa => uint) ravnoteže;

prijenos funkcije (adresa _to, uint _value) public {

require (saldo [msg.sender] – _value >= 0);

stanja [msg.sender] – = _value;

ravnoteže [_to] + = _value;

}

}

U gore navedenom kodu, uvjet zahtjeva u funkciji “prijenosa” na prvi pogled može izgledati ispravno, ali samo dok ne shvatite da operacije između dviju jedinica stvaraju vrijednost jedinice.

Što u konačnici to znači, jest vrijednost stanja [msg.sender] – _value >= 0 uvijek će biti istina bez obzira na stanje.

Zbog ovog stanja haker zapravo može posjedovati više sredstava od onoga što zapravo posjeduje i maksimalizirati svoju ravnotežu. Npr. ako haker posjeduje 100 tokena i pokuša posjedovati 101 tokena, na kraju će dobiti 100-101 tokena, što mu daje 2 ^ 256-1 tokena kao rezultat podlijevanja!

To jednostavno može slomiti cijeli sustav.

Problemi u stvarnom svijetu uzrok napada podlijevanja

4chan’s / biz / grupirani zajedno kreirali su „Proof of Weak Hands Coin Coin“ ili POWH. Bila je to legitimna Ponzijeva shema, međutim, ljudi su je i dalje kupovali i narasla je na preko milijun dolara.

Međutim, ispada da programeri novčića POWH nisu osigurali sve operacije i nisu uspjeli podići odgovarajuću obranu od napada prelijevanja i podlijevanja. Upravo iz tog razloga nepoznati je haker uspio izvući 2000 ETH koji je vrijedio oko 2,3 milijuna američkih dolara.

Kao što vidite, za programera je važno ojačati svoju obranu od napada prelijevanja i podlijevanja. Kao lovac na nagrade, trebali biste biti u potrazi za napadima prelijevanja / podlijevanja.

Mike Owergreen Administrator
Sorry! The Author has not filled his profile.
follow me