北京時間2020年08月05日,DeFi 期權平臺 Opyn 的看跌期權(Opyn ETH Put)智慧合約遭到駭客攻擊,損失約37萬美元。
Opyn 是一個通用期權協議,於今年2月份轉型為保險平臺,透過 oTokens 為 DeFi 平臺提供可交易的 ETH 看跌期權,以此錨定 ETH 市場價格,為高波動性的 DeFi 市場提供相對的穩定性。
PeckShield 安全團隊獲悉 Opyn 平臺遭受攻擊後,迅速定位到問題關鍵點在於:
攻擊者發現Opyn 智慧合約行權(exercise)介面對接收到的 ETH 存在某些處理缺陷,其合約並沒有對交易者的實時交易額進行檢驗,使得攻擊者可以在一筆對自己發起真實的交易之後,再插入一筆偽裝交易騙得賣方所抵押的數字資產,進而實現空手套白狼。
簡單來說,由於 Opyn ETH Put 智慧合約中的行權函式 exercise() 沒有對交易者的ETH 進行實時校驗。根據 Opyn 平臺的業務邏輯,看跌期權的買方給賣方轉移相應價值的 ETH,即可獲得賣方抵押的數字資產。狡猾的攻擊者,先向自己發起偽裝的交易,利用這筆 ETH 可以重複使用的特性,再次向賣方使用者發起轉賬,進而騙取賣方已經抵押的數字資產。
下面為您詳細分析漏洞原因及攻擊過程。
漏洞詳細過程分析
先來說說,Opyn 平臺的業務邏輯:當使用者使用 Opyn 合約行權即買賣期貨(exercise)時,需要買方向賣方轉入相應數量的 ETH 或者 ERC20 Token,然後合約將銷燬買方對應的 oToken,而後買方將獲得賣方已經抵押的資產。
例如:小王認為行情進入了下跌趨勢,看到 Opyn 上掛著一個小李對 ETH 330美元的看跌期權,於是進入交易系統,向小李轉賬一個 ETH,獲得小李抵押的等額數字資產。若此刻行情已經跌至了300美元,小王便可獲得其中的差價。
圖1. exercise() 函式中迴圈執行傳入的 vaults 地址列表
如上面的合約程式碼片段所示,行權函式 exercise() 的內部是一個迴圈,依據引數中傳遞的 vaultsToExerciseFrom 中的地址數量依次呼叫真正的行權邏輯 _exercise() 函式。
圖2. 重用傳入合約的 ETH 來獲得抵押資產
函式處理 ERC20 Token 時,和大部分的 DeFi 專案做法一樣,使用 transferFrom(),如程式碼 1882 行所示,從 msg.sender 轉賬到 address(this)。
但是當函式處理的資產為 ETH 時,處理的方式就完全不一樣了。因為在 Solidity 中,msg.value 的意思是合約呼叫者在呼叫具有 payable 介面時所轉給該合約的 ETH 數量,僅是一個量值,所以在合約程式碼的 1879 行中,檢查 msg.value == amtUnderlyingToPay 僅能確保合約確實收到了 amtUnderlyingToPay 數量的 ETH,並不會對 msg.value 的值造成任何影響。
但是正如上面講到的在 exercise() 中會迴圈呼叫 _exercise() 函式,這導致儘管合約實際只收到一次ETH,然而在迴圈過程中卻可以重複使用。
攻擊點就在這裡,由於合約少了一步對 ETH 實時數量的檢驗,使得攻擊者可以先偽造一筆指向自己的交易,然後再把已經花掉的本金再次利用,和平臺其他使用者完成一筆正常交易。
圖3. 攻擊交易分析
在圖3中,我們透過 Bloxy 瀏覽器顯示的呼叫過程來展示攻擊的過程。由於攻擊者吃掉了很多筆訂單,我們以其中一筆交易為例,向大家展示其攻擊邏輯:
1、攻擊者先從 Uniswap 購入了 75 oETH 為進一步呼叫函式行權做好籌備;2、攻擊者建立了一個 Vault 地址,作為看空期權賣方,並且抵押24,750 USDC 鑄造出75 oETH,但並未賣出這些期權,等於自己同時買入了以 330 的價格賣出75 ETH 的權利;3、攻擊者在 Opyn 合約中呼叫了 exercise(),在持有 150 oETH 看空期權的情況下,先向自己的 Vault 地址轉入了75個 ETH,獲得自己事先抵押的 24,750 個 USDC,再重利用了這75個 ETH,成功吃掉了另一個使用者的 24,750 個 USDC,進而實現非法獲利。
修復建議
PeckShield 安全團隊建議,在 Solidity 中,合約可使用一個區域性變數 msgValue 來儲存所收到 ETH(即 msg.value 的值)。這樣,在後續的步驟中透過操作 msgValue ,就能準確的標記有多少 ETH 已經被花費,進而避免資產被重複利用。此外,我們還可以使用 address(this).balance 來檢查合約餘額來規避 msg.value 被重複使用的風險。