AES价格被操纵攻击事件分析
// force balances to match reserves
function skim(address to) external lock {
address _token0 = token0; // gas savings
address _token1 = token1; // gas savings
_safeTransfer(_token0, to, IERC20(_token0).balanceOf(address(this)).sub(reserve0));
_safeTransfer(_token1, to, IERC20(_token1).balanceOf(address(this)).sub(reserve1));
}
该函数的作用是提取lp合约地址中多余的代币转到指定的地址
function sync() external lock {
_update(IERC20(token0).balanceOf(address(this)), IERC20(token1).balanceOf(address(this)), reserve0, reserve1);
}
// update reserves and, on the first call per block, price accumulators
function _update(uint balance0, uint balance1, uint112 _reserve0, uint112 _reserve1) private {
require(balance0 <= uint112(-1) && balance1 <= uint112(-1), 'Pancake: OVERFLOW');
uint32 blockTimestamp = uint32(block.timestamp % 2**32);
uint32 timeElapsed = blockTimestamp - blockTimestampLast; // overflow is desired
if (timeElapsed > 0 && _reserve0 != 0 && _reserve1 != 0) {
// * never overflows, and + overflow is desired
price0CumulativeLast += uint(UQ112x112.encode(_reserve1).uqdiv(_reserve0)) * timeElapsed;
price1CumulativeLast += uint(UQ112x112.encode(_reserve0).uqdiv(_reserve1)) * timeElapsed;
}
reserve0 = uint112(balance0);
reserve1 = uint112(balance1);
blockTimestampLast = blockTimestamp;
emit Sync(reserve0, reserve1);
}
该函数的作用是更新reserve为balance的值。
攻击者地址:0x286e09932b8d096cba3423d12965042736b8f850
恶意合约地址:0x3cdfbee6ec194e5258d2f9557e2e41015fc8b6e8
攻击tx:0xca4d0d24aa448329b7d4eb81be653224a59e7b081fc7a1c9aad59c5a38d0ae19
通过blocksec的phalcon工具来分析攻击的tx: https://phalcon.blocksec.com/tx/bsc/0xca4d0d24aa448329b7d4eb81be653224a59e7b081fc7a1c9aad59c5a38d0ae19
1.从dodo通过闪电贷借出836944 USDT
2.将其中的100000 USDT换成了AES,按照价格可以兑换2421667 AES,但是其中有72650发送到了address(0),最终获得2179500 AES。
我们查看一下transfer方法
一共有四种情况:
(1)如果from是代币合约地址,to是uniswapV2Pair(经过查询是AES-USDT),那么调用super._transfer函数转帐。super.方法名 是调用最近的父合约的方法。查看父合约的_transfer方法后,发现就是一个普通的转帐,没有任何其他费用。
(2)如果发起转帐的地址在automatedMarketMakerPairs数组中,那么调用buyTokenAndFees函数:
转帐数量的3%为burnAmount 从from地址的余额中扣除,这部分通过父函数的_burn方法发送到address(0)销毁。
转帐数量的1%添加到swapFeeTotal这个状态变量中。
转帐数量的10%为feeAmount 从转帐数量中扣除。
最终接收方收到的数量为转帐数量的90%,发送方的AES余额减少转帐数量的3%。
(3)如果接收转帐的地址在automatedMarketMakerPairs数组中,那么调用sellTokenAndFees函数:
转帐数量的3%从转帐数量中扣除。
转帐数量的1%添加到swapFeeTotal这个状态变量中。
转帐数量的3%为burnAmount 从from地址的余额中扣除,这部分通过父函数的_burn方法发送到address(0)销毁。
最终接收方收到转帐数量的97%
(4)除上面3种情况外的其他情况,都直接调用父函数的_transfer方法,不需要额外的手续费等。
100000 USDT换成了AES这个过程中,AES的from地址为AES-USDT的地址(0x40ed17221b3b2d8455f4f1a05cac6b77c5f707e3),该地址在automatedMarketMakerPairs数组中,因此转帐过程是上述第(2)种情况
3.攻击者将兑换得到的其中1089750个AES发送到AES-USDT地址,触发transferr中的第三种情况,AES-USDT地址收到1057057个AES。
4.攻击者调用skim方法,to地址设为AES-USDT地址,因为from地址为AES-USDT地址,在automatedMarketMakerPairs数组中,因此触发transfer方法中的第(2)种情况,在这种情况下AES-USDT地址的AES代币会被销毁转帐数量的3%。
5.攻击者又多次调用了skim方法,to地址设置为AES-USDT地址,导致AES-USDT地址的AES代币数量逐渐减少。
6.攻击者调用distributeFee方法,将AES-USDT地址的swapFeeTotal转出pair合约,该方法直接调用的父合约的_transfer方法,因此不存在额外的手续费。
7.攻击者调用sync方法来更新reserve的值,AES-USDT地址的AES只剩20246个了,因此每个AES代币的价格都提高了很多。
8.攻击者将139794个AES兑换为161608 USDT,购买AES花费100000 USDT,卖出AES获得161608 USDT,获利61608 USDT。
9.将闪电贷借来的USDT归还,攻击结束。
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!