skim函数
// 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合约地址中多余的代币转到指定的地址
sync函数
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的值。
攻击者地址:0xcfc591db031b760961fe8943a183741ed7cd1f82
恶意合约地址:0x24cb6980995aeb7d5a9204e04b17dcd1e99a4694
攻击tx:0x04673c95054247588bb8380dbc7d361f08f8f0baa319366f48ad46e51d08422d
通过blocksec的phalcon工具来分析攻击的tx
https://phalcon.blocksec.com/tx/bsc/0x04673c95054247588bb8380dbc7d361f08f8f0baa319366f48ad46e51d08422d
攻击者通过部署恶意合约通过如下步骤完成套利攻击:
1.从dodo闪电贷借出2500wbnb。
2.在pancake中将其中1995个换成hackerdao,一共换取了10315个hackerdao,在swap前wbnb-hakcerdao地址的hackerdao的余额为11335
我们实际收到了9077个hackerdao,我们分析交易流程可以发现,412个被发送到了address(1),825个被发送到了0x465开头的地址。
然后看了下transfer方法,发现如果不在白名单中,转账有个fee,如果是收款地址为uniswapV2Pair(经过查询是hackerdao-usdt的lp合约地址)的话,那么fee由发送方出。
在bscscan上得到fee为12%,那么fee一共需要1237个hackerdao。
继续查看代码,发现fee分成了2个部分,4%发送到address(1)销毁,8%发送到0x465开头的poolAddress地址
3.将9077个hackerdao转账给hackerdao-wbnb的lp合约,转帐扣除fee后到账7988个
4.调用hackerdao-wbnb的lp合约的skim函数,将多余的7988个hackerdao转出到hackerdao-usdt的lp合约地址,因为目标地址是hackerdao-usdt的lp地址,因此调用skim函数所扣的fee是原本lp合约中的hackerdao余额,到账后还是7988个。
hackerdao-wbnb中的hackerdao减少,wbnb的数量不变,那么单个hackerdao的价格相当于上升了。
5.继续调用hackerdao-usdt的lp合约的skim函数,将多余的7988个hackerdao转回攻击者的恶意合约地址。扣除fee后实际收到7029个。
6.调用hackerdao-wbnb的lp合约的sync函数更新一下reserve,hackerdao的价格更新为上升后的。
7.然后调用pancake的swapExactTokensForTokensSupportingFeeOnTransferTokens,将全部hackerdao换成wbnb,一共换取了2170个wbnb,之前一共花费了1995个wbnb,因此这次攻击获利175个wbnb。
8.归还借来的2500个wbnb,提取获利的175个wbnb,攻击结束。