BIGFI代币通缩漏洞分析

说明

这也是一个典型的代币通缩类型的漏洞,之前前面也分析了多个代币通缩类型的漏洞,漏洞合约完全是一样的,本篇文章就简单分析下腺管漏洞合约。

基本信息

漏洞合约:https://bscscan.com/address/0xd3d4b46db01c006fb165879f343fc13174a1ceeb

攻击交易:https://bscscan.com/tx/0x9fe19093a62a7037d04617b3ac4fbf5cb2d75d8cb6057e7e1b3c75cbbd5a5adc

漏洞函数

balanceOf

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
uint256 private _tTotal;
uint256 private _rTotal;

function balanceOf(address account) public view override returns (uint256) {
if (_isExcluded[account]) return _tOwned[account];
return tokenFromReflection(_rOwned[account]);
}
function tokenFromReflection(uint256 rAmount) public view returns(uint256) {
require(rAmount <= _rTotal, "Amount must be less than total reflections");
uint256 currentRate = _getRate();
return rAmount.div(currentRate);
}

function _getRate() private view returns(uint256) {
(uint256 rSupply, uint256 tSupply) = _getCurrentSupply();
return rSupply.div(tSupply);
}

function _getCurrentSupply() private view returns(uint256, uint256) {
uint256 rSupply = _rTotal;
uint256 tSupply = _tTotal;
for (uint256 i = 0; i < _excluded.length; i++) {
if (_rOwned[_excluded[i]] > rSupply || _tOwned[_excluded[i]] > tSupply) return (_rTotal, _tTotal);
rSupply = rSupply.sub(_rOwned[_excluded[i]]);
tSupply = tSupply.sub(_tOwned[_excluded[i]]);
}
if (rSupply < _rTotal.div(_tTotal)) return (_rTotal, _tTotal);
return (rSupply, tSupply);
}

burn

1
2
3
4
5
6
7
8
9
10
function burn(uint256 _value) public {
_burn(msg.sender, _value);
}

function _burn(address _addr, uint256 _value) private {
require(_value <= _rOwned[_addr]);
_rOwned[_addr] = _rOwned[_addr].sub(_value);
_tTotal = _tTotal.sub(_value);
emit Transfer(_addr, dead, _value);
}

漏洞位置和之前没有任何的区别。所以就不作过多的分析,直接看攻击过程。

攻击分析

flashloan

通过aave3闪电贷获得200000个BUSD。

executeOperation

aave3闪电贷的回调函数。

BUSDToBIGFI

将闪电贷得到的BUSD全部兑换为BIGFI,得到BIGFI代币的数量是5869494994603961621184

burn

调用BIGFI中的burn()函数,用于修改_tTotal

实际攻击,调用burn()得到的数量是20999908387035301038233370

需要分析,是如何计算得到这个20999908387035301038233370值的。

在了解到具体的计算方法之前,需要了解到balanceOf()的计算方法。

中间的换算公式也非常的简单,在没有调用burn之前,计算balanceOf的公式如下:
$$
old_balanceOf[account] = \frac{r_owned[account]}{\frac{r_total}{r_total}}
= \frac{r_ownwd[account] \times t_total}{r_total}
$$
当调用burn之后,会执行_tTotal = _tTotal.sub(_value);,此时公式就变成:
$$
new_balanceOf[account] = \frac{r_ownwd[account] \times (t_total - _value)}{r_total}
$$

如果需要new_balanceOf[account]=1,需要计算此时的_value是多少,得到此时的t_total是:
$$
_value = t_total - \frac{t_total}{old_balanceOf[account]}
$$
代入到实际的计算公式,得到:
$$
burn_value = BIGFI.totalSupply() - \frac{BIGFI.totalSupply()}{BIGFI.balanceOf(address(Pair))}
$$
所以对应此时的攻击代码就是:

1
2
uint256 burnAmount = BIGFI.totalSupply() - 2 * (BIGFI.totalSupply() / BIGFI.balanceOf(address(Pair)));
BIGFI.burn(burnAmount);
sync

由于通过burn方法,修改了BIGFI.balanceOf(pair)的值,需要通过sync将更新后的值复写到Pair中。

此时在Pair中的BIGFI的数量已经成功变为了1

swap

由于此时在Pair合约中的两种代币的数量差距非常大,其中的BIGFI的数量只有1,此时攻击者通过swap将BIGFI兑换成为了USDT就可以大量获利。

最终攻击者通过兑换,获得了230466095832979875922923的BUSD。

最后就是完成闪电贷的还款。

获利分析

攻击者通过swap()获得了230466095832979875922923数量的BUSD,除去闪电贷的还款200160000000000000000000。最终攻击者获得了:

1
230466095832979875922923 - 200160000000000000000000 = 30306095832979875922923

所以攻击者最终获利是30306数量BUSD。

文章作者: Oooverflow
文章链接: https://www.oooverflow.com/2023/04/16/BIGFI-vuln/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 Oooverflow