區塊鏈合約安全系列(二):如何認識及預防公鏈合約中的重入攻擊漏洞

BSN研習社發表於2022-05-25

id:BSN_2021

公眾號:BSN研習社

背景:由於公鏈環境下所有的資訊都是共享的,智慧合約相當於是完全透明化,任何人都可以呼叫,外加一些利益的驅動,導致引發了很多hacker的攻擊。其中重入 (Re-Entrance) 攻擊是以太坊中的攻擊方式之一,例如The DAO 事件被盜取了大量的以太幣從而造成了以太坊的硬分叉。

目標:將目標合約裡的所有資金(ether)進行盜取,從而認識以及預防重入攻擊漏洞。

物件:適用於用Solidity語言開發的智慧合約,例如BSN中的武漢鏈(基於ETH)和泰安鏈(基於 fisco bcos)上執行的智慧合約。

前言

在進入今天的正題之前,我們先來看一段典型的有重入漏洞的程式碼:

上述方法的業務也很簡單,提取呼叫者存入的錢,成功後將其餘額設為零。


不知道大家腦海裡有沒有發出這樣的疑問:這怎麼就存在重入漏洞了呢?


如果你存在這樣的疑惑,那麼請由我來講解一下其中的知識。在開始之前需要大家清楚幾個知識點:


1.重入定義
什麼是重入?官方的解釋如下:

為方便記憶,暫且可以簡單的理解為我們開發者傳統印象中的遞迴。


2.特殊函式
這裡的特殊函式是指在合約互動傳送ether轉賬時,會隱式觸發呼叫的函式,包含receive和fallback。官方的解釋如下:

通俗的來說就是當你的合約接收ether時,必須至少包含這倆函式中的一個。


3.轉賬操作

什麼是轉賬???對,沒錯,就是你腦子裡想的那樣。在這裡是指在兩個以太坊賬戶之間的傳送和接收ether的操作。但需要知道的一點是以太坊上的賬戶分為兩種,即普通賬戶和合約賬戶。

在以太坊上轉賬操作常見的函式包括三種.transfer()、.send()和.call{value:xxx}("")

在各位瞭解了基本的知識後,下面我們開始進入今天的正題:我將通過一個示例來進行演示,通過重入漏洞,盜取一個“銀行”裡的 全部資金。


示例涉及到兩個合約,一個為資金管理合約,一個為攻擊者合約。其完整程式碼如下:

合約部署

為其方便演示在此使用remix進行測試環境準備。首先,部署資金管理合約,並初始化存入100Wei(為顯示方便,其金額任意)。結果如下:

然後,部署攻擊者合約,結果如下:

重入攻擊

首先,存入ether。因為資金管理合約的提款邏輯為提取自己已存入的,所以我們需要先存入。結果如下圖:

然後,提取ether,利用重入漏洞將額外的資金進行盜取。交易執行成功後,查詢提取的資金資訊,結果如下圖:

該筆交易的事件日誌資訊如下:

另外,我們檢視一下資金管理合約的餘額資訊,查詢結果如下圖:

解決方案

通過上面的示例,細心的朋友可能已經大概明白,其實重入攻擊漏洞是因為觸發了特殊函式(receive或者fallback)造成遞迴呼叫轉賬,目前常用的解決方案有下面幾種:


方案一、  使用安全的轉賬函式
使用內建的 transfer或send 函式,因為transfer和send在轉賬時會傳遞2300Gas,不足以執行重入調回合約操作,而.call會傳遞所有可用的Gas(當然也可以設定傳遞可用的Gas)。


方案二、 使用官方推薦的檢查-生效-互動模式 (checks-effects-interactions)

方案三、 使用專門針對重入攻擊的安全合約

例如OpenZeppelin 官方庫中的安全合約ReentrancyGuard.sol

今天的講解到此結束,感謝大家的閱讀,如果你有其他的方案,歡迎一塊交流。

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/70012206/viewspace-2896961/,如需轉載,請註明出處,否則將追究法律責任。

相關文章