Comment échapper aux contrats intelligents de l’emprise des attaques de réentrée ? Intelligence des données PlatoBlockchain. Recherche verticale. Aï.

Comment échapper aux contrats intelligents de l'embrayage des attaques de réentrance ?

Temps de lecture: 6 minutes

Si nous examinons de plus près les plus grands piratages cryptographiques et les chiffres alléchants qu'ils ont perdus, ils seraient profondément enracinés dans les défauts de codage.

L'attaque de réentrance est l'un de ces cas courants de vulnérabilité de sécurité. Cependant, l'effet destructeur causé par une réentrance mal gérée peut ne pas sembler aussi simple que de lancer l'attaque elle-même.

Bien qu'il s'agisse d'un problème familier et bien médiatisé, l'apparition du bogue de réentrance dans les contrats intelligents est toujours inévitable. 

À quelle fréquence la vulnérabilité Reentrancy a-t-elle été exploitée par des pirates au cours des dernières années ? Comment ça marche? Comment empêcher les contrats intelligents de perdre des fonds à cause des bogues de réentrance ? Trouvez les réponses à ces questions dans ce blog.

Alors, avant longtemps, rafraîchissons-nous les plus grandes attaques de réentrée en mémoire. 

Certains des hacks de réentrance en temps réel les plus infâmes 

Les attaques de réentrance qui ont causé les effets les plus dévastateurs sur les projets ont fini par faire l'un de ces deux ou même les deux. 

  • Égoutter complètement l'Ether des contrats intelligents
  • Les pirates se faufilent dans le code des contrats intelligents

Nous pouvons maintenant observer quelques cas d'attaques de réentrance et leur impact. 

Juin 2016: Attaque DAO – 3.54 millions ou 150 millions de dollars d'éther

Avril 2020: Piratage Uniswap/Lendf.Me – 25 millions de dollars

May 2021: Le piratage BurgerSwap – 7.2 millions de dollars

Aug 2021: Piratage de CREAM FINANCE – 18.8 millions de dollars

Mars 2022: Ola Finance - 3.6 millions de dollars

Juil 2022: Protocole OMNI – 1.43 M$

Il est clair que les attaques de réentrance ne se sont jamais démodées. Approfondissons-le dans les passages suivants. 

Présentation de l'attaque par réentrance

Comme du nom "Réentrance", qui implique "Rentrer encore et encore". L'attaque par réentrance implique deux contrats : le contrat de la victime et le contrat de l'attaquant. 

Le contrat de l'attaquant exploite la vulnérabilité de réentrance dans le contrat de la victime. Il utilise la fonction de retrait pour y parvenir. 

Le contrat de l'attaquant appelle la fonction de retrait pour drainer les fonds du contrat de la victime en effectuant des appels répétés avant que le solde du contrat de la victime ne soit mis à jour. Le contrat de la victime vérifiera le solde, enverra des fonds et mettra à jour le solde. 

Mais dans le délai d'envoi des fonds et de mise à jour du solde du contrat, le contrat de l'attaquant lance un appel continu pour retirer des fonds. Par conséquent, le solde n'est pas mis à jour dans le contrat de la victime tant que le contrat de l'attaquant n'a pas épuisé tous les fonds.

La gravité et le coût de l'exploitation de la réentrance alarment le besoin urgent d'effectuer audits de contrats intelligents pour exclure la possibilité d'ignorer de telles erreurs. 

Vue illustrative de l'attaque de réentrance

Examinons le concept d'attaque par réentrance à partir de l'illustration simplifiée ci-dessous. 

Voici deux contrats : Le contrat vulnérable et le contrat Hacker

Le contrat de pirate fait un appel pour se retirer du contrat vulnérable. À la réception de l'appel, le contrat vulnérable vérifie les fonds dans le contrat du pirate puis transfère les fonds au pirate. 

Le pirate reçoit les fonds et implémente la fonction de secours, qui rappelle le contrat vulnérable avant même que le solde ne soit mis à jour dans le contrat vulnérable. Répétant ainsi la même opération, le pirate retire complètement les fonds du contrat vulnérable. 

Comment échapper aux contrats intelligents de l'embrayage des attaques de réentrance ?

Caractéristiques de la fonction de repli utilisée par l'attaquant 

  • Ils sont appelables de l'extérieur. C'est-à-dire qu'ils ne peuvent pas être appelés à partir du contrat dans lequel ils sont rédigés
  • Fonction sans nom
  • La fonction de repli n'inclut pas de logique arbitraire à l'intérieur
  • Le repli est déclenché lorsque ETH est envoyé à son contrat intelligent englobant et qu'aucune fonction receive() n'est déclarée.

Analyser l'attaque par réentrance d'un point de vue technique 

Prenons un exemple de contrat et comprenons comment l'attaque par réentrance se produit.

Contrat malveillant

contract Attack {
    DepositFunds public depositFunds;

    constructor(address _depositFundsAddress) {
        depositFunds = DepositFunds(_depositFundsAddress);
    }

    // Fallback is called when DepositFunds sends Ether to this contract.
    fallback() external payable {
        if (address(depositFunds).balance >= 1 ether) {
            depositFunds.withdraw();
        }
    }

    function attack() external payable {
        require(msg.value >= 1 ether);
        depositFunds.deposit{value: 1 ether}();
        depositFunds.withdraw();
    }


}

Il s'agit du contrat de l'attaquant dans lequel l'attaquant dépose 2ETH. L'attaquant appelle la fonction de retrait dans le contrat vulnérable. Une fois les fonds reçus du contrat vulnérable, la fonction de secours est déclenchée. 

Le repli exécute ensuite la fonction de retrait et draine les fonds du contrat vulnérable. Ce cycle se poursuit jusqu'à ce que les fonds soient complètement épuisés du contrat vulnérable.

Contrat vulnérable

contract DepositFunds {
    mapping(address => uint) public balances;

    function deposit() public payable {
        balances[msg.sender] += msg.value;
    }

    function withdraw() public {
        uint bal = balances[msg.sender];
        require(bal > 0);

        (bool sent, ) = msg.sender.call{value: bal}("");
        require(sent, "Failed to send Ether");

        balances[msg.sender] = 0;
    }


}

Le contrat vulnérable a 30ETH. Ici, la fonction retrait() envoie le montant demandé à l'attaquant. Comme le solde n'est pas mis à jour, les jetons sont transférés à l'attaquant à plusieurs reprises. 

Types d'attaques de réentrance

  • Réentrance monofonction 
function withdraw() external {
   uint256 amount = balances[msg.sender];
   require(msg.sender.call.value(amount)());
   balances[msg.sender] = 0;
}

Le msg.sender.call.value(amount)() transfère les fonds après quoi la fonction de secours du contrat de l'attaquant appelle à nouveau le retrait() avant que les soldes[msg.sender] = 0 ne soient mis à jour.

  • Réentrance interfonctionnelle
function transfer(address to, uint amount) external {
   if (balances[msg.sender] >= amount) {
       balances[to] += amount;
       balances[msg.sender] -= amount;
   }
}
function withdraw() external {
   uint256 amount = balances[msg.sender];
   require(msg.sender.call.value(amount)());
   balances[msg.sender] = 0;
}

La réentrance interfonctionnelle est beaucoup plus complexe à identifier. La différence ici est que la fonction de repli appelle transfer, contrairement à la réentrance à fonction unique, où elle appelle retrait.

Prévention contre les attaques de réentrance

Motif Contrôles-Effets-Interactions : Le modèle vérifications-effets-interactions aide à structurer les fonctions. 

Le programme doit être codé de manière à vérifier d'abord les conditions. Une fois les vérifications passées, les effets sur l'état des contrats doivent être résolus, après quoi les fonctions externes peuvent être appelées. 

function withdraw() external {
   uint256 amount = balances[msg.sender];
   balances[msg.sender] = 0;
   require(msg.sender.call.value(amount)());
}

Le code réécrit ici suit le modèle vérifications-effets-interactions. Ici, le solde est mis à zéro avant de passer un appel externe. 

Utilisation du modificateur

Le modificateur noReentrant appliqué à la fonction garantit qu'il n'y a pas d'appels réentrants. 

contract ReEntrancyGuard {
    bool internal locked;

    modifier noReentrant() {
        require(!locked, "No re-entrancy");
        locked = true;
        _;
        locked = false;
    }
}

À la fin

L'étape la plus efficace consiste à effectuer des audits de contrats intelligents auprès d'une entreprise de sécurité de premier plan comme QuillAudits, dans laquelle les auditeurs surveillent de près la structure du code et vérifient le fonctionnement de la fonction de secours. Sur la base des modèles étudiés, des recommandations sont données pour restructurer le code s'il semble y avoir comportements vulnérables

La sécurité des fonds est assurée juste avant d'être victime de pertes. 

FAQ

Qu'est-ce qu'une attaque par réentrance ?

Une attaque par réentrance se produit lorsqu'une fonction du contrat vulnérable appelle un contrat non approuvé. Le contrat non approuvé sera le contrat de l'attaquant effectuant des appels récursifs au contrat vulnérable jusqu'à ce que les fonds soient complètement épuisés. 

Qu'est-ce qu'un rentrant ?

L'acte de ré-entrer signifie interrompre l'exécution du code et recommencer le processus, ce qui est également connu sous le nom de ré-entrer.

Qu'est-ce qu'un gardien de réentrance ?

La protection de réentrance utilise un modificateur qui empêche la fonction d'être appelée à plusieurs reprises. Lisez le blog ci-dessus pour trouver l'exemple de la garde de réentrance.

Quelles sont certaines des attaques contre les contrats intelligents ?

Les contrats intelligents sont exposés à de nombreuses vulnérabilités, telles que la réentrance, la dépendance à l'horodatage, les débordements arithmétiques, les attaques DoS, etc. Par conséquent, l'audit est indispensable pour s'assurer qu'il n'y a pas de bogues qui perturbent la logique du contrat.

69 Vues

Horodatage:

Plus de Quillhasch