事故主要原因在於 Alchemix 通過 transmuter 添加了 3 次 vault,最終導致通過錯誤的元素獲取了錯誤的收益。

原文標題:《走過最長的路,竟是自己的套路 —— Alchemix 事件分析》
撰文:yudan,就職於慢霧安全團隊

據慢霧區消息,2021 年 06 月 16 日,以太坊 DeFi 項目 Alchemix 的 alETH 合約疑似出現安全問題。17 日,Alchemix 發佈了事故分析報告,慢霧安全團隊迅速介入分析,並在官方分析報告的基礎上梳理了本次事件的整個脈絡和核心關鍵點,供大家參考。

慢霧:技術詳解 DeFi 借貸協議 Alchemix 中 alETH 合約漏洞事件

太長不看系列

本次分析文章很長。這裏先說結論,方便大家有個大概的理解。本次事故的主要原因在於 Alchemix 通過 transmuter 添加了 3 次 vault,導致收益信息記錄在了一個錯誤的元素上,而在調用 transmuter 的 harvest 函數時也沒有傳入正確的 index 值,導致通過錯誤的元素獲取了錯誤的收益,將錯誤的 4300 ETH 的收益發送到 adapter 合約,幫助用戶償還了 alETH 的貸款,造成收益增多的問題,導致了悲劇。

核心分析——Round 1

慢霧:技術詳解 DeFi 借貸協議 Alchemix 中 alETH 合約漏洞事件

根據官方發佈的事故分析報告,本次事故的原因是官方的 alETH 的部署腳本意外地創建了額外的 vaults,導致 Alchemix 使用了 vaults 數組中錯誤的索引並計算出了錯誤的獎勵,導致 transmuter 把所有的獎勵用於償還了用戶的所有負債。我知道單單是這句簡短的分析讓人有點雲裏霧裏,摸不着頭腦,所以我們只能把目標放在官方給出的交易中,看看能不能找到真相。

根據官方給出的交易,通過 ethtx.info 分析工具進行分析,我們不難發現,這筆交易調用了 AlchemistEth 合約的 harvest 函數,並且傳入了 _vaultId=0 這個參數,最後返回了
「4308144937764982868765」和「4308144937764982866415」這兩個值。

慢霧:技術詳解 DeFi 借貸協議 Alchemix 中 alETH 合約漏洞事件

爲了更加了解 harvest 函數的作用,我們需要對整個函數進行分析:

慢霧:技術詳解 DeFi 借貸協議 Alchemix 中 alETH 合約漏洞事件

不難發現,harvest 函數其實包含兩個重要的操作,分別是收穫獎勵和將獎勵分發給 transmuter 合約。其中 vault 是一個 library 庫合約,其中的 harvest 邏輯實現如下:

慢霧:技術詳解 DeFi 借貸協議 Alchemix 中 alETH 合約漏洞事件

通過代碼分析不難發現,vault 庫合約的 harvest 函數其實是檢查了外部的 adapter 的總的資金量,然後根據 adapter 中的資金量減去用戶的充值數量計算出收益的部分。

這裏我們可以將這個 adapter 理解爲一個策略池,用於管理用戶的資金和收益。然後我們回到用戶一開始的 AlchemistEth 合約中的 harvest 函數 , 發現返回的「4308144937764982868765」和
「4308144937764982866415」這兩個值其實對應的就是 vault 庫合約的 harvest 函數計算出的需要提現的代幣數量和從 adapter (策略池) 中取回的代幣的數量。由於這個 adapter 對應的收益代幣是 WETH,精度爲 18 位,那麼 「4308144937764982866415」這個數值換算過來就是「4308.144937764982866415」個 WETH。

也就是說,本次 harvest 操作,收益了超過 4300 個 ETH 的收益,然後這個收益在下一步中通過 _distributeToTransmuter 函數給到了 transmuter 合約進行分發,我們看下分發過程中的邏輯是怎樣的:

慢霧:技術詳解 DeFi 借貸協議 Alchemix 中 alETH 合約漏洞事件

_distributeToTransmuter 函數的邏輯只有簡單的 3 行,我們主要關注的是最後的外部調用 —— lowerHashMinted 函數。該函數所對應的 xtoken 在這裏指的是 alETH 本身。因爲 alETH 本身是用戶通過借貸借出來的,所以 lowerHashMinted 這裏的操作其實是使用 harvest 的收益將 alETH 總的貸出數量減少了,從而減少了每個用戶的貸款。總結來說就是用 harvest 4300 ETH 的收益償還用戶的 alETH 貸款。

慢霧:技術詳解 DeFi 借貸協議 Alchemix 中 alETH 合約漏洞事件

打個小總結

這裏先總結下這個流程,就是 AlchemistEth 合約通過 harvest 函數,得到了 4300 ETH 的收益,並將這個收益分發出去了,用於償還用戶的 alETH 貸款,導致了我們看到的情況 —— 已經貸出 alETH 的用戶在不需要還款的情況下就可以拿回他們質押的 ETH。那究竟是爲什麼,會有這 4300 ETH 的收益呢?這多出來的 4300 ETH 的收益是怎麼來的?針對這個問題,我們開始下一輪的分析。

核心分析——Round 2

要了解爲什麼會多出來 4300 ETH,就必須瞭解 AlchemistEth 的資金存儲過程。在 AlchemistEth 合約中,合約總的充值情況是使用 Vault library 庫的 Data 結構體進行記錄的,然後通過 flushActiveVault 函數更新對應的充值數量 (totalDeposit)。

慢霧:技術詳解 DeFi 借貸協議 Alchemix 中 alETH 合約漏洞事件

慢霧:技術詳解 DeFi 借貸協議 Alchemix 中 alETH 合約漏洞事件

然後 depositAll 函數會將充值的代幣金額打到對應的 adapter(策略池) 中,那麼在下一次 harvest 的時候,通過 adapter(策略池) 獲取的 totalValue,就會是用戶的本金加上策略池的收益。爲了計算收益過程中的本金部分,我們對官方給出的交易進行 debug,發現本金僅爲 9000 ETH,從 adapter 獲取的收益加上本金共有 13000 ETH,也就是說 9000 ETH 的本金產生了 4300 ETH 的收益。

慢霧:技術詳解 DeFi 借貸協議 Alchemix 中 alETH 合約漏洞事件

但是,按照上面分析的邏輯,用戶的本金是不會產生那麼大的收益的,問題肯定是出在了 adapter 獲取的 totalValue。也就是說 adapter 不止只有 AlchemistEth 充值代幣,還存在其他的收益渠道。爲了驗證我們的想法,慢霧安全團隊分析了 adapter 的所有代幣收入,果然發現了一筆異常的轉入行爲,並且金額也能剛好對上多出的 4300 ETH 的收益。也就是說,問題就在這裏了。

慢霧:技術詳解 DeFi 借貸協議 Alchemix 中 alETH 合約漏洞事件

通過查看交易數據,發現這是一筆調用 harvest 操作的交易,調用的合約是 transmuter 合約:

慢霧:技術詳解 DeFi 借貸協議 Alchemix 中 alETH 合約漏洞事件

也就是說,是這個 harvest 函數出問題了,harvest 函數的邏輯如下:

慢霧:技術詳解 DeFi 借貸協議 Alchemix 中 alETH 合約漏洞事件

同樣是調用了 vault 的 harvest 函數,熟悉的配方,熟悉的味道。我們再次進行 debug,發現一個驚人的事實 ——在進行收益的時候,vault 的 totalDeposit 竟然爲 0,導致 4300 ETH 的收益直接分發給了 adapter,導致了 adapter 獲取的 totalValue 錯誤了,多了 4300 個 ETH,原因就是在這裏。

慢霧:技術詳解 DeFi 借貸協議 Alchemix 中 alETH 合約漏洞事件

到了這裏,我們已經很接近真相了,剩下要解決的就是爲什麼 totalDeposit 會爲 0?我們查詢了 transmuter 合約中能改變 totalDeposit 的地方,發現只有 _plantOrRecallExcessFunds 函數可以改變這個值,而這個函數上層調用的又是 distribute 函數。而 transmuter 合約的 distribute 函數是 AlchemistEth 合約在收益的時候進行調用的。也就是說本身的流程應該是:

  1. AlchemistEth 合約調用 harvest 進行收益
  2. AlchemistEth 合約調用 transmuter 合約的 distribute 函數記錄收益情況,並把收益部分給 adapter
  3. adapter 收到了 transmuter 的收益,根據收益償還用戶的 alETH 的貸款

但是問題就出在了 _plantOrRecallExcessFunds 函數中。由於在記錄充值信息的時候,用的是 _vaults.last() 來獲取最新的 vault,所以其實充值信息疊加在了最後一個元素上。但是項目方調用了三次 setActiveVault 函數,所以其實充值信息是疊加到了 _vaults 數組的 3 號元素,也就是 index 爲 2 的 vault 元素上。但是在 transmuter 合約在 harvest 的時候傳入的 _vaultId 卻是 0,0 號元素是沒有任何充值記錄的,所以 transmuter 合約就誤將所有的收益都給了 adapter 了。導致了悲劇的發生。

慢霧:技術詳解 DeFi 借貸協議 Alchemix 中 alETH 合約漏洞事件

慢霧:技術詳解 DeFi 借貸協議 Alchemix 中 alETH 合約漏洞事件

總結

到這裏,整個事情已經變得很清晰了,Alchemix 項目方由於某種原因,通過 transmuter 添加了 3 次 vault,導致收益信息記錄在了一個錯誤的元素上,而在調用 transmuter 的 harvest 函數時也沒有傳入正確的 index 值,導致通過錯誤的元素獲取了錯誤的收益,錯誤收益被髮送到 adapter 合約,造成收益增多,導致了悲劇。

慢霧安全團隊在此提醒,DeFi 是一個複雜的系統,在進行 DeFi 操作的時候,要記得檢查好業務邏輯中的每一個流程,防止意外的發生,在必要的時候可以聯繫專業的安全團隊進行專業的安全審計,防止事故的發生。

參考鏈接

官方事故分析報告:
https://forum.alchemix.fi/public/d/137-incident-report-06162021

收益計算錯誤交易
https://etherscan.io/tx/0x3cc071f9f40294bb250fc7b9aa6b2d7e6ca5707ce4d6d222157d7a0feef618b3

來源鏈接:mp.weixin.qq.com