Mishandling of ETH (Má gestão de ETH)

Refere-se a erros na gestão de transações com ETH, o que pode resultar em perda de fundos, bloqueio de contratos ou comportamentos inesperados. Essas vulnerabilidades geralmente ocorrem devido à implementação incorreta de funções responsáveis por receber, enviar ou transferir ETH.

Exemplo: Considere um contrato que permite aos usuários depositar ETH e, posteriormente, sacar seu saldo a qualquer momento. Um problema comum é o uso das funções transfer() ou send() para enviar ETH, sem levar em conta que essas funções limitam a quantidade de gas disponível para a execução da lógica do contrato destinatário. Isso pode ser explorado se o destinatário for um contrato que consome muito gas, fazendo com que a transação falhe e o ETH não seja transferido, potencialmente resultando no bloqueio permanente dos fundos..

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

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

    function withdraw(uint _amount) external {
        require(balances[msg.sender] >= _amount, "Insufficient balance");
        balances[msg.sender] -= _amount;
        msg.sender.transfer(_amount);  // Vulnerable to failure if the recipient contract consumes too much gas
    }
}

Neste exemplo, se um usuário tentar sacar ETH e seu endereço for um contrato que requer mais gas do que os 2300 gas fornecidos por transfer() ou send(), a transação falhará e os fundos poderão ficar bloqueados permanentemente no contrato.

Mitigação:

  1. Uso de call para Enviar ETH: Em vez de utilizar transfer() ou send(), recomenda-se o uso da função call{value: amount}(""), que oferece maior flexibilidade no gerenciamento de gas. No entanto, call retorna um valor booleano que deve ser verificado para garantir que a transferência foi bem-sucedida.

    function withdraw(uint _amount) external {
        require(balances[msg.sender] >= _amount, "Insufficient balance");
        balances[msg.sender] -= _amount;
        (bool success, ) = msg.sender.call{value: _amount}("");
        require(success, "Transfer failed");
    }
  2. Considerar a Reentrância: Ao utilizar call para enviar ETH, o contrato torna-se vulnerável a ataques de reentrância se não forem implementadas as medidas de segurança adequadas. Para mitigar esse risco, é essencial aplicar o padrão "checks-effects-interactions", garantindo que o estado do contrato seja atualizado antes da chamada externa.

    function withdraw(uint _amount) external {
        require(balances[msg.sender] >= _amount, "Insufficient balance");
        balances[msg.sender] -= _amount; // Actualizar el estado antes de la llamada externa
        (bool success, ) = msg.sender.call{value: _amount}("");
        require(success, "Transfer failed");
    }
  3. Funções Fallback e Recebimento de ETH: É importante implementar corretamente as funções fallback ou receive para lidar com o recebimento de ETH de forma segura no contrato. Recomenda-se evitar incluir muita lógica nessas funções, a fim de reduzir o risco de erros ou explorações.

    receive() external payable {
        balances[msg.sender] += msg.value;
    }
  4. Gestão de Fundos Bloqueados: Implementar uma função de emergência que permita ao proprietário do contrato recuperar fundos bloqueados, caso ocorra algum erro durante as transferências. Essa funcionalidade garante que os fundos não fiquem permanentemente inacessíveis, preservando a segurança e a recuperabilidade dos ativos.

    function emergencyWithdraw() external onlyOwner {
        payable(owner).transfer(address(this).balance);
    }

Last updated