SharkTeam:十大智能合約安全威脅之函數惡意初始化

語言: CN / TW / HK

問:我們常提到的智能合約漏洞真的是實際中威脅最大、發生最頻繁的安全漏洞嗎?

答:完全不是那樣。例如“溢出”、“外部調用”等常提到的智能合約安全漏洞並不是最常發生,威脅最大的。

到底哪些安全威脅從發生頻率和危害性上能稱為Top10的呢?SharkTeam合約安全系列課程之【十大 智能合約 安全威脅】和您一起討論和深入。第六課【詳解函數惡意初始化】。

一、什麼是初始化函數

初始化在計算機編程領域中指為數據對象或變量賦初值的做法,如何初始化則取決於所用的程序語言以及所要初始化的對象的存儲類型等屬性,用於進行初始化的程序結構則稱為初始化器或初始化列表。

初始化函數的意思是,當你創建一個實例的時候,這個函數就會被調用。我們在部署合約後進行初始化合約時,一般不會採用傳遞構造函數參數進行初始化合約。

為什麼不會採用構造函數方式constructor(uint256 _a)進行初始化,卻採用上面所展示的方式進行初始化呢?

(1)不需要處理複雜的構造函數參數,可以更容易被Etherscan驗證

(2)當正常工作流需要參數時,部署和配置不需要同時進行,這更加靈活點

(3)允許我們的智能合約升級

(4)可用於避免在傳遞超過 16 個變量時出現“Stack too deep, try removing local variables”錯誤

但是,採用這種方式進行初始化會有一些問題:

(1)權限未嚴格鑑別:未嚴格設置初始化函數調用者的權限,攻擊者會自由利用權限漏洞進行惡意初始化。

(2)多次調用自定義初始化函數:自定義了不安全的初始化函數,沒有對調用次數進行限制,攻擊者利用該漏洞重複調用初始化函數,導致攻擊者可以輕易的重新初始化資金池。

二、攻擊事件分析

2.1 DODO

2021年3月9日消息,DODO的wCRES/USDT資金池遭受攻擊,攻擊者轉走了價值約90萬美元的wCRES和價值約113萬美元的USDT。DODO是一個近期很熱門的DeFi項目。攻擊者利用資金池合約的初始化函數漏洞,從資金池盜取了133,548.93858472505wCRES和1,139,456.204397USDT。DODO官方發推表示,此次攻擊只和DODO V2眾籌池有關,作為預防措施,已經關閉DODO資金池創建入口。本次事件攻擊流程如下:

(1)攻擊者將FDO和FUSDT兩種空氣幣轉入wCRES/USDT資金池

(2)通過DODO閃電貸借出133548.93858472505個wCRES和1139456.204397個USDT

本次攻擊的執行trace如下圖所示,上面紅框表示攻擊者向資金池轉入FDO和FUSDT的過程,下面的紅框表示攻擊者向資金池借貸的過程。

可以看到攻擊者在調用flashLoan函數的過程中執行了init初始化函數,並且輸入的baseTokenAddress參數為FDO, quoteTokenAddress參數為FUSDT。通過分析源碼中的init函數可知,攻擊者利用該函數將資金池重新進行了初始化,此時資金池被重新初始化成了FDO/FUSDT池。因此,攻擊者可以規避閃電貸的貸款歸還機制。

問題分析:DODO資金池的初始化函數init沒有對調用者的權限進行嚴格的校驗,也沒有做一些防止重複調用的措施,這才導致攻擊者可以輕易的重新初始化資金池。

2.2 Punk Protocol

2021年8月10日,去中心化年金協議 Punk Protocol遭到攻擊,損失 890 萬美元,後來團隊又找回了 495 萬美元(向黑客2提供了 100 萬美元作為漏洞發現費,黑客2歸還495萬美金)。

在這次攻擊事件中,黑客1有兩筆攻擊交易,一個是試探攻擊,一個是正式攻擊。黑客2也有兩筆攻擊交易,不過其中一筆是inputdata帶有通知項目方的信息。

(1)黑客1試探攻擊:0x7604c7dd6e9bcdba8bac277f1f8e7c1e4c6bb57afd4ddf6a16f629e8495a0281

(2)黑客1正式攻擊:0xa76cd31bcd48869621e7f2698ac8754699026acd0655a6d33280224dabed4cfa

(3)黑客2攻擊:0x597d11c05563611cb4ad4ed4c57ca53bbe3b7d3fefc37d1ef0724ad58904742b

(4)黑客2通知項目發攻擊:0x4c8072a57869a908688795356777270a77f56ae47d8f1d869be0d25e807e03b1

接下來,以黑客1正式攻擊交易為例,展開詳細分析:

(1)首先,黑客的攻擊執行了delegateCall,將攻擊者的合約地址寫入到compoundModel中initialize函的forge_參數。setForge(address) 函數在初始化函數中執行。這是一個修改Forge地址的功能

(2)其次,它執行withdrawToForge函數並將所有資金髮送到攻擊者的合約,然後在調用initialize函數發現forge參數已經被替換成攻擊者合約的地址。鏈接到forge的所有CompoundModel都使用相同的代碼,因此所有資產都轉移到攻擊者的合約中。目前,導致黑客入侵的代碼已被項目方修補。添加了兩個Modifiers(僅Creator,initializer),這樣只有Contract Creator可以調用Initialize函數並控制它只被調用一次。

問題分析:本次攻擊的根本原因在於 CompoundModel 合約中缺少對初始化函數的安全控制,可以被重複初始化。

2.3 Nomad

2022年8月2日,跨鏈協議 Nomad 遭受到黑客攻擊,損失超過1.9億美元。該事件有多筆交易,我們選擇其中一筆交易進行分析。交易hash:0xb1fe26cc8892f58eb468f5208baaf38bac422b5752cca0b9c8a871855d63ae28

交易調用了process函數:

進一步分析後發現,在調用acceptableRoot函數時,輸入的參數為0,即messages中並沒有對應的消息,結果返回ture。

confirmAt本身的表示root被確認的時間,當root為0時,實際是沒有被確認過的,confirmAt的值應該也為0,最後會返回false。正是這一點小失誤造成了巨大的經濟損失,對整個項目造成了近乎毀滅性的打擊。

從合約中可以看出,合約的初始化函數initialize會將confirmAt的值初始化為1,初始化函數需要在合約部署後即刻調用,一般由合約的部署地址即owner賬户來調用。我們查看了owner賬户以及其所有的交易,發現是在初始化時將confirmAt[0]設置成了1。

(1)owner: 0xa5bd5c661f373256c0ccfbc628fd52de74f9bb55

(2)txHash: 0x53fd92771d2084a9bf39a6477015ef53b7f116c79d98a21be723d06d79024cad

該合約是升級後的合約,初始化函數是在合約升級後對合約進行初始化。對比於升級前的實現合約(0x7f58bb8311db968ab110889f2dfa04ab7e8e831b),初始化函數相同,但process函數是不同的,升級前的process函數如下:

問題分析:本次安全事件的根本原因是可升級合約在合約升級的過程中存在失誤,容易被攻擊者利用初始化函數漏洞,進行惡意攻擊。

三、預防措施

我們應該採取哪些措施去避免函數惡意初始化?

(1)初始化函數應只能調用一次,而且需要進行調用者權限鑑別

(2)如果合約是使用初始化函數,而不是在構造函數中進行初始化,則應使用安全合約庫中的初始化器來進行初始化。避免合約被惡意操縱,造成合約關鍵參數和邏輯的錯誤

(3)項目上線前,需聯繫專業的第三方專業審計團隊進行審計

關於我們: SharkTeam 的願景是全面保護Web3世界的安全。團隊成員分佈在北京、南京、蘇州、硅谷,由來自世界各地的經驗豐富的安全專業人士和高級研究人員組成,精通區塊鏈和智能合約的底層理論,提供包括智能合約審計、鏈上分析、應急響應等服務。已與區塊鏈生態系統各個領域的關鍵參與者,如OKC、polygon、Huobi Global、Polkadot、imToken、ChainIDE等建立長期合作關係。

Telegram:http://t.me/sharkteamorg

Twitter:http://twitter.com/sharkteamorg

更多區塊鏈安全諮詢與分析,點擊下方鏈接查看

D查查|鏈上風險核查 http://m.chainaegis.com