{"content":{"title":"XSDWETHpool Hack Reply","body":"## Hack事件简介\r\n项目方在BSC上的XSD-WBNB 池子被黑客攻击, 攻击交易为:\r\nhttps://bscscan.com/tx/0xbdf76f22c41fe212f07e24ca7266d436ef4517dc1395077fabf8125ebe304442\r\n整个的攻击思路可以梳理如下:\r\n1. 从DODO中flashloan出4000 ether的WBNB\r\n2. 调用XSD项目方的router合约, 调用其swapXSDForETH方法, 将39,566.238265722260955438个XSD兑换成9.84 etehr的BNB\r\n3. 在fallback函数中, 直接调用XSD-WBNB的swap方法, 将3800 ether的BNB兑换成XSD\r\n4. 在fallback函数中, 调用PIDController的systemCalculations方法 (即: refreshPID)\r\n5. 在fallback函数中, 重入router的swapETHForXSD方法, 将0.00001 ether的BNB兑换成100 ether的XSD\r\n6. 然后利用重入后的router函数,直接burnPoolXSD, 将XSD-WBNB池子中的XSD burn掉3956623826572226095543, 此时池子中的XSD价格极度偏高\r\n7. 在router调用结束后, 将手中由3800 etherBNB swap得到的XSD, 全部dump到XSD-WBNB池子中, 得到3857 ether的BNB\r\n8. 偿还3800 ether的BNB的债务, 得到57 BNB的利润\r\n\r\n\r\n## 核心漏洞逻辑\r\nXSD-WBNB pool: 基本上是一个UniV2的仿盘\r\nrotuer中的swapXSDForETH函数中, \r\n1. 没有non-reentrant防止重入\r\n2. 存在XSD.burnpoolXSD的逻辑, 可以将池子中的XSD销毁, 从而拉偏池子的价格\r\n```js\r\n//approve router to use users xsd\r\n    //burn 10% of XSD when uXSD is +ve\r\n    function swapXSDForETH(uint amountOut, uint amountInMax)\r\n        external\r\n        override\r\n    {\r\n        require(!swap_paused, \"Swaps have been paused\");\r\n        (uint reserveA, uint reserveB, ) = IXSDWETHpool(XSDWETH_pool_address).getReserves();\r\n        uint amounts = BankXLibrary.quote(amountOut, reserveB, reserveA);\r\n        require(amounts <= amountInMax, 'BankXRouter: EXCESSIVE_INPUT_AMOUNT');\r\n        TransferHelper.safeTransferFrom(\r\n            xsd_address, msg.sender, XSDWETH_pool_address, amountInMax\r\n        );\r\n        XSDWETHpool(XSDWETH_pool_address).swap(0, amountOut, address(this));\r\n        //function will fail if conditions are not met\r\n        //XSDWETHpool(XSDWETH_pool_address).flush();\r\n        IWBNB(WETH).withdraw(amountOut);\r\n        TransferHelper.safeTransferETH(msg.sender, amountOut);\r\n        //burn xsd here \r\n        //value of xsd liquidity pool has to be greater than 20% of the total xsd value\r\n        if(XSD.totalSupply()-CollateralPool(payable(collateral_pool_address)).collat_XSD()>amountOut/10 && !pid_controller.bucket1()){\r\n            XSD.burnpoolXSD(amountInMax/10);\r\n        }\r\n        refreshPID();\r\n    }\r\n```\r\n### 疑惑点\r\nrefershPID()里面的逻辑是什么\r\n```js\r\nfunction refreshPID() internal{\r\n        if(block.timestamp>(last_called+pid_cooldown)){\r\n            pid_controller.systemCalculations();\r\n            last_called = block.timestamp;\r\n        }\r\n    }\r\n```\r\npid_controller: https://bscscan.com/address/0x82a6405B9C38Eb1d012c7B06642dcb3D7792981B#code\r\n初步看, systemCalculations函数里:\r\n1. 通过XSD-WBNB pool的reserve0/ reserve1 算出 xsd的price, 即spot price\r\n2. 根据xsd的price更新xsd的抵押率\r\n\r\n通过拉高xsd的spot price改变collat_xsd的值, 使其能通过if条件的检查.\r\n\r\n## 思路整理\r\nrouter的swapXSDForETH函数中,存在XSD.burnpoolXSD的逻辑, 也就意味着可以通过这个函数去额外的burn掉池子中的XSD, 从而拉偏XSD的价格.\r\n最简单的思路是 swapETHforXSD得到XSD, 然后burn掉池子中的XSD, 然后sync, 最后把XSD dump到池子里swap成ETH.\r\n这里的挑战是burn池子的XSD这个逻辑在swapXSDForETH中, 希望能够burn掉尽可能多的XSD,但是不通过swap移动曲线.\r\n幸运的是, swapXSDForETH中, 首先完成swap, 然后把ETH打出, 最后才是burn XSD. 我们可以在ETH打出后的fallback函数里, 将简单思路的逻辑插入执行.\r\n于是整体思路变成:\r\n1. 调用router的swapXSDForETH\r\n2. 在ETH的fallback中, swapETHForXSD, 拉高XSD的价格\r\n3. 继续执行router的swapXSDForETH逻辑, burn掉pool里的XSD\r\n4. 在外侧dump XSD for ETH获利.\r\n\r\n这里为了能够顺利进入到burn逻辑里, 在fallback函数中额外执行了pid_controller.systemCalculations();操作."},"author":{"user":"https://learnblockchain.cn/people/3295","address":null},"history":"QmU8t8pqhuefC45EyUYmHkbZFN4EtCcSWacQvwEVU2igt6","timestamp":1696408550,"version":1}