【漏洞分析】Reflection Token 反射型代幣攻擊事件通用分析思路
在本篇文章中,我將通過一個攻擊事件引出 Reflection Token 攻擊事件的一個通用分析思路。
關于 Reflection Token 的其他案例分析,可以參考BEVO代幣攻擊事件分析及復現一文。
TomInu 攻擊事件
TomInu Token 是一個反射型代幣 reflection token,于2023-01-26遭到黑客攻擊,攻擊者獲利35577美元。
TomInu(被攻擊合約): 0x2d0e64b6bf13660a4c0de42a0b88144a7c10991f
攻擊交易: https://phalcon.blocksec.com/tx/eth/0x6200bf5c43c214caa1177c3676293442059b4f39eb5dbae6cfd4e6ad16305668
攻擊過程較為簡單,攻擊者通過幾個常規操作就完成獲利。

- 攻擊者借出閃電貸
- 用 WETH swap 出 1465904852700232013011 TINU
- deliver 1465904852700232013011 TINU
- skim 得到 1733770910894426471783 TINU(在這一步已經完成了獲利)
- 把 TINU swap 為 WETH
- 歸還閃電貸
本次攻擊事件通過推文告警并且進行了分析,但是很可惜分析的結論略顯含糊的。紅框標注的部分并不是攻擊者獲利的真正原因。(為什么不是真的)
推文:https://twitter.com/QuintenDes/status/1618730379447508998

并且,目前在在網絡上搜索到的所有關于 reflection token 攻擊事件的成因分析中提到:“由于攻擊者 deliver 了一筆 token ,導致了 pair 中的 token 升值,從而能夠 skim 出更多的 token 進行獲利”。這類分析大多是理所當然地下結論,沒有通過實際的計算推導,妄下結論誤導讀者。(為什么這么說)
攻擊過程很簡單,先 deliver 然后 skim,就能夠獲利了。根據這個攻擊過程的特征,我們直接找 rToken增發代碼,定位漏洞點。(為什么可以這么做)
以上的幾個為什么都將會在后面“為什么”這一章節進行解釋,讀者可以先帶著疑問進行閱讀。
漏洞分析
在 _transferStandard 函數中可以看出,TomInu 代幣在進行轉賬 rAmount 時需要收取 team 和 fee 兩種手續費,并將扣除了手續費后的 rTransferAmount 轉給收款人。

其中 team 手續費 rteam 留存在本合約中,fee 手續費 rfee 則是直接銷毀。

此時他們的數量關系應該為:rTransferAmount = rAmount - rteam - rfee
問題出現在 _getRValues 函數中,該函數在計算 rTransferAmount 的過程中忽略了 rteam 參數,計算 rTransferAmount = rAmount - rfee 得到的結果比實際結果要大,造成了 rToken 的增發。也就是說,因為這個計算問題,市場上實際流通的 rAmount 總和是要大于 rTotal 的值的。

此時,隨著用戶不斷交易,整個市場上流轉的總額就不斷增發。在代幣進行流轉的過程中以下兩個操作使得增發的代幣累積到 pair 合約中:
-
用戶自行在 pair 合約中進行 TomInu → WETH 的 swap 操作。
-
TomInu 代幣合約在執行
_transfer()函數的時候,會將本合約的代幣 swap 成 WETH。
可以通過下面的這個例子來說明增發部分確實隨著 rTransferAmount 發送給 to 地址。首先輸入 tAmount 數量為 1470,然后扣除手續費后的 tAmount 為 1352。

其次,event 事件中記錄的數額為 tTransferAmount ,這個值是減去了兩個手續費的,是計算正確的值。所以 event 事件中記錄的值 tAmount 1352 應該為 to 地址實際的收款金額。

但是由于增發部分(數值為 rteam)的 rAmount 隨著 rTransferAmount 發送給 to 地址,在 to 地址收到增發的 rTransferAmount 后,再計算得出的 tAmount 為 1465,要大于 event 事件中記錄的值 tAmount 1352。
為什么
在這個章節中,會對前面的暴言暴論進行解釋
- 為什么推文中的關于漏洞成因的結論是含糊的不準確的。
- 什么說關于 reflection token 攻擊事件的成因分析中提到“由于攻擊者 deliver 了一筆 token ,導致了 pair 中的 token 升值,從而能夠 skim 出更多的 token 進行獲利”的分析結論都是錯誤的。
- 為什么看到攻擊過程先 deliver 然后 skim 就完成獲利之后,我得出的結論是直接找 rToken增發代碼定位漏洞點。
首先我將舉幾個例子來模擬整個 deliver-skim 的過程,為了使得這個例子盡可能的簡單,這個過程中將不考慮任何手續費的收取。
場景1:
只有 attacker 和 pair 持有所有的 token
rTotal 1000, tTotal 100, rate 10
pair: rAmount 500, tAmount 50
attacker: rAmount 500, tAmount 50
attacker deliver 500 rAmount
rTotal 500, tTotal 100, rate 5
pair: rAmount 500, tAmount 100
attacker: rAmount 0, tAmount 0
此時,pair 的 tAmount 從 50 變成了 100。接下來 attacker 將調用 skim 來獲利了是不是?
attacker calls pair.skim()
rTotal 500, tTotal 100, rate 5
pair: rAmount 250, tAmount 50
attacker: rAmount 250, tAmount 50
attacker 如愿以償獲利了嗎?沒有,attacker 和 pair 又回到了最初的 50 tAmount,并不能通過這個操作來進行獲利。
場景2:
attacker, pair 以及一些其他用戶共同持有所有的 token
rTotal 1000, tTotal 100, rate 10
pair: rAmount 250, tAmount 25
attacker: rAmount 500, tAmount 50
others: rAmount 250, tAmount 25
attacker deliver 500 rAmount
rTotal 500, tTotal 100, rate 5
pair: rAmount 250, tAmount 50
attacker: rAmount 0, tAmount 0
others: rAmount 250, tAmount 50
attacker calls pair.skim()
rTotal 500, tTotal 100, rate 5
pair: rAmount 125, tAmount 25
attacker: rAmount 125, tAmount 25
others: rAmount 250, tAmount 50
pair 回到了原始的 25 tAmount,而 attacker 由原來的 50 虧損到了 25 tAmount。堅定持有的 others 由 25 上漲到了 50 tAmount。
通過上面的兩個例子,我們可以得出結論,只有當 attacker 和 pair 所持有的代幣份額合計 100% 的情況下,deliver-skim 的操作 attacker 才不會虧損。而兩者份額不足 100% 的情況下,deliver-skim 的操作反而會導致 attacker 遭受損失。也就是說 attacker 通過 deliver-skim 的操作無論怎么樣都是不賺的,最好的情況是 attacker 和 pair 所持有的代幣份額合計 100% 的情況下才不至于虧損。
那么…有沒有更好的情況呢?好到…兩者持有的代幣份額合計起來…超過100%?
比如,發生了代幣增發?
場景3:
由于代碼存在 rToken 相關的計算錯誤,導致代幣增發的發生,具體表現為 rToken 的實際流通量大于 rTotal 的數量。
rTotal 1000, tTotal 100, rate 10
pair: rAmount 400, tAmount 40
attacker: rAmount 800, tAmount 80
others: rAmount 400, tAmount 40
sum_rAmount = 1600 > rTotal = 1000
pair.rAmount + attacker.rAmount = 1200 > rTotal = 1000
attacker deliver 800 rAmount
rTotal 200, tTotal 100, rate 2
pair: rAmount 400, tAmount 200
attacker: rAmount 0, tAmount 0
others: rAmount 400, tAmount 200
attacker calls pair.skim()
rTotal 200, tTotal 100, rate 2
pair: rAmount 80, tAmount 40
attacker: rAmount 320, tAmount 160
others: rAmount 400, tAmount 200
至此,attacker 從原來的 80 tAmount,通過 deliver-skim 操作獲利達到 160 tAmount。
通過這個場景,也就可以解釋為什么看到攻擊過程中通過 deliver-skim 操作獲利時,首先想到的就是去找代碼中使得 rAmount 增發的計算操作。因為只有 rAmount 發生了增發,pair 和 attacker 的份額大于 100%,且增發部分需要留存在 pair 合約中,才能夠滿足通過 deliver-skim 操作進行獲利的基礎條件。
攻擊要求 pair 和 attacker 的份額大于 100%
在部署代幣合約的時候,_tTotal 設為 1733820000000000000000

在攻擊者 swap 獲取大量代幣時(進行 deliver 前),pair 中的 tAmount 為 316871513264115731249,attacker 中的 tAmount 為 1465904852700232013011

計算得出,此時 pair + attacker 的份額已經大于 100%,滿足攻擊要求。
>>> 316871513264115731249 + 1465904852700232013011
1782776365964347744260
>>> 1782776365964347744260 - 1733820000000000000000
48956365964347744260
>>> 1782776365964347744260 / 1733820000000000000000
1.0282361294507778
后記
在 TomInu 攻擊事件發生的4個月后,存在相同漏洞的 ADU token 再次被攻擊。
ADU token attack tx:https://explorer.phalcon.xyz/tx/bsc/0xc6f6b70e9e35770b699da9b60244d461d02db66859df42319c3207d76931423c
為什么會寫這篇文章,因為當我想對這些攻擊事件進行學習與分析的時候,我查看了網絡上的分析文章,他們給出的漏洞成因含糊不清毫無根據。我讀了很多篇分析文章,說辭都是大同小異地糊弄。還沒分析清楚就胡亂指點,最終被忽悠的就是真心想研究清楚的人。走了不少彎路,把彎路總結成這篇文章,感謝你的閱讀。
感謝某位不愿意透露ID的大佬對本文的指正,本文在大佬的指點下進行了二次修改,respect。

浙公網安備 33010602011771號