Bienvenue dans le guide du débutant sur l'audit intelligent des contrats ! L'une des meilleures façons de se lancer dans l'audit des contrats intelligents consiste à se lancer et à examiner quelques types courants de vulnérabilités dans les contrats intelligents.
Il serait utile que vous ayez déjà une compréhension de base du langage de programmation Solidity d'Ethereum. Comme nous allons examiner certains des codes écrits par les programmeurs de solidité Noob.
Attaque de réentrée
Scénario réel :
Imaginez que vous avez 50 chocolats. Vous avez une petite sœur coquine à qui vous avez permis de ne vous prendre que 2 chocolats à la fois. Vous ne voulez pas non plus lui donner plus de 10 chocolats par jour, de peur qu'elle n'ait des caries dentaires. Pour cela, vous comptez chaque soir le nombre de chocolats qui vous restent. Pensez-vous que cela fonctionnerait? Ou vous feriez-vous pirater par votre petite sœur ?
Malheureusement, cela ne fonctionnera pas ! Votre sœur découvre que vous ne savez pas combien de chocolats vous avez jusqu'au soir. Alors dès le lendemain, votre petite sœur vous rend visite 6 fois avant le soir et prend 2 chocolats à chaque fois ! C'est ce que nous appelons une attaque de réentrée.
Ici, vous mettez à jour le nombre de chocolats que vous avez le soir au lieu de mettre à jour le nombre chaque fois que votre sœur vous prend 2 chocolats. C'est aussi ce qui se passe avec le contrat intelligent. Le contrat intelligent suppose un équilibre particulier alors que l'attaquant est en fait occupé à retirer plusieurs fois une certaine quantité de crypto du contrat.
Exemple de code réel :
Ce code appartient à un contrat intelligent appelé Unbanked. N'importe qui peut retirer de l'ether d'un contrat non bancaire tant que les soldes de l'expéditeur du msg (c'est-à-dire l'appelant de withdraw
fonction ) est supérieur ou égal au montant demandé à retirer.
function withdraw(uint _amount) { require(balances[msg.sender] >= _amount); msg.sender.call.value(_amount)(); balances[msg.sender] -= _amount;
}
Notez qu'il existe un mot clé d'appel utilisé pour envoyer la quantité requise d'éther au msg.sender
. Un attaquant peut exploiter cela en créant un contrat appelé Thief dans lequel il appelle la fonction de retrait dans un fallback()
une fonction. UNE fallback()
La fonction dans Solidity est une fonction spéciale qui est exécutée lorsque l'éther est envoyé au contrat intelligent.
Cela signifie qu'un attaquant peut appeler récursivement la fonction de retrait. Ainsi, avant les mises à jour du smart contract, les soldes des msg.sender
à la dernière ligne de code, l'attaquant a déjà retiré de l'éther plusieurs fois. Ceci pourrait être évité si les soldes sont mis à jour avant d'utiliser le mot clé d'appel, donc suite à une contrôles-effets-interactions motif.
Impact:
La la toute première attaque de réentrance s'est produit en 2016 sur un DAO (organisation autonome décentralisée) qui a abouti à un piratage d'environ 50 millions de dollars. Pour inverser ce piratage, la communauté Ethereum a scindé la blockchain Ethereum qui a donné naissance à ETC (Ethereum Classic) et ETH (Ethereum).
Débordement supérieur et inférieur arithmétique
Scénario réel :
Jouons à un jeu de réflexion. Il consiste en une roue Spin-the, et le gagnant est déterminé en fonction du plus grand nombre qu'il est capable d'obtenir en faisant tourner la roue. La roue est marquée partout de 256 à -256.
La règle du jeu est que le pointeur de tous les joueurs repose sur 0 au début de chaque tour. Et un joueur est autorisé à tourner uniquement dans le sens des nombres négatifs. Comment allez-vous gagner ce jeu ?
Une bonne stratégie pour gagner ce jeu à chaque fois serait de faire tourner la roue avec une telle puissance que la roue tourne jusqu'à -256 puis tourne à 256 en une seule fois. C'est possible car 256 vient juste après -256 sur la roue. C'est ce que nous appelons un dépassement arithmétique. Et le débordement arithmétique n'est que l'inverse de cela.
Exemple de code réel :
An débordement ou débordement se produit lorsqu'une opération arithmétique atteint son minimum ou son 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;
}
La _amount
Le paramètre de la fonction de retrait est un entier non signé. La valeur du mappage des soldes (qui ressemble à un dictionnaire en python ou à une paire clé-valeur en C++ ou Java) est également un entier non signé.
mapping(address => uint256) public balances
Le relevé requis vérifie si les soldes des msg.sender
est positif ou non. Mais cette affirmation sera toujours vraie même si le montant est supérieur aux soldes des msg.sender
. C'est parce que les deux balances
ainsi que _amount
les variables sont de type entier non signé et leur résultat arithmétique (après dépassement négatif) sera également un entier non signé !
Et comme vous vous en souvenez peut-être, un entier non signé est toujours positif. Cela signifie qu'un attaquant peut retirer une quantité illimitée d'Ether du contrat intelligent ! Vous pouvez trouver un exemple détaillé et un code d'implémentation pour cette vulnérabilité ici.
Une autre chose cruciale à noter ici est que l'opération arithmétique entre, par exemple, deux entiers non signés est également un entier non signé. Cela peut être dangereux si cela est négligé dans les contrats intelligents, car cela peut entraîner des failles de sécurité indésirables !
function votes(uint postId, uint upvote, uint downvotes) { if (upvote - downvote < 0) { deletePost(postId) }
}
Comme vous l'avez peut-être remarqué dans l'exemple ci-dessus, l'instruction if est tout à fait inutile car upvote - downvote
sera toujours positif. Et le message sera supprimé même si downvotes
est supérieure upvotes
. Pour éviter de telles attaques, il est recommandé d'utiliser une version du compilateur Solidity supérieure à 0.8.0.
Impact:
Une pièce appelée Pièce PoWH a été lancé en 2017. Bien qu'il s'agisse d'un jeu Ponzi, il a lui-même été piraté en raison d'un bogue de débordement arithmétique qui a entraîné une perte d'environ 866 ETH ou 950,000 XNUMX $ à l'époque. Vous pouvez lire à ce sujet en détail ici.
Doit lire: Leçons de l'attaque sur Tinyman, le plus grand DEX sur Algorand
Attaque par déni de service
Scénario réel :
Imaginez que vous êtes dans une université Bitcoin Tech. Tout semble bien sauf qu'il y a une table à manger commune pour tout le monde. Et malheureusement, il y a peu de gens d'une autre classe qui réussissent toujours à occuper la table à manger avant n'importe qui de votre classe.
Dans le scénario pratique, ils refusent le service essentiel à tout le monde, ce qui entraîne une perte de temps précieux. C'est ce que nous appelons une « attaque par déni de service ».
Exemple de code réel :
Dans le jeu appelé Roi-de-l'Ether, n'importe qui peut devenir roi. Mais la règle pour devenir roi est qu'une personne doit déposer plus d'éther que le roi actuel. Cela peut être fait en appelant le claimThrone()
fonction du contrat King of Ether dans lequel la personne envoie de l'éther directement au roi précédent et devient le nouveau roi.
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; }
Comme vous l'avez peut-être deviné, ce code est vulnérable à une attaque DoS, mais comment ? Pour cela, vous devrez comprendre qu'il existe deux types d'adresses dans Ethereum - le premier est le l'adresse d'un externe compte détenu ou simplement l'adresse d'un portefeuille, et le deuxième est le adresse du contrat. Maintenant, l'éther peut être envoyé à partir de l'un ou l'autre de ces types d'adresses.
Si cet éther est envoyé par l'adresse du contrat, alors le contrat deviendra le roi. Mais supposons que ce nouveau contrat n'ait pas de fallback()
fonction qui est nécessaire si le contrat veut accepter l'éther. Ensuite, si une nouvelle personne arrive et essaie d'appeler le claimThrone()
fonction, elle échouera toujours !
Notez que cela se produit aussi en partie parce que le claimThrone()
La fonction vérifie explicitement si le transfert d'éther a réussi ou non dans la deuxième instruction requise. Vous pouvez trouver le code complet et faire une attaque DoS dessus ici.
Il est également possible qu'un code soit vulnérable à une attaque DoS si le code a une boucle sur un tableau de grandes tailles. Cela se produit parce que le limite d'essence peut être dépassé dans de tels cas. Vous pouvez lire à ce sujet ici.
Impact:
Un jeu appelé Gouvernemental, qui était apparemment un stratagème de Ponzi, s'est retrouvé coincé avec 1100 éther car une grande quantité de gaz était nécessaire pour traiter le paiement.
Aléatoire non sécurisé
Scénario réel :
Il était une fois un homme nommé Hesky qui était toujours accompagné de son singe Pesky. Hesky a organisé des jeux de loterie et a réalisé de bons bénéfices. Un jour, Alice remarqua que Hesky fixait intensément son singe Pesky. Puis elle l'a vu écrire quelque chose sur un morceau de papier et l'a scellé dans une enveloppe. Curieuse, elle a décidé d'enquêter plus avant.
Plus tard dans la soirée, Alice a vu que le gagnant de la loterie avait été choisi en ouvrant publiquement l'enveloppe scellée. Après l'avoir observé pendant quelques jours, Alice a compris que le Hesky décidait du numéro de loterie gagnant en regardant les gestes de Pesky (par exemple si le singe se grattait la tête, Hesky notait 10) ! Maintenant, Alice avait la formule pour gagner à chaque loterie et n'avait plus qu'à acheter le billet de loterie avec le bon numéro !
Hesky avait supposé que sa façon "aléatoire" de décider du gagnant de la loterie ne pouvait jamais être découverte, mais il avait en effet tort.
Exemple de code réel :
Dans cet exemple, un nombre aléatoire est généré sur la base du hachage de la combinaison du numéro d'un bloc et de son horodatage de bloc. ce hachage est ensuite affecté à la variable de réponse. Maintenant, quiconque devine ce nombre (apparemment) aléatoire, est récompensé 1 Ether. Pensez-vous que c'est unhackable?
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"); } }
Non! Un attaquant peut toujours deviner ce nombre aléatoire en faisant simplement un copier-coller du code pour générer la valeur affectée à la variable de réponse, et passer la même variable de réponse au guess()
une fonction!
guessTheRandomNumber.guess(answer);
Vous pouvez trouver le code complet ici. Pour éviter cette attaque, il est recommandé d'utiliser une Fonction Aléatoire Vérifiable telle que la VRF à maillons de chaîne.
Impact:
Environ 400 ETH ont été perdus en raison d'une attaque contre le Loterie Smart Billions Contrat. Étonnamment, même la loterie contractuelle elle-même était un schéma de Ponzi (aïe !).
Manipulation du temps
Scénario réel :
Satoshi adore manger des cookies. Il aime tous les types de cookies que sa mère fait. Mais sa mère est très stricte et estime que manger trop de biscuits n'est pas bon pour lui. Sa mère a donc établi une règle selon laquelle il n'obtiendrait les cookies qu'à 8 heures.
Ce jour-là à 7h45, Satoshi court vers sa mère et demande des cookies. Sa mère demande : « Quelle heure est-il ?
"Il est 8 heures!" - il à répondu.
"D'accord. Alors prends les biscuits de mon placard.
Et ainsi, Satoshi a réussi à manipuler le temps avec succès de 15 minutes afin qu'il puisse obtenir ses cookies ! Quel type affamé de biscuits !
Exemple de code réel :
L'horodatage d'un bloc peut être manipulé d'environ en 15 secondes par un mineur. De cette façon, un mineur peut définir un horodatage favorable et inclure sa transaction dans le même bloc qu'il exploite. La fonction play()
appartient à un contrat de jeu appelé G-Dot.
function play() public { require(now > 1640392200 && neverPlayed == true); neverPlayed = false; msg.sender.transfer(1500 ether);
}
Ce contrat récompense 1500 ethers au joueur qui est le premier à appeler la fonction play. Mais comme vous pouvez le voir, la fonction play ne peut être appelée que si le now ou block.timestamp de la transaction qui contient l'appel à la play()
fonction, est supérieure à la temps d'époque 1640392200.
Un mineur peut facilement manipuler cet horodatage et inclure sa transaction d'appel du play()
fonction dans le même bloc de sorte qu'il est lui-même le premier joueur. De cette façon, il est garanti que le mineur gagnera la partie !
Impact:
Le block.timestamp a été utilisé pour générer des nombres aléatoires dans le Gouvernemental et était donc vulnérable aux attaques de manipulation temporelle.
Contactez QuillAudits
QuillAudits est une plateforme sécurisée d'audits de contrats intelligents conçue par QuillHash
Les technologies.
Il s'agit d'une plate-forme d'audit qui analyse et vérifie rigoureusement les contrats intelligents pour vérifier les vulnérabilités de sécurité grâce à un examen manuel efficace avec des outils d'analyse statiques et dynamiques, des analyseurs de gaz ainsi que des simulateurs. De plus, le processus d'audit comprend également des tests unitaires approfondis ainsi qu'une analyse structurelle.
Nous effectuons à la fois des audits de contrats intelligents et des tests d'intrusion pour trouver le potentiel
vulnérabilités de sécurité qui pourraient nuire à l'intégrité de la plate-forme.
Si vous avez besoin d'aide pour l'audit des contrats intelligents, n'hésitez pas à contacter nos experts ici!
Pour être au courant de notre travail, rejoignez notre communauté : -
Twitter | LinkedIn | Facebook | Telegram
Le poste Guide du débutant sur l'audit des contrats intelligents : Partie 1 apparaît en premier sur Blog Quillhash.
Source : https://blog.quillhash.com/2022/01/19/beginners-guide-to-smart-contract-auditing-part-1/
- "
- &
- 000
- 2016
- 7
- À propos
- Compte
- propos
- Tous
- déjà
- Bien que
- selon une analyse de l’Université de Princeton
- audit
- autonome
- Début
- LES MEILLEURS
- Bitcoin
- blockchain
- Punaise
- acheter
- Appelez-nous
- cas
- Contrôles
- classiques
- code
- Coin
- combinaison
- Commun
- Communautés
- contient
- contrat
- contrats
- biscuits
- pourriez
- La création
- Crypto
- Courant
- DAO
- journée
- Décentralisé
- Déni de service
- détail
- Dex
- down
- même
- manger
- ETH
- Éther
- Ethereum
- Blockhaus d'Ethereum
- Ethereum classique
- exemple
- Exploiter
- fin
- Prénom
- Test d'anglais
- fonction
- jeu
- Games
- GAS
- générer
- GitHub
- aller
- Bien
- guide
- entaille
- hacks
- hachage
- front
- ici
- Comment
- HTTPS
- enquêter
- IT
- Java
- rejoindre
- saut
- King
- langue
- gros
- Gamme
- Location
- recherchez-
- loterie
- man
- million
- mère
- numéros
- organisation
- Papier
- Patron de Couture
- Payer
- Personnes
- pièce
- plateforme
- Jouez
- joueur
- ponzi
- Schéma de Ponzi
- power
- processus
- Programmeurs
- Programmation
- public
- inverser
- Avis
- Programme de fidélité
- Satoshi
- sécurité
- set
- smart
- contrat intelligent
- Contrats intelligents
- So
- solidité
- quelque chose
- Spin
- scission
- j'ai commencé
- Déclaration
- de Marketing
- réussi
- Avec succès
- technologie
- tests
- Avec
- fiable
- les outils
- transaction
- non bancaire
- université
- Actualités
- Plus-value
- vulnérabilités
- vulnérabilité
- Vulnérable
- Wallet
- Quoi
- Jante
- WHO
- gagner
- Activités:
- écriture