前言
BEVO代幣是一種Reflection Token(反射型代幣),並且擁有通縮的特性。關於Reflection Token更為詳細的說明可參考這篇文章。然後目前瀏覽到的很多分析報告沒有指出其漏洞產生的真正原因,所以就自己試著去做一下分析吧。
相關資訊
- 攻擊交易:https://explorer.phalcon.xyz/tx/bsc/0xb97502d3976322714c828a890857e776f25c79f187a32e2d548dda1c315d2a7d
- BEVO合約地址:https://bscscan.com/token/0xc6cb12df4520b7bf83f64c79c585b8462e18b6aa
- FeeAddress地址:https://bscscan.com/address/0x473141b6f5e33dd90bd653940a854b58e83451db
- FeeAddress地址透過Pair用BEVO兌換WBNB:https://bscscan.com/tx/0xc38c290d9139b2f30fbf65666854e09fdc0efe6368c810d4863b3031c93ebc37
攻擊過程分析
整個攻擊過程很簡單:
- 首先閃電貸借出大量的WBNB
- 透過池子換出大量BEVO
- 呼叫deliver函式消耗所有的BEVO
- 呼叫skim函式,獲得比在deliver函式中所消耗的更多的BEVO
- 呼叫swap函式獲得超額的WBNB
每一步都是正常的操作,但是最後的結果就是攻擊者成功獲利了。背後漏洞的成因還有點繞,接下來詳細分析。
函式流程對比
由於BEVOToken是基於ReflectionToken進行修改的版本,而原版的反射代幣執行這個簡單的deliver+skim操作是安全的。那麼我們將原版的ReflectionToken與魔改後的BEVOToken的_transferStandard函式進行一個對比(該函式在transfer的過程中要用到)。
REFLECT.sol(Original)
_transferStandard 流程
- 透過_getValues函式,基於tAmount計算出所有後續要用到的tAmount和rAmount,包括Fee和TransferAmount
- 登記rTransferAmount的轉賬:sender賬戶減少,recipient賬戶增加。
- 然後將Fee燃燒掉:從rTotal中減去rFee
CoinToken.sol(BEVO)
_transferStandard 流程
- 透過_getValues函式,基於tAmount計算出所有後續要用到的tAmount和rAmount,包括Fee,Burn,Charity和TransferAmount
- 透過_standardTransferContent函式,登記rTransferAmount的轉賬:sender賬戶減少,recipient賬戶增加。
- 呼叫_sendToCharity函式,將Charity值增加到FeeAddress賬戶中。
- 呼叫_reflectFee函式,從rTotal中減去rCharity,rFee,rBurn。從tTotal中減去tBurn。
BEVO機制漏洞
盈利的條件
先來了解一個前置條件,假設不收取任何費用的情況下,要透過deliver+skim這兩個操作進行盈利,則需要要求skim得到的rAmount大於deliver掉的rAmount值。表示為rSkim > rDeliver。
公式推導為:
- [1]
rPair = tPair * (rTotal / tTotal)
- deliver操作
- [2]
rPair2 = tPair * (rTotal - rDeliver) / tTotal
- [3]
rSkim = rPair2 - rPair
- 若要求[4]
rSkim > rDeliver
,將公式1,2,3代入公式4 - 得[5]
rDeliver > rTotal - rPair
而正常情況下,rTotal作為所有rAmount值的總和,自然可得rTotal >= rDeliver + rPair,調整後得[6]rDeliver <= rTotal - rPair
。顯然,公式5中的條件並不能滿足,無法實現透過deliver+skim這兩個操作進行盈利的目的。
那究竟發生了什麼,打破了這種安全的場景。
奇怪的計算
在函式流程對比這一章節中有提到,BEVO呼叫_transferStandard轉賬的時候,會收取一個叫Charity的費用(1%)。收取這個費用的時候會將rCharity和tCharity增加到FeeAddress賬戶中,並且從rTotal中減去rCharity。
這裡就出現了問題:
- 如果BEVO只是將rCharity從rTotal中減去,則效果類似於deliver和Fee的燃燒。
- 如果BEVO只是將rCharity和tCharity增加到FeeAddress賬戶中,則效果類似於轉賬。
但是它既將rCharity從rTotal中減去,又將rCharity和tCharity增加到FeeAddress賬戶中,這使得從rTotal的數值是不包括rAmount[FeeAddress]的,而FeeAddress地址上確實存在有一筆rAmount。這就有點像一筆本該銷燬的資金,被FeeAddress偷偷藏了起來,這筆資金從rTotal賬面上來看是銷燬了,但是卻被FeeAddress揣在了口袋裡。
然後,如果這筆資金凍結在FeeAddress地址中不再流轉,那麼也不會對整個經濟模型進行影響(就相當於deliver了)。但是,FeeAddress會用它手中的BEVO從Pair裡兌換WBNB。這就使得了這筆不在rTotal範圍內的資金流入了Pair地址中。
這會造成什麼影響?
漏洞的成因
前面提到,FeeAddress會用它手中不合規的BEVO從Pair裡兌換WBNB,從而使這筆不在rTotal範圍內的資金rFeeAddress流入了Pair地址中。此時,將Pair池中的rAmount從新定義為[7]rPair2 = rPair + rFeeAddress
。
這筆不在rTotal累加範圍內的資金加入,使得代表盈利條件的公式5能夠實現,由rDeliver > rTotal - rPair2,將公式7代入後調整可得[8]rDeliver + rFeeAddress > rTotal - rPair
。
所以,攻擊者首先借助閃電貸借出大量的BEVO代幣,透過deliver操作,使得Pair中屬於rFeeAddress的那部分的代幣份額增大,從而執行skim操作的時候就能拿到超額的BEVO代幣。最後歸還閃電貸跑路。
後語
最近在學習ReflectionToken的相關內容,瞭解到了BEVO代幣的安全事件,打算跟著網上的分析文章把細節摸清一下。但很可惜發現網上的分析文章都是淺嘗輒止,一副懂的都懂不懂的我也不多說了的樣子,看到攻擊者deliver+skim獲利了就斷定是deliver導致rTotal減少,從而rate增大就獲得了套利機會(其實賺不了)。然後我按照文章中的思路計算了兩天死活就算不出來盈利(就讓我多吐槽幾句吧因為我真的手算了好多頁紙硬是沒算出來)。所以自己摸索著搞了一份分析,因為小老弟也是在學習的過程中,所以文章有可能有錯的地方,還請各位師傅多多指點。感謝閱讀!