Analise smart-contract
27.03.2024

Pièges cachés dans Honeypot

Les contrats intelligents Honeypot sont un type de contrats intelligents frauduleux qui semblent attrayants pour les utilisateurs et promettent des avantages financiers. Cependant, ces contrats contiennent des pièges cachés ou des codes malveillants, ce qui fait que les utilisateurs perdent au contraire leurs fonds.

Pour vous protéger des contrats intelligents Honeypot, vous devez faire des recherches approfondies et vérifier le code du contrat avant de vous engager avec lui. Le monde des smart contracts et de la blockchain est en constante évolution, de nouvelles vulnérabilités et méthodes de tromperie apparaissant régulièrement, il est donc essentiel pour les développeurs, les auditeurs et les utilisateurs de se tenir au courant des dernières tendances et évolutions dans ce domaine.

Il existe de nombreux exemples de smart contracts Honeypot. Dans cet article, nous allons en examiner quelques-uns.

1. Honeypot à fonction de repli

Dans ce type de honeypot, le contrat dispose d'une fonction vulnérable de "rollback" qui permet apparemment à n'importe qui de posséder le contrat et de supprimer son solde. Cependant, une telle fonction contient une condition cachée qui empêche une exécution réussie.

Exemple de code:


pragma solidity 0.4.18

contract FallbackHoneypot {
  address public owner;

  function() public payable {
      if (owner == 0) {
          owner = msg.sender;
      } else {
          revert();
      }
  }

  function withdraw() public {
      require(msg.sender == owner);
      msg.sender.transfer(this.balance);
  }
}

Dans cet exemple, la fonction de repli permet à n'importe quel utilisateur de devenir propriétaire. Cependant, l'opérateur revert() garantit que le changement de propriétaire ne se produira jamais, laissant les fonds de l'attaquant piégés dans le contrat.

2. Pot de miel à condition cachée

De tels contrats contiennent une condition cachée ou une exigence qui doit être remplie pour que la fonction apparemment vulnérable réussisse. Les utilisateurs peuvent penser qu'ils peuvent utiliser le contrat, mais la condition cachée garantit qu'ils ne le peuvent pas.

Exemple de code:


pragma solidity 0.4.18

contract HiddenConditionHoneypot {
  uint256 public counter = 0;

  function deposit() public payable {
      require(msg.value == 1 ether);
      counter++;
  }

  function withdraw() public {
      require(counter > 100);
      msg.sender.transfer(this.balance);
  }
}

Dans ce contrat, l'utilisateur peut déposer 1 éther, et il semblerait qu'après 100 dépôts, l'utilisateur puisse retirer la totalité du solde du contrat. Cependant, la condition counter > ; 100 garantit que le retrait n'est possible qu'après 101 dépôts, ce qui retarde les fonds dans le contrat.

3. pot de miel de réentrance

Dans ce type de pot de miel, le contrat est vulnérable à une attaque de réentrance, où un attaquant peut invoquer de manière répétée une fonction du contrat pendant le retrait. Cependant, le contrat contient un mécanisme caché qui empêche une exploitation réussie.

Exemple de code:


pragma solidity 0.4.18

contract ReentrancyHoneypot {
  mapping(address => uint256) public balances;

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

  function withdraw(uint256 _amount) public {
      require(balances[msg.sender] >= _amount);
      if (msg.sender.call.value(_amount)()) {
          balances[msg.sender] -= _amount;
      }
  }
}

Dans cet exemple, le contrat est vulnérable à l'attaque par réentrance à cause de l'utilisation de msg.sender.call.value(_amount)(). Cependant, le contrat ne permet pas aux utilisateurs de déposer plus que le solde initial, ce qui retarde effectivement leurs fonds.

reentrancy honeypot

4. Pot de miel à limite de gaz

Dans ce type de pot de miel, le contrat semble permettre à l'utilisateur de retirer des fonds, mais la quantité de gaz nécessaire pour exécuter la fonction de retrait dépasse la limite du bloc de gaz, ce qui rend la transaction impossible.

Exemple de code:


pragma solidity 0.4.18

contract GasLimitHoneypot {
  uint256 constant public numArrayElements = 1000;
  uint256[numArrayElements] public someArray;

  function deposit() public payable {
      for (uint256 i = 0; i < numArrayElements; i++) {
          someArray[i] = 0;
      }
  }

  function withdraw() public {
      uint256 balance = address(this).balance;
      for (uint256 i = 0; i < numArrayElements; i++) {
          someArray[i] = 1;
      }
      msg.sender.transfer(balance);
  }
}

Dans cet exemple, la fonction deposit initialise un tableau de 1000 éléments. La fonction withdraw transfère le solde du contrat à l'expéditeur, mais modifie les éléments du tableau. Le gaz nécessaire à l'exécution de la boucle dépasse la limite du bloc de gaz, ce qui empêche le retrait et le retarde dans le contrat.

5. Pot de miel de manipulation de l'horodatage

Dans ce type de pot de miel, le contrat utilise l'horodatage de la blockchain Ethereum comme condition d'exécution. Cependant, comme les mineurs peuvent manipuler l'horodatage du bloc, le contrat devient vulnérable.

Exemple de code:


pragma solidity 0.4.18

contract TimestampHoneypot {
  uint256 public lastInteraction;
  uint256 public prize;

  function deposit() public payable {
      lastInteraction = now;
      prize += msg.value;
  }

  function withdraw() public {
      require(now >= lastInteraction + 1 hours);
      msg.sender.transfer(prize);
      prize = 0;
  }
}

Dans ce contrat, les utilisateurs peuvent déposer des fonds et retirer la totalité du solde du contrat 1 heure après la dernière interaction. Cependant, comme les mineurs peuvent manipuler l'horodatage du bloc, ils peuvent déclencher la fonction de retrait plus tôt que prévu et ainsi voler des fonds.

6. Pot de miel de transfert caché

Dans ce type de pot de miel, le contrat contient un transfert caché de fonds vers l'adresse de l'attaquant, ce qui donne l'apparence d'un contrat légitime mais entraîne en réalité une perte de fonds.

Exemple de code:


pragma solidity 0.4.18

contract HiddenTransferHoneypot {
  address public owner;

  constructor() public {
      owner = msg.sender;
  }

  function deposit() public payable {
      if (msg.value >= 1 ether) {
          owner.transfer(msg.value);
      }
  }

  function withdraw() public {
      require(msg.sender == owner);
      owner.transfer(address(this).balance);
  }
}

Dans cet exemple, le contrat semble autoriser les dépôts et les retraits. Cependant, la fonction de dépôt contient un transfert caché de fonds vers le propriétaire (l'attaquant) si le montant du dépôt est égal ou supérieur à 1 éther. Les utilisateurs peu méfiants qui déposent des fonds perdront leurs éthers, car ils seront transférés à l'adresse de l'attaquant.

7. Pot de miel de réentrance

Le pot de miel de réentrance est basé sur l'exploitation de la vulnérabilité de réentrance dans le contrat, où une fonction de contrat peut être appelée de manière récursive avant que son état ne soit mis à jour.

Exemple de code:


pragma solidity 0.4.18

contract ReentrancyHoneypot {
  mapping(address => uint256) public balances;

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

  function withdraw() public {
      uint256 amount = balances[msg.sender];
      (bool success,) = msg.sender.call.value(amount)("");
      require(success, "Withdrawal failed.");
      balances[msg.sender] = 0;
  }
}

Dans cet exemple, le contrat autorise les dépôts et les retraits. Cependant, il est vulnérable aux attaques par réentrance. Un attaquant pourrait créer un contrat malveillant qui appelle récursivement la fonction de retrait avant que le solde ne soit mis à zéro, épuisant ainsi tout le solde du contrat.

8. Pot de miel de conversion de type et de débordement

Dans ce type de pot de miel, l'attaquant utilise la conversion de type et le débordement d'entier pour faire croire aux utilisateurs que le contrat est sûr, alors qu'il contient en réalité des pièges cachés.

Exemple de code:


pragma solidity 0.4.18

contract TypeCastingHoneypot {
  uint8 public count = 0;
  uint256 public reward = 1 ether;

  function increment() public payable {
      require(msg.value >= reward);

      uint8 prevCount = count;
      count++;

      if (count < prevCount) {
          msg.sender.transfer(address(this).balance);
      }
  }
}

Dans cet exemple, le contrat récompense les utilisateurs qui incrémentent la variable count lorsqu'ils envoient un montant égal ou supérieur au montant de la récompense. Cependant, en raison de l'utilisation de uint8 pour la variable count, un dépassement d'entier se produit lorsque la valeur atteint 255. Après le dépassement, le compte est réinitialisé à 0 et un attaquant peut obtenir le solde total du contrat.

overflow honeypot

9. Pot de miel Delegatecall

Dans ce type de pot de miel, l'attaquant utilise la fonction delegatecall pour exécuter un code malveillant au nom d'un contrat apparemment sécurisé.

Exemple de code:


pragma solidity 0.4.18

contract DelegateCallHoneypot {
  address public owner;

  constructor() public {
      owner = msg.sender;
  }

  function() external payable {}

  function withdraw() public {
      require(msg.sender == owner);
      owner.transfer(address(this).balance);
  }

  function execute(address _target, bytes memory _data) public {
      require(msg.sender == owner);
      (bool success,) = _target.delegatecall(_data);
      require(success, "Execution failed.");
  }
}

Dans cet exemple, le contrat autorise les dépôts, les retraits et l'exécution de code via la fonction execute. Toutefois, un pirate peut créer un code malveillant capable de manipuler l'état du contrat, y compris la variable owner, en exécutant la fonction delegatecall. En changeant le propriétaire, un attaquant peut prendre le contrôle du contrat et vider son solde.

10. Pot de miel de manipulation de stockage caché

Dans ce type de pot de miel, l'attaquant manipule secrètement les variables de stockage pour inciter les utilisateurs à prendre le contrôle du contrat.

Exemple de code:


pragma solidity 0.4.18

contract HiddenStorageManipulation {
  mapping(address => uint256) public balances;
  uint256 public totalSupply;
  address public owner;

  constructor() public {
      owner = msg.sender;
      balances[owner] = 1000;
      totalSupply = 1000;
  }

  function transfer(address _to, uint256 _value) public {
      require(balances[msg.sender] >= _value);
      balances[msg.sender] -= _value;
      balances[_to] += _value;

      if (totalSupply - balances[owner] >= 1000) {
          owner = _to;
      }
  }
}

Dans cet exemple, le contrat ressemble à un simple contrat de jetons. Les utilisateurs peuvent se transmettre des jetons et le propriétaire est initialement défini comme le créateur du contrat. Cependant, un attaquant peut manipuler secrètement les variables totalSupply et balances pour reprendre le contrôle du contrat. Lorsque la différence entre totalSupply et balances[owner] atteint 1000, la variable owner est mise à jour, ce qui permet à l'attaquant de reprendre le contrôle du contrat.

11. Pot de miel à collision de noms de fonctions

Dans ce type de pot de miel, l'attaquant utilise des collisions de noms de fonctions pour inciter les utilisateurs à appeler la mauvaise fonction.

Exemple de code:


pragma solidity 0.4.18

contract FunctionNameCollision {
  address public owner;

  constructor() public {
      owner = msg.sender;
  }

  function withdraw() external {
      require(msg.sender == owner);
      msg.sender.transfer(address(this).balance);
  }

  function withdraw(uint256 amount) external {
      require(msg.sender == owner);
      owner.transfer(amount);
  }

  function() external payable {}
}

Dans cet exemple, le contrat comporte deux fonctions de retrait. L'une d'entre elles permet au propriétaire de retirer la totalité du solde et l'autre lui permet de retirer un certain montant. Cependant, si l'utilisateur tente de retirer un certain montant en appelant withdraw(uint256), en raison d'une collision de noms de fonctions, l'utilisateur appellera par inadvertance withdraw() sans arguments, ce qui entraînera le retrait de la totalité du solde du contrat.

12. Pot de miel de la vulnérabilité Delegatecall

Dans ce type de pot de miel, l'attaquant utilise delegatecall pour exécuter une fonction malveillante dans un autre contrat, ce qui peut conduire au vol des fonds des utilisateurs.

Exemple de code:


pragma solidity 0.4.18

contract DelegatecallVulnerability {
  address public owner;

  constructor() public {
      owner = msg.sender;
  }

  function withdraw() external {
      require(msg.sender == owner);
      msg.sender.transfer(address(this).balance);
  }

  function () external payable {
      if (msg.value > 1 ether) {
          this.delegatecall(msg.data);
      }
  }
}

contract Malicious {
  function withdraw() external {
      msg.sender.transfer(address(this).balance);
  }
}

Dans cet exemple, le contrat semble inoffensif, avec une simple fonction withdraw() qui permet au propriétaire de retirer le solde du contrat. Cependant, le contrat contient également une fonction de repli qui, lorsqu'elle reçoit plus de 1 éther, effectue un appel délégué d'un autre contrat à l'aide de msg.data. Si un attaquant crée un contrat malveillant avec withdraw() qui vole le solde du contrat et envoie plus de 1 éther au contrat victime, le delegatecall exécutera la fonction malveillante withdraw(), volant le solde du contrat.

vulnerability honeypot

13. Pseudo-integers overflow honeypot

Ce type de pot de miel est basé sur le fait que Solidity n'a pas de support intégré pour les nombres à virgule flottante. Les attaquants peuvent créer des contrats qui utilisent apparemment des nombres décimaux mais qui manipulent en réalité des nombres entiers de manière frauduleuse.

Exemple de code:


pragma solidity 0.4.18

contract PseudoIntegersOverflow {
  mapping(address => uint256) public balances;

  function deposit() external payable {
      require(msg.value > 0);
      uint256 pseudoValue = msg.value * 100;
      balances[msg.sender] += pseudoValue;
  }

  function withdraw(uint256 amount) external {
      uint256 pseudoAmount = amount * 100;
      require(balances[msg.sender] >= pseudoAmount);
      balances[msg.sender] -= pseudoAmount;
      msg.sender.transfer(amount);
  }
}

Cet exemple donne l'impression que le contrat gère le solde de l'utilisateur avec une précision décimale en multipliant les valeurs par 100 avant de les enregistrer. Cependant, cette approche est trompeuse car Solidity ne supporte pas les nombres à virgule flottante. Lorsque l'utilisateur tente de retirer ses fonds, le contrat calcule incorrectement la pseudo-somme en raison de la troncature des nombres entiers, et l'utilisateur peut ne pas être en mesure de retirer la totalité de son solde.

Pour vous protéger contre ce type de pot de miel, vous devez comprendre les limites du langage Solidity et la façon dont il gère les opérations numériques. Soyez prudent lorsque vous travaillez avec des contrats qui prétendent prendre en charge la précision décimale, et vérifiez toujours le code du contrat pour détecter les problèmes potentiels.

14. Frais cachés dans les contrats intelligents

Certains contrats intelligents malveillants peuvent imposer des paiements cachés aux utilisateurs à leur insu. Ces paiements peuvent être déguisés dans le code du contrat ou ne se déclencher que sous certaines conditions.

Exemple de code:


pragma solidity 0.4.18

contract HiddenFees {
  address public owner;
  uint256 public feePercentage;

  constructor(uint256 _feePercentage) public {
      owner = msg.sender;
      feePercentage = _feePercentage;
  }

  function withdraw(uint256 amount) external {
      uint256 fee = amount * feePercentage / 100;
      uint256 netAmount = amount - fee;
      require(address(this).balance >= netAmount);
      msg.sender.transfer(netAmount);
      owner.transfer(fee);
  }
}

Dans cet exemple, le créateur du contrat définit une commission (feePercentage) lors du déploiement du contrat. Lorsqu'ils interagissent avec le contrat, les utilisateurs peuvent ne pas être au courant de ces frais. Lorsqu'un utilisateur retire ses fonds, le contrat calcule la commission, la soustrait du montant du retrait et envoie le reste à l'utilisateur. Pour éviter de tomber dans ce piège, examinez attentivement le code du contrat et assurez-vous de bien comprendre tous les frais associés. Recherchez les règlements cachés ou déguisés.

15. Manipulation de l'état caché

Dans certains cas, les contrats malveillants peuvent permettre au propriétaire du contrat ou à un attaquant de manipuler l'état interne du contrat, ce qui entraîne des conséquences inattendues pour les autres utilisateurs.

Exemple de code:


pragma solidity 0.4.18

contract HiddenStateManipulation {
  address public owner;
  mapping(address => uint256) public balances;

  constructor() public {
      owner = msg.sender;
  }

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

  function withdraw(uint256 amount) external {
      require(balances[msg.sender] >= amount);
      msg.sender.transfer(amount);
      balances[msg.sender] -= amount;
  }

  function manipulateBalance(address user, uint256 newBalance) external {
      require(msg.sender == owner);
      balances[user] = newBalance;
  }
}

Dans cet exemple, le contrat permet aux utilisateurs de déposer et de retirer des fonds. Toutefois, la fonction manipulateBalance permet au propriétaire du contrat de modifier arbitrairement le solde de n'importe quel utilisateur. Cela peut entraîner des pertes inattendues pour les utilisateurs ou permettre au propriétaire du contrat de voler des fonds.

Le code du contrat doit être examiné de près pour détecter les fonctions qui permettent la manipulation de l'état, en particulier si seul le propriétaire du contrat ou certaines adresses y ont accès.

hidden state manipulation

16. Vol de jetons caché

Dans certains cas, les contrats malveillants peuvent contenir des caractéristiques cachées qui permettent au propriétaire du contrat ou à l'attaquant de voler des jetons à des utilisateurs qui ne se doutent de rien.

Exemple de code:


pragma solidity 0.4.18

contract HiddenTokenStealing {
  address public owner;
  mapping(address => uint256) public balances;

  constructor() public {
      owner = msg.sender;
  }

  function deposit(IERC20 token, uint256 amount) external {
      require(token.transferFrom(msg.sender, address(this), amount));
      balances[msg.sender] += amount;
  }

  function withdraw(IERC20 token, uint256 amount) external {
      require(balances[msg.sender] >= amount);
      require(token.transfer(msg.sender, amount));
      balances[msg.sender] -= amount;
  }

  function stealTokens(IERC20 token, address user, uint256 amount) external {
      require(msg.sender == owner);
      require(token.transfer(owner, amount));
      balances[user] -= amount;
  }
}

Dans cet exemple, le contrat permet aux utilisateurs de déposer et de retirer des jetons ERC20. Cependant, la fonction stealTokens permet au propriétaire du contrat de voler des jetons à n'importe quel utilisateur.

Conclusion:

Honeypot n'est qu'un des nombreux schémas frauduleux de DeFi qui utilise non seulement la restriction de retrait, mais aussi les différents pièges cachés décrits ci-dessus.

Pour ne pas tomber dans le panneau, vous devez comprendre les particularités du schéma frauduleux, les subtilités du langage de programmation Solidity et étudier en profondeur les contrats intelligents avant d'interagir avec eux.

En restant informé et en faisant preuve d'une diligence raisonnable approfondie, vous pouvez minimiser les risques associés à l'utilisation des smart contracts.

 

Si vous avez des doutes sur la sécurité ou la fonctionnalité d'un smart contract, utilisez notre plateforme Lotus Market.

 

 

La plateforme Lotus Market a été créée par une équipe de développeurs expérimentés et d'auditeurs professionnels. L'objectif du projet est de minimiser les risques d'investissement dans des jetons frauduleux et d'aider à échanger des cryptocurrencies en toute sécurité et confortablement.

 

Regards, Lotus Market team.

All posts

Connect to a wallet

Metamask