ccryptoo 1 1640340847
新聞

DeFi乾貨 | 深入解析 Dexfolio 的重入漏洞事件 -Amber安全團隊

以太坊虛擬機(EVM)及 Solidity 智慧合約問世後,重入攻擊事件頻傳。本文 Amber Group 安全團隊將分享並解析他們在 8 月所發現幣安智能鏈(BSC)項目 Dexfolio 的 LPFarming 合約漏洞案例。
(前情提要:DeFi 乾貨|Dinosaur Eggs 的流動性池漏洞解析 – Amber 安全團隊

 

(本文由  Amber Group 研究團隊撰著、提供。)


以太坊虛擬機(EVM)及 Solidity 智慧合約問世後,短短數年間內就出現了許多重入攻擊事件。 2021 年 8 月 15 日,我們發現幣安智能鏈(BSC)項目 Dexfolio 的 LPFarming 合約存在一個可被重入攻擊的漏洞,並透過漏洞懸賞平台 ImmuneFi 通報了這個漏洞。

由於 Dexfolio 研發團隊並未在過去的 120 天內公布事後剖析報告,因此我們將在本篇文章中提供相關細節。

0x00: Dexfolio

Dexfolio 的 LPFarming 合約可讓用戶以質押資產的方式來進行流動性挖礦。 對於用戶所持有的每項資產,LPFarming 合約提供了四個公用函數處理質押任務。

例如:LPFarming.stake() 函數可讓使用者將幣安幣(BNB)轉入合約,並將半數的 BNB 轉換為 DEXF 代幣,用以鑄造 DEXF-WBNB LP 代幣後,質押 LP 代幣。

ccryptoo 1 1640340848

從上方的程式碼片段可看到,在第 555 行 newBalance 由當前餘額扣掉原始餘額 initialBalance 計算出新鑄造的 DEXF-BNB LP 代幣數量。在第 560 行,新的質押記錄被加入了 _stakes[]  陣列,作為計算挖礦獎勵的依據。

0x01:漏洞

在這四個質押函數中,stakeToken() 是一個較為特殊的函數,它可以讓使用者透過支付任意一種 ERC20 代幣來交換 DEXF-BNB LP 代幣以作為質押資產。

然而,我們發現此處並未具有防止重入的 nonReentrant modifier。由於使用者可以傳入任意的 fromTokenAddress 執行 stakeToken(),因此很多的 ERC20 函數調用 (例如 transfer()、transferFrom() 及 approve() 等) 都可能被用於劫持控制流程或重入 stakeToken()

ccryptoo 1 1640340849

這使得不法分子可於原始 stakeToken() 調用的主體內嵌入另一個質押操作,導致 651 行的 newBalance 變大。 簡言之,攻擊者可能會針對同一批 LP 代幣進行雙重質押。

例如,攻擊者調用 stakeToken(10),並透過另一個帳戶及 fromTokenAddress.approve() 嵌入另一個 stakeToken(90)。 最終,第一個帳戶持有 10 + 90 = 100 個質押的 LP 代幣,而第二個帳戶則持有 90 個,後者的 90 在此處經過了重複計算。

乍看下,因為第二個帳戶必須執行某些程式碼才能重入 stakeToken(),故第 638 行的 isContract 檢查可防止重入。 然而,在 LPFarming 裡的 isContract 的實作無法涵蓋所有情況,例如在 constructor 裡就能實現繞過檢查的惡意程式碼。

0x02:漏洞利用

為利用重入漏洞,我們需要一個惡意 ERC20 合約 (Ftoken) 來劫持 approve() 調用。 一如下方的程式碼片段所示, Ftoken 透過 _optIn 開關覆寫了 OpenZeppelin ERC20 實作的 approve() 函數。

當開關開啟時,創建 Exp 合約並在其 constructor 中嵌入上文提及的 stakeToken() 調用,以便避開有漏洞的 isContract 檢查。

ccryptoo 1 1640340851

由於 LPFarming 的 isContract modifier 僅檢查某地址對應的 extcodesize,我們可透過執行 Exp 合約中建構函數 (constructor) 裡的 LPFarming.stakeLPToken() 來繞過保護機制,如下所示。

ccryptoo 1 1640340852

若真的很希望避免使用合約帳戶,則須確保檢查 tx.origin == msg.sender

ccryptoo 1 1640340853

此處遺漏了一個部分。由於 stakeToken() 在 PancakeSwap 將 fromTokenAddress 資產轉換成 DEXF-BNB LP 代幣,我們必須創造 Ftoken-BNB 對並增加其流動性。我們透過另一個 Lib 合約來實現此一目的。

下方的 Lib.trigger() 函數可使我們在 PancakeSwap 上創造 Ftoken-BNB 對,並將與 Ftoken 數量相同的 WBNB 放入流動池中。 此外,我們也加入了一個 Lib.sweep() 方便 owner 在完成攻擊後搜刮流動池中所有剩餘的 WBNB。

ccryptoo 1 1640340853 1

備妥這三份合約後,我們就可以進行實驗以驗證我們的理論。 如下方 eth-brownie 截圖所示,我們先從 21 WBNB 開始,並部署了 FtokenLib 合約。

如前所述,我們使用 Exp 合約的 constructor 來重新質押部分 LP 代幣並通過 LPFarming.getStakes() view function 可以觀察到 Exp 合約目前所持有的份額數量。

由於 Exp 合約需要 LP 代幣用於重新質押,但直到 Ftoken.approve() 中 Exp 才會被創造,如此處所示範的 [1],我們用 Ftoken 合約地址預先以 eth-util 計算出 Exp 地址,並且將 LP 代幣轉過去。

ccryptoo 1 1640340854

準備好 FtokenLib,並以 Lib.trigger() 創造 Ftoken-BNB 對後,我們便可執行第一個 stakeToken() 發起重入攻擊。

ccryptoo 1 1640340856

如上圖所示,我們在 stakeToken() 調用前後均執行 Ftoken.optIn(),以便啟動及切換 Ftoken.approve() 中的劫持機制。

ccryptoo 1 1640340856 1

最後,我們用 LPFarming.emergencyWithdraw() 提取 LP 代幣的數額並轉換為 WBNB。 此外, 我們也執行 Lib.sweep() 以便獲取 Ftoken-BNB 流動池中其餘的 WBNB。

最終我們取得了 19.36 WBNB,並在 LPFarming 合約中留下 Exp 合約的質押記錄。 由於 Exp 合約已部署於 Ftoken.approve() 調用中,我們無法重新初始化合約以及在 constructor 中再次執行 LPFarming.emergencyWithdraw(),因此,攻擊者似乎無法從中獲利。 然而實際上,CREATE2 指令可使我們能夠重新初始化 Exp 合約。

0x03: CREATE2

CREATE2 指令是以太坊 Constantinople 硬分叉後的產物,能使用戶透過特定合約的位元組程式碼及 salt 值預先計算合約地址。 伴隨而來的結果是,如果合約以 SELFDESTRUCT 指令自毀,同樣的位元組程式碼及 salt 值可能會再次被部署在同一個地址上。

利用這個特性,我們可以讓 Ftoken.approve() 部署 Exp 合約並執行重入後執行 SELFDESTRUCT。之後,我們可重新部署 Exp 合約並執行 LPFarming.emergencyWithdraw() 以提取雙重質押的 LP 代幣。

ccryptoo 1 1640340858

上圖顯示了變更後的 Ftoken.approve()。有四個參數傳遞至 CREATE2 調用。 第一個 0 代表創造合約時支付了 0 以太幣,最後一個 0 是 salt 值,其應與重新建立合約時的值相同。 第二、第三個參數均與我們要部署的位元組程式碼相關。

ccryptoo 1 1640340858 1

除了變更後的 Ftoken.approve() 外,我們還新增了 getAddress() view 函數以預先計算 Exp 合約地址。 我們在這裡只須根據 EIP-1014,以 0xff、創建者地址及 32 位元組的 salt 值及合約程式碼 bytecode。

ccryptoo 1 1640340859

除了上述的修改,我們也調整了 Exp 合約,根據 token 合約所保持的狀態來質押或提取 LP 代幣,並以 SELFDESTRUCT 指令自毀的方式允許下次的重新部署操作。

在 CREATE2 的幫助下,我們的 Exp 合約便可賺取利潤,如下方截圖所示:

ccryptoo 1 1640340859 1

我們成功地在區塊高度 10181384 從 LPFarming 合約中獲取 1,558 個 LP 代幣,並將其轉換為 WBNB。

ccryptoo 1 1640340860

0x04:事件的時間軸與致謝

我們在 2021 年 8 月 15 日將此問題回報給 ImmuneFi 平台 [2], 且 Dexfolio 團隊已要求使用者於 2021 年 8 月 20 日提取其資產 [3]。在我們通報之後,ImmuneFi 隨即告知我們,此漏洞報告有可能是重複回報,但須與 Dexfolio 團隊確認。

由於我們已逾 90 天未能從 ImmuneFi 或 Dexfolio 得到回應,因此我們選擇以獨立研究的方式揭露細節。 在我們表示有意揭露細節後,ImmuneFi 便為這份有效的報告向我們適當地揭露流程,於 2021 年 11 月 26 日贈予我們 00 美元等值的以太幣ETH)以作為獎勵。 此外,ImmuneFi 也協助我們聯繫了最初回報同樣漏洞的「白帽」駭客 lucash-dev。

關於 Amber Group

成立於 2017 年,業務遍及亞洲及歐美各大主要城市,現為 1,000 多家知名大型機構客戶提供加密金融服務,在 100 多個電子交易所中累計交易總額已超過 1 兆美元,資產管理規模超過 40 億美元,幫助客戶管理各種加密資產的風險,提供靈活化的投資、最大化的回報來優化長期價值

Amber Group官方網站: www.ambergroup.io

WhaleFin官方網站: www.whalefin.com

若有產品相關問題,請聯繫 Amber 客服團隊: [email protected]

📍