Hundred Finance 攻擊事件分析

阿菜ACai發表於2023-11-05

Hundred Finance

背景知識

Hundred Finance 是 fork Compound 的一個借貸專案,在2023/04/15遭受了駭客攻擊。攻擊者在發起攻擊交易之前執行了兩筆準備交易佔據了池子,因為發起攻擊的前提是池子處於 empty 的狀態(發行的 hToken 數量為 0)。

準備交易:

  1. https://optimistic.etherscan.io/tx/0xf479b1f397080ac01d042311ac5b060ceccef491867c1796d12ad16a8f12a47e
  2. https://optimistic.etherscan.io/tx/0x771a16e02a8273fddf9d9d63ae64ff49330d44d31575af3dff0018b04da39fcc

攻擊交易:Phalcon || Tendery

交易分析

兩次準備交易一共存入 63816 + 30000000 = 30063816 wei WBTC,獲得 3190800 + 1499976495 = 1503167295 wei hWBTC

WBTC decimal = 8,hWBTC decimal = 8


執行攻擊交易,首先從 AAVE V3 閃電貸出來 500 WBTC

image

透過 tendery 的模擬交易可以查詢到,在攻擊交易執行前,池子中存在 30064194 wei WBTC

image

首先 redeem 之前存入的所有 WBTC,將池子還原到 empty 的狀態。

image

redeem 之後池子中存在 378 wei WBTC(其中1wei為留存資金,377wei為reserve資金),發行 0 hWBTC。empty狀態僅代表 hWBTC 的 totalsupply 為 0。(如果先入為主地認為 WBTC的數量也為0,那麼當你看到後面的時候會發現憑空多redeem出來了1 wei WBTC)

建立合約 0xd340 並往其中傳送 50030063816 wei WBTC


合約 0xd340:

首先存入 4 WBTC,mint 200 hWBTC

image

redeem 19999999998 wei hWBTC,收到 4 WBTC。此時合約持有 2 wei hWBTC

image

向池子轉入 50030063816 wei WBTC,然後借出 1021.915074492787011273 ETH

image

呼叫 redeemUnderlying 函式取出 50030063815 wei WBTC,消耗 1 wei hWBTC。此時合約持有 1 wei hWBTC,池子持有 1 wei WBTC

image


攻擊合約呼叫 liquidateBorrow 函式對建立的 0xd340 合約的債務進行清算。支付 0.000000267919888739 ETH,獲得 1 wei hWBTC。

image

redeem 1 wei hWBTC,獲得 2 wei WBTC,此時池子重新回到 empty 狀態。這樣做的目的是為了可以再次掏空其他池子。

image

把 50030063817 wei WBTC 轉移走

image

隨後攻擊者又再進行了6次相同的操作來掏空其他的池子完成獲利,文章篇幅有限就不再展開說明。

漏洞程式碼分析

合約 0xd340 在進行 redeem 操作時利用了精度丟失的漏洞,獲取超額的 WBTC 。漏洞的發生在於 redeemFresh 函式中。

image

進入到 trace 分析,發現在 truncate 函式中進行了精度丟失。

image

跟進程式碼檢視 truncate 函式的具體實現方法,在對輸入引數 exp1e18 的時候發生了精度丟失。

image

攻擊細節分析

在分析攻擊的過程中,對一些細節的部分存在著困惑,嘗試著用生疏的技巧淺淺的分析一下。

為什麼要先 mint 再 redeem,剩餘 2 wei hWBTC

因為 mint 函式只能根據抵押物的數量來 mint hToken。也就是說在 initialExchange = 0.02 WBTC/hWBTC 的情況下,即使是傳入 1 wei 的 WBTC,也會 mint 出 50 wei hWBTC。想要得到 2 wei hWBTC的剩餘,沒辦法透過直接 mint 2 hWBTC 的方式(因為你無法提供 0.04 wei WBTC),所以只能先 mint 出大量的 hWBTC,然後再 redeem 使其剩餘 2 wei。

所以按道理來說是不是先 mint 出 50 wei hWBTC,再 redeem 48 wei hWBTC 也可以?

image

為什麼要剩餘 2 wei hWBTC,而不是其他數量

剩餘一定數量的 hWBTC 是為了後續構造精度丟失的攻擊,使得合約從 hWBTC 的數量來計算抵押率是滿足的,從而批准這筆 redeem,而實際上借出的 WBTC 數量是不滿足抵押率要求的。而攻擊者構造 2 wei 的這個數量就是為了透過精度丟失,是的超額借出的 WBTC 數量最大化。

假設在 2 wei 的情況下,borrow 了一半價值(1 wei)的資產:

贖回價值 1.99… wei hWbtc 的 WBTC,實際銷燬 1 wei hWbtc。此時超額部分為 0.99… wei hWBTC,獲得的 WBTC 佔總資金的 1.99 / 2 。

在 20 wei 的情況下,borrow 了一半價值(10 wei)的資產:

贖回價值 10.99… wei hWbtc 的 WBTC,實際銷燬 10 wei hWbtc。此時超額部分為 0.99… wei hWBTC,獲得的 WBTC 佔總資金的 10.99 / 20 。

透過上面兩個例子我們可以得出,剩餘的 hWBTC 數量越少,攻擊者透過精度丟失所獲得的超額 WBTC 比例就越大。

剩餘的 hWBTC 數量可不可以為 1 wei 呢?

假設剩餘 1 wei hWBTC,攻擊者可以借出 100% 價值的資產,此時贖回價值 0.99… wei hWBTC 的 WBTC,利用精度丟失實際 burn 0 hWBTC。這樣構造最大的好處是借出的資產可以達到 100%,而 2 wei 的方案借出資產只能借出 50%。

攻擊者是如何構造獲利場景的

攻擊者只消耗 1 wei hWBTC ,然後 redeemUnderlying 出了 50030063815 wei WBTC

image

攻擊者先 deposit 50030063816 wei WBTC,然後 redeem 50030063815 wei WBTC,希望透過 redeem (deposit amount - 1) 的方式構造精度丟失的場景:redeem 出價值 1.999… wei hWBTC 的 WBTC,最終會 burn 1 wei hWBTC,超額收益 0.999... hWBTC。

image

但是由於在攻擊執行前池子裡剩餘有 1 wei WBTC,所以攻擊者直接 redeem 50030063816 wei WBTC 也是可以達到 burn 1 wei hWBTC 的目的的。也就是說只有當 redeem 50030063817 wei WBTC 的時候才會 burn 2 wei hWBTC。

image

這個精度缺失攻擊的前提是池子中 WBTC 的數量大於 hWBTC 的數量

假設 3 WBTC,2 hWBTC,可得 exchangeRateStoredInternal = 3 / 2 = 1.5

贖回 2 WBTC,計算需要 burn 的 hWBTC 數量:2 / 1.5 = 1.333… → 1 hWBTC

可以透過一個公式來計算出攻擊者持有部分 hWBTC 的情況下透過精度丟失得到最大獲利的情況嗎?

  1. 假設池子持有 x WBTC,總共發行了 y hWBTC。攻擊者持有 z hWBTC (z < y),贖回 kx WBTC (0 < k < 1)
  2. exchangeRateStoredInternal = x / y
  3. 由 1 和 2 可得,要 burn 的 hWBTC 數量 = kx / (x / y) = ky
  4. 攻擊者為了獲取儘可能大的超額收益,需要透過精度丟失漏洞構造 burn z + 0.999… hWBTC → burn z hWBTC
  5. 由 3 和 4 可得,ky = z + 0.999 → k = (z + 0.999) / y

舉例說明:

  1. 假設池子持有 20000 WBTC,總共發行了 100 hWBTC,exchangeRateStoredInternal = 200。攻擊者持有 50 hWBTC
  2. k = (z + 0.999) / y = (50 + 0.999) / 100 = 0.5099
  3. 贖回 kx = 0.5099 * 20000 = 10198 WBTC
  4. burn 的 hWBTC 數量為 kx / (x / y) = 10198 / 200 = 50.99 → 50

如何計算出清算所需要的 token 數量

透過 liquidateCalculateSeizeTokens 函式,計算得出提供 267919888739 wei ETH,能夠清算獲得 1 wei hWBTC

image

然後攻擊者執行 liquidateBorrow 函式,提供 267919888739 wei ETH 進行清算,獲得 1 wei hWBTC 。具體的計算過程以及涉及的引數如下圖所示:

image

後記

都週末了還擱這寫分析文章博主是沒有自己的生活的嗎?

相關文章