Socket Gateway - 代币交换逻辑漏洞
字数 1831 2025-08-18 11:35:59
Socket Gateway 代币交换逻辑漏洞分析报告
1. 事件概述
2024年1月17日,Socket Gateway智能合约由于performAction函数中存在逻辑漏洞,导致约330万美元的损失。攻击者利用该漏洞转移了用户的USDC、USDT、WETH、WBTC、DAI和MATIC等代币,并将其转换为ETH获利。
2. 关键地址信息
- 攻击者地址: 0x50DF5a2217588772471B84aDBbe4194A2Ed39066
- 被攻击合约地址: 0x3a23F943181408EAC424116Af7b7790c94Cb97a5
- 漏洞合约地址: 0xCC5fDA5e3cA925bd0bb428C8b2669496eE43067e
- 攻击交易示例: 0xc6c3331fa8c2d30e1ef208424c08c039a89e510df2fb6ae31e5aa40722e28fd6
- 攻击合约地址: 0xf2D5951bB0A4d14BdcC37b66f919f9A1009C05d1
3. 漏洞背景
Socket Gateway的Admin在1月13日执行了addRoute函数添加了新的routeAddress。新部署的合约中的performAction函数存在逻辑错误,该函数原本设计用于ETH与WETH的代币交换。
4. 漏洞代码分析
4.1 漏洞函数代码
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;
}
4.2 正常功能逻辑
该函数设计用于ETH与WETH的转换,主要功能路径:
-
ETH转WETH:
- 检查
fromToken是否为原生代币地址 - 记录初始WETH余额
- 执行WETH合约调用,转入ETH
- 验证WETH余额增加量等于转入的ETH数量
- 将WETH转给接收者
- 检查
-
WETH转ETH:
- 记录初始ETH余额
- 从调用者转移WETH到合约
- 执行WETH合约调用,提取ETH
- 验证ETH余额增加量等于提取的WETH数量
- 将ETH转给接收者
4.3 漏洞点分析
漏洞存在于以下关键点:
-
amount参数为0时的验证绕过:
- 当
amount为0时,(_finalBalanceTokenOut - _initialBalanceTokenOut) == amount恒成立 - 这使得余额验证检查被绕过
- 当
-
swapExtraData参数可控:
- 攻击者可以完全控制
swapExtraData参数 - 通过构造恶意的
swapExtraData,可以调用任意代币的transferFrom函数
- 攻击者可以完全控制
-
fromToken参数未严格验证:
- 函数没有严格限制
fromToken必须为WETH地址 - 允许传入任意代币地址
- 函数没有严格限制
5. 攻击流程详解
-
攻击准备:
- 攻击者创建2个攻击合约
- 识别出
performAction函数的漏洞
-
攻击执行:
- 调用
performAction函数,设置amount为0 - 设置
fromToken为受害者已授权的代币地址(如USDC) - 构造
swapExtraData为调用该代币transferFrom函数的数据 - 由于
amount为0,余额验证检查被绕过 - 恶意
transferFrom调用被执行,转移受害者授权的代币
- 调用
-
资金转移:
- 攻击者将获取的USDC、USDT、WETH、WBTC、DAI、MATIC等代币全部转换为ETH
- 最终获利约330万美元
6. 漏洞修复建议
-
严格验证输入参数:
require(amount > 0, "Amount must be greater than zero"); require(fromToken == NATIVE_TOKEN_ADDRESS || fromToken == WETH_ADDRESS, "Invalid token"); require(toToken == NATIVE_TOKEN_ADDRESS || toToken == WETH_ADDRESS, "Invalid token"); -
限制swapExtraData的使用:
- 对
swapExtraData进行严格格式验证 - 或完全移除该参数,改为固定调用已知安全的函数
- 对
-
添加权限控制:
modifier onlyAuthorized() { require(msg.sender == authorizedRouter, "Unauthorized"); _; } -
改进余额验证逻辑:
- 即使
amount为0也应进行严格验证 - 可以考虑添加最小交换量限制
- 即使
7. 安全开发最佳实践
-
输入验证:
- 对所有外部输入进行严格验证
- 特别是涉及资金操作的参数
-
边界条件测试:
- 特别关注0值、最大值等边界条件
- 编写全面的单元测试覆盖所有边界情况
-
最小权限原则:
- 合约函数应遵循最小权限原则
- 避免过度灵活的接口设计
-
代码审计:
- 在部署前进行专业的安全审计
- 特别是涉及资金操作的合约
8. 总结
Socket Gateway漏洞展示了智能合约开发中几个关键的安全问题:
- 边界条件处理不足
- 过度灵活的接口设计
- 输入验证不严格
- 权限控制缺失
开发者应从该事件中吸取教训,在设计和实现智能合约时遵循安全最佳实践,特别是在处理代币转移等敏感操作时。