Defi安全--价格操纵攻击事件分析

1. SellToken02攻击事件

2023年5月13日,发生在BSC链上的价格操纵攻击

(1)phalcon调用序列分析

image-20240502123755653

  • 攻击者调用对应攻击合约的0x74ff2dff攻击函数,传入对应的参数
  • 先通过DPPOracle调用对应的闪电贷,回调函数中继续调用闪电贷,并且接着调用,调用了三次闪电贷函数,分别在不同的地方借的,借的都是WBNB。
  • 进入最后一个回调函数,看看干了些啥

image-20240502124621471

  • 看到攻击者先调用PancakeRouter的交换代币函数,将对应400个WBNB换成对应数量400多万的SellToken代币,并且将闪电贷剩下的WBNB全部转到自己的账上
  • Short、balanceOf等函数,查看对应的状态值,应该没啥问题
  • 攻击者调用SellToken: Router v2ShortStart()函数,用13.37个WBNB去short减少SellTokenrouter中Selltoken的数量,看一下ShortStart()函数的源码:
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
30
31
32
33
34
35
36
37
38
39
40
41
42
43
function ShortStart(address coin,address addr,uint terrace)payable public {
address bnbOrUsdt=mkt.getPair(coin);
require(terraces[terrace]!=address(0) && tokenPrice[addr][coin] > 0);
require(coin != address(0));
require(bnbOrUsdt == _WBNB || bnbOrUsdt==_USDT);
require(!getNewTokenPrice(addr,coin,bnbOrUsdt) && block.timestamp > tokenPriceTime[addr][coin]);
uint bnb=msg.value;
uint tos=getToken2Price(coin,bnbOrUsdt,mkt.balanceOf(coin))/10;
require(Short[addr][coin].bnb+bnb <= tos);
Short[addr][coin].token=bnbOrUsdt;
Short[addr][coin].coin=coin;
Short[addr][coin].bnb+=bnb*98/100;
tokenPrice[addr][coin]=0;
uint newTokenValue=getTokenPrice(coin,bnbOrUsdt,bnb*98/100);
Short[addr][coin].tokenPrice+=newTokenValue;
Short[addr][coin].time=block.timestamp;
address[] memory add=mySells[addr].coin;
bool isCoin;
for(uint i=0;i<add.length;i++){
if(add[i]==coin){
isCoin=true;
}
}
if(!isCoin){
mySells[addr].mnu++;
mySells[addr].coin.push(coin);
}
sum+=bnb;
payable(mkt).transfer(bnb*97/100);
if(bnbOrUsdt ==_USDT){
uint usdts=IERC20(_USDT).balanceOf(address(mkt));
mkt.buy(_WBNB,_USDT,bnb*97/100);
if(IERC20(_USDT).balanceOf(address(mkt))>usdts){
uint ut=IERC20(_USDT).balanceOf(address(mkt))-usdts;
mkt.buy(_USDT,coin,ut);
}
}else{
mkt.buy(bnbOrUsdt,coin,bnb*97/100);
}
payable (owner()).transfer(bnb*2/100);
payable (terraces[terrace]).transfer(bnb/100);
}
//通过getToken2Price()和getTokenPrice()函数获得对应代币的价格,后面细看
  • 接下来攻击者调用Pancakeswap Router的swapExactETHForTokensSupportingFeeOnTransferTokens()函数去操纵SELLC Token的价格,这里攻击者用大量的SELLC去兑换WBNB,看一下对应的代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
function getAmountsOut(address factory, uint amountIn, address[] memory path) internal view returns (uint[] memory amounts) {
require(path.length >= 2, 'PancakeLibrary: INVALID_PATH');
amounts = new uint[](path.length);
amounts[0] = amountIn;
for (uint i; i < path.length - 1; i++) {
(uint reserveIn, uint reserveOut) = getReserves(factory, path[i], path[i + 1]);
amounts[i + 1] = getAmountOut(amounts[i], reserveIn, reserveOut);
}
}
// given an input amount of an asset and pair reserves, returns the maximum output amount of the other asset
function getAmountOut(uint amountIn, uint reserveIn, uint reserveOut) internal pure returns (uint amountOut) {
require(amountIn > 0, 'PancakeLibrary: INSUFFICIENT_INPUT_AMOUNT');
require(reserveIn > 0 && reserveOut > 0, 'PancakeLibrary: INSUFFICIENT_LIQUIDITY');
uint amountInWithFee = amountIn.mul(9975);
uint numerator = amountInWithFee.mul(reserveOut);
uint denominator = reserveIn.mul(10000).add(amountInWithFee);
amountOut = numerator / denominator;
}
  • 这里可以看到,直接通过自动做市商对应的价格攻击计算相应的代币价格,可以兑换更多的WBNB。