重入漏洞相信大家都有所耳聞,那么什么是重入漏洞呢?
眾所周知,以太坊的轉(zhuǎn)賬不僅可以在錢包地址之間進行,合約與錢包地址之間、合約與合約之間也可以,而合約在接收到轉(zhuǎn)賬的時候會觸發(fā)fallback 函數(shù)執(zhí)行相應(yīng)的邏輯,這是一種隱藏的外部調(diào)用。攻擊者就會利用這一點,在合約的fallback 函數(shù)中寫入惡意邏輯重新進入到被攻擊的合約內(nèi)部,讓被攻擊的合約執(zhí)行非預(yù)期的外部調(diào)用,從而到達(dá)獲取不正當(dāng)利益的目的。
漏洞示例
下面我們來看一個比較典型的有重入漏洞的代碼:

漏洞分析
上面的代碼就是個普通的充提幣的合約,那么怎么對其發(fā)起重入攻擊呢?我們來看這個合約的withdraw 函數(shù),這個函數(shù)中的轉(zhuǎn)賬操作有一個外部調(diào)用“msg.sender.call{value: bal}”,所以我們就可以認(rèn)為這個合約是可能有重入漏洞的,讓我們來進一步分析看看:
在withdraw 函數(shù)中是先執(zhí)行外部調(diào)用進行轉(zhuǎn)賬后才將賬戶余額清零的,那么就可以在轉(zhuǎn)賬外部調(diào)用的時候構(gòu)造一個惡意的邏輯合約在合約執(zhí)行balance[msg.sender]=0之前一直循環(huán)調(diào)用 withdraw 函數(shù)一直提幣從而將合約賬戶清空。
攻擊合約
下面我們看看攻擊者編寫的攻擊合約中的攻擊手法是否與我們的漏洞分析相同:

我們看到EtherStore 合約是一個充提合約,我們可以在其中充值和提現(xiàn)。
攻擊者先調(diào)用合約中的攻擊函數(shù)先向EtherStore中充值1ether,在EtherStore中他的賬戶余額就為1ether,那么他就可以提現(xiàn)這些余額。緊接著,withdraw 函數(shù)發(fā)起提現(xiàn),當(dāng)EtherStore向攻擊合約轉(zhuǎn)賬完成時,攻擊合約就會調(diào)用fallback函數(shù),再次請求提現(xiàn)余額,如此循環(huán)就能將EtherStore中的余額提現(xiàn)到不足1ether,才結(jié)束這個循環(huán)。
攻擊函數(shù)調(diào)用流程圖:

修復(fù)建議
看了上面的攻擊手法相信大家對重入漏洞都會有一個自己的認(rèn)知了,但是我們的應(yīng)該怎么避免重入漏洞防御重入攻擊呢?以下是我給大家的建議:
1. 寫代碼時需要遵循先判斷,后寫入變量在進行外部調(diào)用的編碼規(guī)范(Checks-Effects-Interactions)。
2. 加入防重入鎖。
下面是一個防重入鎖的代碼示例:

3.記住所有涉及到外部合約調(diào)用的代碼位置都是不安全的。
那么智能合約中的重入攻擊就講解完了,如果想了解更多的區(qū)塊鏈知識,或是有什么疑問,可以到區(qū)塊鏈交流社區(qū)CHAINPIP來,一起學(xué)習(xí)和交流。
社區(qū)地址:https://www.chainpip.com/