# Socket 业务逻辑漏洞 **Published by:** [Fuzzingq](https://paragraph.com/@liusy/) **Published on:** 2024-02-26 **URL:** https://paragraph.com/@liusy/socket ## Content 01/12/2024 socket gateway合约遭受攻击,漏洞合约地址: 0xCC5fDA5e3cA925bd0bb428C8b2669496eE43067e 漏洞原因: 合约0xCC5fDA5e3cA925bd0bb428C8b2669496eE43067e存在一个performAction函数,函数可以用于任意两个代币的swap交换,代码如下: function performAction( address fromToken, address toToken, uint256 amount, address receiverAddress, bytes32 metadata, bytes calldata swapExtraData ) external payable override returns (uint256) { uint256 _initialBalanceTokenOut; uint256 _finalBalanceTokenOut; // Swap Native to Wrapped Token if (fromToken == NATIVE_TOKEN_ADDRESS) { _initialBalanceTokenOut = ERC20(toToken).balanceOf(socketGateway); (bool success, ) = toToken.call{value: amount}(swapExtraData); if (!success) { revert SwapFailed(); } _finalBalanceTokenOut = ERC20(toToken).balanceOf(socketGateway); require( (_finalBalanceTokenOut - _initialBalanceTokenOut) == amount, "Invalid wrapper contract" ); // Send weth to user ERC20(toToken).transfer(receiverAddress, amount); } else { _initialBalanceTokenOut = address(socketGateway).balance; // Swap Wrapped Token To Native Token ERC20(fromToken).safeTransferFrom( msg.sender, socketGateway, amount ); (bool success, ) = fromToken.call(swapExtraData); if (!success) { revert SwapFailed(); } _finalBalanceTokenOut = address(socketGateway).balance; require( (_finalBalanceTokenOut - _initialBalanceTokenOut) == amount, "Invalid wrapper contract" ); // send ETH to the user payable(receiverAddress).transfer(amount); } emit SocketSwapTokens( fromToken, toToken, amount, amount, Identifier, receiverAddress, metadata ); return amount; } 可以看到这一行通过call理论上可以调用toToken的函数,切toToken地址由用户传入,可控: (bool success, ) = toToken.call{value: amount}(swapExtraData); 因此,当toToken为任意ERC20代币时,可以调用原生transferFrom方法,将用户approve给该合约的代币进行转移。只需要swapExtraData为abi-encode的transferFrom(address, address, uint256)并带入参数即可。 例如攻击者的这次调用: https://phalcon.blocksec.com/explorer/tx/eth/0xc6c3331fa8c2d30e1ef208424c08c039a89e510df2fb6ae31e5aa40722e28fd6?line=22&debugLine=22 其swapExtraData数据为:编码结果如下: 0x23b872dd00000000000000000000000038d2ca742b90ebd21dcceceb02ff6fd3d233b21e00000000000000000000000050df5a2217588772471b84adbbe4194a2ed39066000000000000000000000000000000000000000000000000000000407c809af0 同时,代码中的以下部分理论上对调用前后是否有代币转移作了限制:通过比较_finalBalanceTokenOut和_initialBalanceTokenOut值完成,但是很容易在amount=0时被绕过。 _initialBalanceTokenOut = ERC20(toToken).balanceOf(socketGateway); (bool success, ) = toToken.call{value: amount}(swapExtraData); if (!success) { revert SwapFailed(); } _finalBalanceTokenOut = ERC20(toToken).balanceOf(socketGateway); require( (_finalBalanceTokenOut - _initialBalanceTokenOut) == amount, "Invalid wrapper contract" ); ## Publication Information - [Fuzzingq](https://paragraph.com/@liusy/): Publication homepage - [All Posts](https://paragraph.com/@liusy/): More posts from this publication - [RSS Feed](https://api.paragraph.com/blogs/rss/@liusy): Subscribe to updates - [Twitter](https://twitter.com/_fuzzingq): Follow on Twitter