Välkommen till nybörjarguiden till smart kontraktsrevision! Ett av de bästa sätten att komma igång med smart kontraktsrevision är att hoppa in och titta på några vanliga typer av sårbarheter i smarta kontrakt.
Det skulle vara till hjälp om du redan har en grundläggande förståelse för Ethereums programmeringsspråk Solidity. Som vi kommer att titta på några av koderna skrivna av Noob solidity programmerare.
Återinträdesattack
Real-World Scenario:
Föreställ dig att du har 50 choklad. Du har en stygg lillasyster som du bara har låtit ta 2 choklad ifrån dig vid varje enskilt tillfälle. Du vill heller inte ge mer än 10 choklad till henne på en dag, rädsla för att hon ska få karies. För att säkerställa detta räknar du varje kväll hur många choklad som finns kvar med dig. Tror du att detta skulle fungera? Eller skulle du bli hackad av din lillasyster?
Tyvärr kommer det inte att fungera! Din syster kommer på att du inte är medveten om hur mycket choklad du har förrän det är kväll. Så redan nästa dag besöker din lillasyster dig 6 gånger innan kvällen och tar 2 choklad varje gång! Detta är vad vi kallar en återinträdesattack.
Här uppdaterar du antalet choklad du har på kvällen istället för att uppdatera antalet varje gång din syster tar 2 choklad från dig. Detta är också vad som händer med smart kontrakt. Det smarta kontraktet antar en viss balans medan angriparen faktiskt är upptagen med att ta ut en viss mängd krypto från kontraktet flera gånger.
Exempel på verklig kod:
Denna kod tillhör ett smart kontrakt som kallas unbanked. Vem som helst kan ta ut eter från ett obankat kontrakt så länge saldot hos msg.sender (dvs. den som ringer till withdraw
funktion ) är större än eller lika med det belopp som begärts för att ta ut.
function withdraw(uint _amount) { require(balances[msg.sender] >= _amount); msg.sender.call.value(_amount)(); balances[msg.sender] -= _amount;
}
Observera att det finns ett samtalsnyckelord som används för att skicka den nödvändiga mängden eter till msg.sender
. En angripare kan utnyttja detta genom att skapa ett kontrakt som heter Thief där han anropar tillbakadragningsfunktionen i en fallback()
fungera. A fallback()
funktion i Solidity är en speciell funktion som exekveras när eter skickas till det smarta kontraktet.
Det betyder att en angripare kan anropa uttagsfunktionen rekursivt. Alltså, innan det smarta kontraktet uppdateras, saldon av msg.sender
på den sista raden kod har angriparen redan dragit tillbaka eter flera gånger. Detta kan undvikas om saldot uppdateras innan du använder samtalsnyckelordet, alltså efter ett kontroller-effekter-interaktioner mönster.
Inverkan:
Smakämnen första återinträdesattack någonsin hände 2016 på en DAO (Decentralized Autonomous Organization) som resulterade i ett hack på cirka 50 miljoner dollar. För att vända detta hack delade Ethereum-gemenskapen Ethereum-blockkedjan som gav upphov till ETC (Ethereum Classic) och ETH (Ethereum).
Aritmetiskt överflöde och underflöde
Real-World Scenario:
Låt oss spela ett tankespel. Den består av ett Spin-the wheel, och vinnaren avgörs baserat på det största antalet han kan få när han snurrar på hjulet. Hjulet är märkt överallt från 256 till -256.
Reglerna för spelet är att pekaren för alla spelare vilar på 0 i början av varje snurr. Och en spelare får endast snurra i riktning mot negativa tal. Hur kommer du att vinna det här spelet?
En bra strategi för att vinna det här spelet varje gång skulle vara att snurra hjulet med sådan kraft att hjulet snurrar upp till -256 och sedan blir 256 på en gång. Detta är möjligt eftersom 256 kommer strax efter -256 på hjulet. Detta är vad vi kallar ett aritmetiskt underflöde. Och aritmetiskt spill är bara tvärtom av detta.
Exempel på verklig kod:
An underflöde eller överflöde händer när en aritmetisk operation når sitt minimum eller maximum.
function withdraw(uint _amount) public { require(balances[msg.sender] - _amount > 0); address payable to = payable(msg.sender); to.transfer(_amount); balances[msg.sender] -= _amount;
}
Smakämnen _amount
parametern för uttagsfunktionen är ett heltal utan tecken. Värdet på balansmappningen (som är som en ordbok i python eller ett nyckel-värdepar i C++ eller Java) är också ett heltal utan tecken.
mapping(address => uint256) public balances
Den erforderliga redogörelsen kontrollerar om saldona av msg.sender
är positivt eller inte. Men detta påstående kommer alltid att vara sant även om beloppet är större än saldot på msg.sender
. Detta beror på att både balances
och _amount
variabler är av typen heltal utan tecken och deras aritmetiska resultat (efter underflöde) kommer också att vara ett heltal utan tecken!
Och som du kanske minns är ett heltal utan tecken alltid positivt. Detta innebär att en angripare kan ta ut en obegränsad mängd Ether från det smarta kontraktet! Du kan hitta ett detaljerat exempel och implementeringskod för denna sårbarhet här..
En annan viktig sak att notera här är att den aritmetiska operationen mellan säg två heltal utan tecken också är ett heltal utan tecken. Det kan vara farligt om detta förbises i smarta kontrakt, eftersom det kan resultera i oönskade säkerhetsintrång!
function votes(uint postId, uint upvote, uint downvotes) { if (upvote - downvote < 0) { deletePost(postId) }
}
Som du kanske har märkt i exemplet ovan är if-satsen ganska meningslös som upvote - downvote
kommer alltid att vara positivt. Och inlägget kommer att raderas även om downvotes
är större än upvotes
. För att undvika sådana attacker rekommenderas det att använda en Solidity-kompilatorversion större än 0.8.0.
Inverkan:
Ett mynt som heter PoWH mynt lanserades 2017. Även om det var ett Ponzi-spel, blev det hackat på grund av en aritmetisk översvämningsbugg som resulterade i en förlust på cirka 866 ETH eller $950,000 XNUMX vid den tiden. Du kan läsa om detta i detalj här..
Måste läsa: Lärdomar från attacken på Tinyman, största DEX på Algorand
Denial of Service Attack
Real-World Scenario:
Föreställ dig att du är på ett Bitcoin Tech University. Allt verkar bra förutom att det finns ett gemensamt matbord för alla. Och tyvärr är det få personer från en annan klass som alltid lyckas ockupera matbordet före någon från din klass.
I det praktiska scenariot nekar de alla nödvändiga tjänster, vilket resulterar i värdefull tidsförlust. Detta är vad vi kallar en "Denial of Service-attack".
Exempel på verklig kod:
I spelet kallas Kung av eter, vem som helst kan bli kung. Men regeln för att bli kung är att en person ska deponera mer eter än den nuvarande kungen. Detta kan göras genom att ringa till claimThrone()
funktion av eterkonungens kontrakt där personen skickar eter direkt till den tidigare kungen och blir den nya kungen.
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; }
Som du kanske har gissat är den här koden sårbar för en DoS-attack, men hur? För detta måste du förstå att det finns två typer av adresser i Ethereum - den första är den adress till en extern ägt konto eller helt enkelt adressen till en plånbok, och den andra är kontraktsadress. Nu kan etern skickas från någon av dessa adresstyper.
Om detta, eter skickas av kontraktsadressen, kommer kontraktet att bli kungen. Men låt oss anta att detta nya kontrakt inte har en fallback()
funktion som är nödvändig om kontraktet vill acceptera eter. Sedan om en ny person kommer och försöker ringa claimThrone()
funktion, det kommer alltid att misslyckas!
Lägg märke till att detta också händer delvis på grund av claimThrone()
funktionen kontrollerar uttryckligen om överföringen av eter var framgångsrik eller inte i den andra obligatoriska satsen. Du kan hitta hela koden och göra en DoS-attack på den här..
Det är också möjligt för en kod att vara sårbar för en DoS-attack om koden har en loop över en rad stora storlekar. Detta händer eftersom gasgräns kan överskridas i sådana fall. Du kan läsa om det här..
Inverkan:
Ett spel som heter statlig, som tydligen var ett Ponzi-schema, fastnade med 1100 eter eftersom en stor mängd gas behövdes för att behandla utbetalningen.
Osäker slumpmässighet
Real-World Scenario:
En gång var det en man som hette Hesky som alltid åtföljdes av sin apa Pesky. Hesky genomförde lotterispel och gjorde goda vinster. En dag lade Alice märke till att Hesky stirrade intensivt på sin apa Pesky. Sedan såg hon hur han skrev något på ett papper och förseglade det i ett kuvert. Nyfiken bestämde hon sig för att undersöka vidare.
Senare på kvällen såg Alice att vinnaren av lotteriet avgjordes genom att offentligt öppna det förseglade kuvertet. Efter att ha sett honom i några dagar kom Alice på att Hesky bestämde det vinnande lottonumret genom att titta på Peskys gester (om apan till exempel kliade sig i huvudet skrev Hesky ner 10)! Nu hade Alice formeln att vinna varje lotteri och var bara tvungen att köpa lotten med rätt nummer!
Hesky hade antagit att hans "slumpmässiga" sätt att avgöra vinnaren av lotteriet aldrig kan listas ut, men han var verkligen felaktig.
Exempel på verklig kod:
I det här exemplet genereras ett slumptal baserat på hashen av kombinationen av ett blocks nummer och dess blocktidsstämpel. denna hash tilldelas sedan svarsvariabeln. Nu belönas alla som gissar detta (till synes) slumpmässiga nummer 1 Ether. Tycker du att det här är okackbart?
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"); } }
Nej! En angripare kan fortfarande gissa detta slumptal genom att helt enkelt kopiera klistra in koden för att generera värdet som tilldelats svarsvariabeln, och skicka samma svarsvariabel till guess()
fungera!
guessTheRandomNumber.guess(answer);
Du kan hitta hela koden här.. För att undvika denna attack, rekommenderas det att använda en verifierbar slumpmässig funktion som t.ex Kedjelänk VRF.
Inverkan:
Cirka 400 ETH gick förlorade på grund av en attack på Smart Billions-lotteri kontrakt. Överraskande nog var till och med själva kontraktslotteriet ett Ponzi-schema (aj!).
Tidsmanipulation
Real-World Scenario:
Satoshi älskar att äta kakor. Han älskar alla typer av kakor som hans mamma gör. Men hans mamma är väldigt strikt och tycker att det inte är bra för honom att äta för många kakor. Så hans mamma gör en regel att han får kakorna först klockan 8.
Just den dagen klockan 7 springer Satoshi till sin mamma och ber om kakor. Hans mamma frågar: "Vad är klockan?"
"Klockan är åtta!" - han svarar.
"Okej. Ta sedan kakorna från mitt skåp.”
Och därmed kunde Satoshi framgångsrikt manipulera tiden med 15 minuter så att han kunde få sina kakor! Vilken kaksugen!
Exempel på verklig kod:
Tidsstämpeln för ett block kan manipuleras med ca 15 sekunder av en gruvarbetare. På detta sätt kan en gruvarbetare sätta en gynnsam tidsstämpel och inkludera sin transaktion i samma block som han bryter. Funktionen play()
tillhör ett spelkontrakt som heter G-Dot.
function play() public { require(now > 1640392200 && neverPlayed == true); neverPlayed = false; msg.sender.transfer(1500 ether);
}
Detta kontrakt belönar 1500 eter till spelaren som är den första att anropa spelfunktionen. Men som du kan se kan uppspelningsfunktionen endast anropas om nu eller block.timestamp för transaktionen som innehåller anropet till play()
funktion, är större än epoktid 1640392200.
En gruvarbetare kan enkelt manipulera denna tidsstämpel och inkludera sin transaktion att anropa play()
fungera i samma block så att han själv är den första spelaren. På så sätt är det garanterat att gruvarbetaren vinner spelet!
Inverkan:
Block.timestamp användes för att generera slumpmässiga tal i governmental och var därmed sårbar för tidsmanipulationsattacker.
Ta kontakt med QuillAudits
QuillAudits är en säker smart kontraktsrevisionsplattform designad av QuillHash
Technologies.
Det är en revisionsplattform som noggrant analyserar och verifierar smarta kontrakt för att kontrollera säkerhetssårbarheter genom effektiv manuell granskning med statiska och dynamiska analysverktyg, gasanalysatorer samt assimulatorer. Revisionsprocessen inkluderar dessutom omfattande enhetstester samt strukturanalys.
Vi genomför både smarta kontraktsrevisioner och penetrationstester för att hitta potential
säkerhetssårbarheter som kan skada plattformens integritet.
Om du behöver hjälp med revisionen av smarta kontrakt, kontakta gärna våra experter här!
För att vara uppdaterad med vårt arbete, gå med i vår community:-
Twitter | LinkedIn | Facebook | Telegram
Posten Nybörjarguide till smart kontraktsrevision: Del 1 visades först på Quillhash-blogg.
Källa: https://blog.quillhash.com/2022/01/19/beginners-guide-to-smart-contract-auditing-part-1/
- "
- &
- 000
- 2016
- 7
- Om oss
- Konto
- adress
- Alla
- redan
- Även
- analys
- revision
- autonom
- Börjar
- BÄST
- Bitcoin
- blockchain
- Bug
- Köp
- Ring
- fall
- Kontroller
- klassiska
- koda
- Coin
- kombination
- Gemensam
- samfundet
- innehåller
- kontrakt
- kontrakt
- Cookiepolicy
- kunde
- Skapa
- crypto
- Aktuella
- <b>PostNord</b>
- dag
- decentraliserad
- Denial of Service
- detalj
- Dex
- ner
- lätt
- ät
- ETH
- Eter
- ethereum
- Ethereum blockchain
- Ethereum Classic
- exempel
- Exploit
- änden
- Förnamn
- Fri
- fungera
- lek
- Games
- GAS
- generera
- GitHub
- kommer
- god
- styra
- hacka
- hacka
- hash
- huvud
- här.
- Hur ser din drömresa ut
- HTTPS
- undersöka
- IT
- java
- delta
- hoppa
- King
- språk
- Large
- linje
- Lång
- du letar
- lotteri
- människa
- miljon
- mor
- nummer
- organisation
- Papper
- Mönster
- Betala
- Personer
- bit
- plattform
- Spela
- Spelaren
- ponzi
- Ponzi-schema
- kraft
- process
- programmerare
- Programmering
- allmän
- vända
- översyn
- Belöningar
- regler
- Satoshi
- säkerhet
- in
- smarta
- smart kontrakt
- Smarta kontrakt
- So
- fasthet
- något
- Snurra
- delas
- igång
- .
- Strategi
- framgångsrik
- Framgångsrikt
- tech
- tester
- Genom
- tid
- verktyg
- transaktion
- unbanked
- universitet
- Uppdateringar
- värde
- sårbarheter
- sårbarhet
- Sårbara
- plånbok
- Vad
- Hjul
- VEM
- vinna
- Arbete
- skrivning