Velkommen til begynderguiden til smart kontraktrevision! En af de bedste måder at komme i gang med smart kontraktrevision er at springe ind og se på nogle få almindelige typer af sårbarheder i smarte kontrakter.
Det ville være nyttigt, hvis du allerede har en grundlæggende forståelse af Ethereums Solidity-programmeringssprog. Som vi vil se på nogle af koderne skrevet af Noob solidity programmører.
Re-Entrancy Attack
Real-World Scenario:
Forestil dig, at du har 50 chokolader. Du har en fræk lillesøster, som du til enhver tid kun har tilladt at tage 2 chokolader fra dig. Du vil heller ikke give mere end 10 chokolader til hende på en dag, da du frygter, at hun får huller i tænderne. For at sikre dette tæller du hver aften, hvor mange chokolade der er tilbage hos dig. Tror du, at dette ville virke? Eller ville du blive hacket af din lillesøster?
Desværre virker det ikke! Din søster regner ud, at du ikke er klar over, hvor mange chokolade du har, før det er aften. Så allerede dagen efter besøger din lillesøster dig 6 gange før aften og tager 2 chokolader hver gang! Dette er, hvad vi kalder et genindtræden angreb.
Her opdaterer du antallet af chokolader, du har om aftenen, i stedet for at opdatere antallet, hver gang din søster tager 2 chokolader fra dig. Det er også, hvad der sker med smart kontrakt. Den smarte kontrakt antager en bestemt balance, mens angriberen faktisk har travlt med at trække en vis mængde krypto fra kontrakten flere gange.
Eksempel på kode i den virkelige verden:
Denne kode hører til en smart kontrakt kaldet uden bankkonto. Enhver kan trække ether fra en ubanket kontrakt, så længe saldi af msg.sender (dvs. den, der ringer til withdraw
funktion ) er større end eller lig med det beløb, der er bedt om at hæve.
function withdraw(uint _amount) { require(balances[msg.sender] >= _amount); msg.sender.call.value(_amount)(); balances[msg.sender] -= _amount;
}
Bemærk, at der bruges et opkaldsnøgleord til at sende den nødvendige mængde ether til msg.sender
. En angriber kan udnytte dette ved at oprette en kontrakt kaldet Thief, hvori han kalder tilbagetrækningsfunktionen i en fallback()
fungere. EN fallback()
funktion i Solidity er en speciel funktion, der bliver udført, når ether sendes til den smarte kontrakt.
Det betyder, at en angriber er i stand til det kalder tilbagetrækningsfunktionen rekursivt. Før den smarte kontrakt opdateres, vil saldi af msg.sender
på den sidste kodelinje har angriberen allerede trukket ether tilbage flere gange. Dette kunne undgås, hvis saldierne opdateres, før du bruger opkaldsnøgleordet, og dermed følger et checks-effekter-interaktioner mønster.
Indvirkning:
det første reentrancy-angreb nogensinde skete i 2016 på en DAO (Decentralized Autonomous Organization), der resulterede i et hack på omkring $50 millioner. For at vende dette hack splittede Ethereum-fællesskabet Ethereum-blokkæden, som gav anledning til ETC (Ethereum Classic) og ETH (Ethereum).
Aritmetisk overløb og underløb
Real-World Scenario:
Lad os spille et tankespil. Det består af et Spin-the-hjul, og vinderen afgøres baseret på det største antal, han er i stand til at få ved at dreje hjulet. Hjulet er mærket over det hele fra 256 til -256.
Reglerne for spillet er, at pointeren for alle spillere hviler på 0 i begyndelsen af hvert spin. Og en spiller må kun spinde i retning af negative tal. Hvordan vil du vinde dette spil?
En god strategi til at vinde dette spil hver gang ville være at dreje hjulet med en sådan kraft, at hjulet drejer op til -256 og derefter bliver til 256 på én gang. Dette er muligt, fordi 256 kommer lige efter -256 på hjulet. Det er det, vi kalder et aritmetisk underløb. Og aritmetisk overløb er bare omvendt af dette.
Eksempel på kode i den virkelige verden:
An underløb eller overløb sker, når en aritmetisk operation når sit minimum eller 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;
}
_amount
parameteren for tilbagetrækningsfunktionen er et heltal uden fortegn. Værdien af balancemappingen (som er som en ordbog i python eller et nøgleværdi-par i C++ eller Java) er også et heltal uden fortegn.
mapping(address => uint256) public balances
Den påkrævede opgørelse kontrollerer, om saldi af msg.sender
er positiv eller ej. Men dette udsagn vil altid være sandt, selvom beløbet er større end saldi af msg.sender
. Dette skyldes, at både balances
, _amount
variabler er af typen heltal uden fortegn og deres aritmetiske resultat (efter underløb) vil også være et heltal uden fortegn!
Og som du måske husker, er et heltal uden fortegn altid positivt. Det betyder, at en angriber er i stand til at hæve en ubegrænset mængde Ether fra den smarte kontrakt! Du kan finde et detaljeret eksempel og implementeringskode for denne sårbarhed link..
En anden afgørende ting at bemærke her er, at den aritmetiske operation mellem f.eks. to heltal uden fortegn også er et heltal uden fortegn. Det kan være farligt, hvis dette overses i smarte kontrakter, da det kan resultere i uønskede sikkerhedsbrud!
function votes(uint postId, uint upvote, uint downvotes) { if (upvote - downvote < 0) { deletePost(postId) }
}
Som du måske har bemærket i ovenstående eksempel, er if-sætningen ret meningsløs som upvote - downvote
vil altid være positiv. Og indlægget bliver slettet, selvom downvotes
er større end upvotes
. For at undgå sådanne angreb anbefales det at bruge en Solidity compiler version større end 0.8.0.
Indvirkning:
En mønt kaldet PoWH mønt blev lanceret i 2017. Selvom det var et Ponzi-spil, blev det selv hacket på grund af en aritmetisk overløbsfejl, der resulterede i et tab på omkring 866 ETH eller $950,000 på det tidspunkt. Du kan læse om dette i detaljer link..
Skal læses: Lektioner fra angrebet på Tinyman, største DEX på Algorand
Denial of Service-angreb
Real-World Scenario:
Forestil dig, at du er på et Bitcoin Tech University. Alt virker fint bortset fra at der er fælles spisebord til alle. Og desværre er der få mennesker fra en anden klasse, der altid når at indtage spisebordet før nogen fra din klasse.
I det praktiske scenarie nægter de den væsentlige service til alle, hvilket resulterer i kostbar tid. Dette er, hvad vi kalder et 'Denial of Service-angreb'.
Eksempel på kode i den virkelige verden:
I spillet kaldet Æterens konge, enhver kan blive konge. Men reglen for at blive konge er, at en person skal deponere mere æter end den nuværende konge. Dette kan gøres ved at ringe til claimThrone()
funktion af Ether-kongen-kontrakten, hvor personen sender ether direkte til den tidligere konge og bliver den nye konge.
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 måske har gættet, er denne kode sårbar over for et DoS-angreb, men hvordan? Til dette skal du forstå, at der er to typer adresser i Ethereum - først er adresse på en ekstern ejet konto eller blot adressen på en tegnebog, og den anden er kontraktadresse. Nu kan etheren sendes fra en af disse adressetyper.
Hvis denne, ether sendes af kontraktadressen, bliver kontrakten kongen. Men lad os antage, at denne nye kontrakt ikke har en fallback()
funktion, som er nødvendig, hvis kontrakten ønsker at acceptere ether. Hvis der så kommer en ny person og prøver at ringe til claimThrone()
funktion, vil det altid mislykkes!
Bemærk, at dette også sker delvist fordi claimThrone()
funktion kontrollerer eksplicit, om overførslen af ether var vellykket eller ej i den anden påkrævede erklæring. Du kan finde den komplette kode og lave et DoS-angreb på den link..
Det er også muligt for en kode at være sårbar over for et DoS-angreb, hvis koden har en loop over en række store størrelser. Dette sker, fordi gasgrænse kan overskrides i sådanne tilfælde. Du kan læse om det link..
Indvirkning:
Et spil kaldet Statslig, som tilsyneladende var en Ponzi-ordning, gik i stå med 1100 ether, fordi der skulle en stor mængde gas til for at behandle udbetalingen.
Usikker tilfældighed
Real-World Scenario:
Engang var der en mand ved navn Hesky, som altid var ledsaget af sin abe Pesky. Hesky gennemførte lotterispil og opnåede gode overskud. En dag lagde Alice mærke til, at Hesky stirrede intenst på sin abe Pesky. Så så hun ham skrive noget på et stykke papir og lukkede det i en konvolut. Nysgerrig besluttede hun at undersøge nærmere.
Senere samme aften så Alice, at vinderen af lotteriet blev afgjort ved offentligt at åbne den forseglede konvolut. Efter at have set ham i et par dage fandt Alice ud af, at Heskyen afgjorde det vindende lottonummer ved at se på Peskys bevægelser (hvis aben f.eks. kløede sig i hovedet, skrev Hesky 10 ned)! Nu havde Alice formlen til at vinde hvert lotteri og skulle bare købe lotterisedlen med det rigtige nummer!
Hesky havde antaget, at hans "tilfældige" måde at afgøre vinderen af lotteriet aldrig kan finde ud af, men han var faktisk forkert.
Eksempel på kode i den virkelige verden:
I dette eksempel genereres et tilfældigt tal baseret på hashen af kombinationen af en bloks nummer og dens bloktidsstempel. denne hash tildeles derefter svarvariablen. Nu bliver enhver, der gætter dette (tilsyneladende) tilfældige tal, belønnet med 1 Ether. Tror du, at dette er uhackbart?
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"); } }
Nix! En angriber kan stadig gætte dette tilfældige tal ved blot at kopiere og indsætte koden for at generere den værdi, der er tildelt svarvariablen, og sende den samme svarvariabel til guess()
fungere!
guessTheRandomNumber.guess(answer);
Du kan finde den komplette kode link.. For at undgå dette angreb anbefales det at bruge en verificerbar tilfældig funktion som f.eks Kædelink VRF.
Indvirkning:
Omkring 400 ETH gik tabt på grund af et angreb på Smart Billions lotteri kontrakt. Overraskende nok var selve kontraktlotteriet en Ponzi-ordning (uh!).
Tidsmanipulation
Real-World Scenario:
Satoshi elsker at spise småkager. Han elsker alle typer småkager, som hans mor laver. Men hans mor er meget streng og føler, at det ikke er godt for ham at spise for mange småkager. Så hans mor laver en regel om, at han først får småkagerne kl. 8.
Samme dag kl. 7 løber Satoshi hen til sin mor og beder om småkager. Hans mor spørger: "Hvad er klokken?"
"Klokken er 8'!" – svarer han.
"Okay. Så tag småkagerne fra mit skab.”
Og således var Satoshi i stand til at manipulere tiden med succes med 15 minutter, så han kunne få sine cookies! Hvilken kagehungrende fyr!
Eksempel på kode i den virkelige verden:
Tidsstemplet for en blok kan manipuleres med ca 15 sekunder af en minearbejder. På denne måde kan en minearbejder indstille et gunstigt tidsstempel og inkludere sin transaktion i den samme blok, som han miner. Funktionen play()
hører til en spilkontrakt kaldet G-Dot.
function play() public { require(now > 1640392200 && neverPlayed == true); neverPlayed = false; msg.sender.transfer(1500 ether);
}
Denne kontrakt belønner 1500 ether til den spiller, der er den første til at kalde spillefunktionen. Men som du kan se, kan afspilningsfunktionen kun kaldes, hvis nu eller blok.tidsstempel for transaktionen, der indeholder opkaldet til play()
funktion, er større end epoke tid 1640392200.
En minearbejder kan nemt manipulere dette tidsstempel og inkludere sin transaktion med at kalde play()
fungere i samme blok, således at han selv er den første spiller. På denne måde er det garanteret, at minearbejderen vinder spillet!
Indvirkning:
Block.timestamp blev brugt til at generere tilfældige tal i Governmental og var således sårbar over for tidsmanipulationsangreb.
Kontakt QuillAudits
QuillAudits er en sikker smart kontraktrevisionsplatform designet af QuillHash
Technologies.
Det er en revisionsplatform, der nøje analyserer og verificerer smarte kontrakter for at tjekke for sikkerhedssårbarheder gennem effektiv manuel gennemgang med statiske og dynamiske analyseværktøjer, gasanalysatorer samt assimulatorer. Derudover omfatter revisionsprocessen også omfattende enhedstests samt strukturelle analyser.
Vi udfører både smarte kontraktrevisioner og penetrationstests for at finde potentiale
sikkerhedssårbarheder, som kan skade platformens integritet.
Hvis du har brug for hjælp til revisionen af smarte kontrakter, er du velkommen til at kontakte vores eksperter her!
For at være opdateret med vores arbejde, Tilmeld dig vores fællesskab:-
Twitter | LinkedIn | Facebook | Telegram
Stillingen Begynderguide til smart kontraktrevision: Del 1 dukkede først på Quillhash-blog.
Kilde: https://blog.quillhash.com/2022/01/19/beginners-guide-to-smart-contract-auditing-part-1/
- "
- &
- 000
- 2016
- 7
- Om
- Konto
- adresse
- Alle
- allerede
- Skønt
- analyse
- revision
- autonom
- Begyndelse
- BEDSTE
- Bitcoin
- blockchain
- Bug
- købe
- ringe
- tilfælde
- Kontrol
- Classic
- kode
- Coin
- kombination
- Fælles
- samfund
- indeholder
- kontrakt
- kontrakter
- cookies
- kunne
- Oprettelse af
- krypto
- Nuværende
- DAO
- dag
- decentral
- Denial of Service
- detail
- Dex
- ned
- nemt
- spiser
- ETH
- Ether
- ethereum
- Ethereum blockchain
- Ethereum Classic
- eksempel
- Exploit
- ende
- Fornavn
- Gratis
- funktion
- spil
- Spil
- GAS
- generere
- GitHub
- gå
- godt
- vejlede
- hack
- hacks
- hash
- hoved
- link.
- Hvordan
- HTTPS
- undersøge
- IT
- Java
- deltage
- hoppe
- King (Konge)
- Sprog
- stor
- Line (linje)
- Lang
- leder
- lotteri
- mand
- million
- mor
- numre
- organisation
- Papir
- Mønster
- Betal
- Mennesker
- stykke
- perron
- Leg
- spiller
- ponzi
- Ponzi Scheme
- magt
- behandle
- Programmører
- Programmering
- offentlige
- vende
- gennemgå
- Belønninger
- regler
- Satoshi
- sikkerhed
- sæt
- Smart
- smart kontrakt
- Smarte kontrakter
- So
- soliditet
- noget
- Spin
- delt
- påbegyndt
- Statement
- Strategi
- vellykket
- Succesfuld
- tech
- tests
- Gennem
- tid
- værktøjer
- transaktion
- uden bankkonto
- universitet
- opdateringer
- værdi
- Sårbarheder
- sårbarhed
- Sårbar
- tegnebog
- Hvad
- Hjul
- WHO
- vinde
- Arbejde
- skrivning