【漏洞分析】ReflectionToken BEVO代币攻击事件分析
前言
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增大就获得了套利机会(其实赚不了)。然后我按照文章中的思路计算了两天死活就算不出来盈利(就让我多吐槽几句吧因为我真的手算了好多页纸硬是没算出来)。所以自己摸索着搞了一份分析,因为小老弟也是在学习的过程中,所以文章有可能有错的地方,还请各位师傅多多指点。感谢阅读!