重入漏洞相信大家都有所耳聞,那麼什麼是重入漏洞呢?

眾所周知,以太坊的轉賬不僅可以在錢包地址之間進行,合約與錢包地址之間、合約與合約之間也可以,而合約在接收到轉賬的時候會觸發fallback 函數執行相應的邏輯,這是一種隱藏的外部調用。攻擊者就會利用這一點,在合約的fallback 函數中寫入惡意邏輯重新進入到被攻擊的合約內部,讓被攻擊的合約執行非預期的外部調用,從而到達獲取不正當利益的目的。

漏洞示例

下面我們來看一個比較典型的有重入漏洞的代碼:

智能合約安全——重入漏洞

漏洞分析

上面的代碼就是個普通的充提幣的合約,那麼怎麼對其發起重入攻擊呢?我們來看這個合約的withdraw 函數,這個函數中的轉賬操作有一個外部調用“msg.sender.call{value: bal}”,所以我們就可以認為這個合約是可能有重入漏洞的,讓我們來進一步分析看看:

在withdraw 函數中是先執行外部調用進行轉賬後才將賬戶餘額清零的,那麼就可以在轉賬外部調用的時候構造一個惡意的邏輯合約在合約執行balance[msg.sender]=0之前一直循環調用withdraw 函數一直提幣從而將合約賬戶清空。

攻擊合約

下面我們看看攻擊者編寫的攻擊合約中的攻擊手法是否與我們的漏洞分析相同:

智能合約安全——重入漏洞

我們看到EtherStore 合約是一個充提合約,我們可以在其中充值和提現。

攻擊者先調用合約中的攻擊函數先向EtherStore中充值1 ether,在EtherStore中他的賬戶餘額就為1ether,那麼他就可以提現這些餘額。緊接著,withdraw 函數發起提現,當EtherStore向攻擊合約轉賬完成時,攻擊合約就會調用fallback函數,再次請求提現餘額,如此循環就能將EtherStore中的餘額提現到不足1ether,才結束這個循環。

攻擊函數調用流程圖:

智能合約安全——重入漏洞

修復建議

看了上面的攻擊手法相信大家對重入漏洞都會有一個自己的認知了,但是我們的應該怎麼避免重入漏洞防禦重入攻擊呢?以下是我給大家的建議:

1. 寫代碼時需要遵循先判斷,後寫入變量在進行外部調用的編碼規範(Checks-Effects-Interactions)。

2. 加入防重入鎖。

下面是一個防重入鎖的代碼示例:

智能合約安全——重入漏洞

3. 記住所有涉及到外部合約調用的代碼位置都是不安全的。

那麼智能合約中的重入攻擊就講解完了,如果想了解更多的區塊鏈知識,或是有什麼疑問,可以到區塊鏈交流社區CHAINPIP來,一起學習和交流。

社區地址:https://www.chainpip.com/