My Avatar

毕竟话少

业精于勤荒于嬉,行成于思毁于随!

智能合约审计-拒绝服务

2020年3月22日 星期日, 发表于 合肥

如果你对本文有任何的建议或者疑问, 可以在 这里给我提 Issues, 谢谢! :)

描述:拒绝服务漏洞(DOS)智能合约无法按照设定的方式被调用

核心问题:智能合约中的拒绝服务是一个致命的漏洞,因为漏洞导致的拒绝服务一般为永久性的,无法恢复

拒绝服务的原因

漏洞分析

selfdestruct()合约自毁函数

selfdestructGame合约分析

pragma solidity ^0.4.24;

contract selfdestructGame{
    address owner;

    constructor() payable {
        owner = msg.sender;
    }
    
    function ownedEth() public constant returns(uint256){
        return this.balance;
    }
    

    function destruct(address _who) public {
        selfdestruct(_who);
    }
}

漏洞点:在address owner处发送地址到_who处,由于调用了selfdestruct()函数对selfdestructGame合约进行自毁,将selfdestructGame合约balance发送到攻击者地址处。

访问控制策略错误

onwer权限变更不需要确认,因此若owner被设置为一个错误地址,合约将彻底失去管理员权限

onwerGame合约分析

pragma solidity ^0.4.24;

contract ownerGame{
    address owner;

    constructor() {
        owner = msg.sender;
    }

    function changeOwner(address _new) public {
        owner = _new;
    }
}

漏洞点:由于owner没有权限控制,导致可以任意调用成为管理员,如果owner地址为无效地址,就会导致合约作废

Gas达到区块上限

对于每一个区块来说,有一个区块的GasLimit,任何交易的gas花费都不超过这个上限,否则无法被打包

GaslimitGame合约分析

pragma solidity ^0.4.24;

contract gaslimitGame{
    mapping (uint => uint) count;
    
    event GasLog(uint gas);

    function gasUse(uint n) public {
        uint gastmp = gasleft();
        for(uint i=0;i<n;i++)
        {
            count[i]=i;
            emit GasLog(gastmp-gasleft());
            gastmp = gasleft();
        }
    }
}

非预期的异常导致拒绝服务

在竞拍合约中,竞拍进行时,每个人向合约发送自己的竞拍出价对应ETH

如果有人出价高于当前最高出价者,则合约退还当前最高出价者的出价,并且此人成为新的最高出价者

攻击者构造一个fallback函数一定会抛出异常的合约,并将合约地址成为最高出价者,则其他人将永远不能成为新的最高出价者

Auction合约分析

pragma solidity ^0.4.24;

contract Auction {
    address public topBidder;
    uint256 public topBid;

    constructor() public payable {
        require(msg.value > 0);      
        topBid = msg.value;
        topBidder = msg.sender;
    }
    
    function bid() payable {
        require(msg.value > topBid);
        topBidder.transfer(topBid);

        topBidder = msg.sender;
        topBid = msg.value;

    }

    // finish the auction
    function auction_end() {
        // ...
    }
    
}

contract revertContract{
    
    function testBid(address _addr) public payable{
        bytes4 methodHash = bytes4(keccak256("bid()"));
        _addr.call.value(msg.value)(methodHash);
    }
    
    function ownedEth() public constant returns(uint256){
        return this.balance;
    }
    
    function() public payable{
        revert();
    }
}

漏洞点:topBidder的攻击者合约在竞拍成功后的上一个最高价格时候,当后面的竞拍者出更高价格的时候,会触发topBidder.transfer(topBid),然而攻击者合约内使用了fallback函数,在经过transfer函数的时候会发生异常,导致交易回滚,从而使bid()函数拒绝服务。使整个合约没办法正常进行。

漏洞预防

  1. 严格限制selfdestruct指令的权限限制
  2. 设置完善合理的访问控制策略
  3. 如果目标地址可以是一个合约,需要考虑合约的特性