# Socket 业务逻辑漏洞

By [Fuzzingq](https://paragraph.com/@liusy) · 2024-02-26

---

### 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](https://phalcon.blocksec.com/explorer/tx/eth/0xc6c3331fa8c2d30e1ef208424c08c039a89e510df2fb6ae31e5aa40722e28fd6?line=22&debugLine=22)

![](https://storage.googleapis.com/papyrus_images/8bf2b386fe048eb48d249cca4c8dbd96a616c87193767f74c8c9aae0164fe0ed.png)

其swapExtraData数据为：编码结果如下：

0x23b872dd00000000000000000000000038d2ca742b90ebd21dcceceb02ff6fd3d233b21e00000000000000000000000050df5a2217588772471b84adbbe4194a2ed39066000000000000000000000000000000000000000000000000000000407c809af0

![](https://storage.googleapis.com/papyrus_images/f93b10d546f0d0ab4a8b17dc2ade3221eb64e1b26abb3f31e6b823c8468ed31f.png)

同时，代码中的以下部分理论上对调用前后是否有代币转移作了限制：通过比较\_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"
                );

---

*Originally published on [Fuzzingq](https://paragraph.com/@liusy/socket)*
