By:Kong@慢霧安全團隊

據慢霧區消息,2021 年06 月28 日,Polygon 上算法穩定幣項目SafeDollar 遭受閃電貸攻擊,慢霧安全團隊第一時間介入分析,並將結果分享如下:

攻擊細節分析

Phase 1

首先攻擊者提前數小時創建了一個合約,並通過Polydex 將MATIC 兌換成PLX 代幣。

隨後攻擊者通過此合約將獲得的PLX 代幣存入SafeDollar 項目的SdoRewardPool 合約中,SdoRewardPool 合約架構參考自SUSHI 的MasterChef 合約,用戶在SdoRewardPool 合約中抵押指定的代幣即可獲得SDO 代幣獎勵。

而攻擊者提前數小時在SdoRewardPool 合約中抵押PLX 代幣就是為了後續獲取抵押獎勵做準備。

Phase 2

攻擊者在Phase 1 中抵押數小時後部署了另一個合約,這也是主要的攻擊合約,在此攻擊合約中,攻擊者利用通過Polydex 的多個池子閃電貸借出大量的PLX 代幣。

隨後在同一筆交易中攻擊合約將獲得的PLX 代幣不斷地在SdoRewardPool 合約中進行“抵押- 提現”操作。

而我們知道SdoRewardPool 合約架構參考自SUSHI 的MasterChef 合約,因此攻擊者在同一筆交易內不斷的進行抵押提現是無法獲得SDO 代幣獎勵的。那麼攻擊者最終是怎麼獲利的呢?

通過鏈上分析我們可以發現攻擊者是通過Phase 1 中部署的第一個合約獲得大量SDO 代幣獎勵的。

那麼為何攻擊者只是在Phase 1 中進行簡單的抵押操作就可以獲得大量的SDO 代幣獎勵呢?這與攻擊者在第二個攻擊者合約中不斷地進行“抵押- 提現”操作有何联係呢?帶著這些疑問我們一步步的進行分析:

首先查看攻擊者獲得大量代幣獎勵的交易,在這筆交易中攻擊者只是簡單的對SdoRewardPool 合約進行updatePool 操作,並通過withdraw 進行提現。而正是在提現時SdoRewardPool 合約鑄造了大量的SDO 代幣給攻擊者。

既然是鑄造出非預期數量的代幣給到攻擊者,那麼必然是獎勵計算出現了問題。在SdoRewardPool 合約中負責SDO 鑄幣獎勵的是_harvestReward 函數,我們跟進此函數:

通過上圖我們可以發現鑄幣數量取決於所計算的_claimableAmount 參數,而影響此參數的就只有用戶的抵押數量user.amount、池子的獎勵數量pool.accSdoPerShare、精度1e18 以及用戶負債user.rewardDebt。我們直接對鏈上數據進行分析可以發現用戶抵押數量與池子的獎勵數量相乘時,pool.accSdoPerShare 是一個十分巨大的數值,這導致了最後計算結果變得非常巨大。

而池子的獎勵數量pool.accSdoPerShare 是在updatePool 函數中進行更新的,我們繼續跟進updatePool 函數:

可以很容易的看出pool.accSdoPerShare 主要是由緩存的accSdoPerShare、_sdoReward 以及lpSupply 決定的。我們直接分析此段邏輯的鏈上數據可以發現此時的lpSupply 只有2!這就導致了在計算pool.accSdoPerShare 時除了一個非常小的lpSupply 造成結果變得十分巨大。

而lpSupply 取的是抵押的PLX 代幣在SdoRewardPool 合約中的餘額,但既然在SdoRewardPool 合約中有大量的PLX 代幣抵押,為何最終在獲取餘額的時候只有2 呢?我們可以很容易的猜測是PLX 代幣在獲取數量時出現的問題,我們跟進PLX 代幣進行分析:

我們可以發現餘額獲取的是_balances 變量,而此變量是在用戶進行轉賬時進行改變,我們跟進其轉賬函數可以發現在用戶進行轉賬時會調用_move 函數,我們跟進此函數可以發現這是一個通縮型邏輯,也就是說A 用戶在轉賬給B 用戶時,B 用戶接收到的數量會小於A 用戶發送的數量!

而此時本次攻擊核心就呼之欲出了!因為我們可以知道SdoRewardPool 合約架構參考自SUSHI 的MasterChef 合約,而此架構是用戶不管抵押了多少代幣,在其提現時都可以取出相同數量的代幣。我們跟進SdoRewardPool 合約的deposit 與withdraw 函數進行驗證:

其抵押與提現的邏輯不出我們所料,用戶存入多少PLX 代幣,在其提現時SdoRewardPool 合約就會發送給用戶多少代幣,即用戶存入100 個PLX,提現時也能提走100個PLX 代幣。

而通過上面的分析我們可以知道PLX 代幣是通縮型代幣,在用戶抵押100 個代幣時,SdoRewardPool 合約收到的數量小於100 個,但用戶提現時SdoRewardPool 合約卻又給了用戶100 個代幣。相當於用戶少充多提了,所以攻擊者就是利用此通縮型代幣與SdoRewardPool 合約的兼容性問題進行反复的“抵押- 提現”操作,最終把SdoRewardPool 合約中的PLX 代幣數量消耗至2,導致SdoRewardPool 合約在鑄造獎勵時鑄造出了大量的SDO 代幣。

攻擊流程

1. 攻擊者先通過“攻擊合約1”在SdoRewardPool 合約中抵押PLX 代幣,以便後續獲取SDO 代幣獎勵。

2. 攻擊利用PLX 通縮型代幣與SdoRewardPool 合約的兼容性問題,通過“攻擊合約2”頻繁地在SdoRewardPool 合約中反復進行“抵押- 提現”操作,最終導致SdoRewardPool 合約中的PLX 代幣數量消耗到一個極小的數量2。

3. 隨後攻擊通過“攻擊合約1”進行在SdoRewardPool 合約中進行提現操作以獲取SDO 代幣獎勵,獎勵的計算會除SdoRewardPool 合約中PLX 代幣數量,而此時SdoRewardPool 合約中PLX 代幣數量是一個極小的數量2,所以導致除法計算後獎勵的數量變成一個巨大的值。

4. 攻擊者在獲得大量SDO 獎勵後,直接在市場上進行拋售,獲利離場。

總結

此次攻擊的核心問題在於“通縮型代幣”與架構參考自SUSHI 的MasterChef 合約不兼容導致的。此兼容性問題造成SafeDollar 項目的SdoRewardPool 合約中PLX 代幣被惡意耗盡,而SDO 代幣獎勵計算又依賴於SdoRewardPool 合約中PLX 代幣數量,最終導致SDO 代幣價格閃崩的慘劇發生。由於當前DeFi 項目需要多個合約間進行交互,因此慢霧安全團隊建議在進行設計時應充分考慮不同合約間交互的兼容性問題。