2026年1月25日,我们在以太坊、Arbitrum、Base和BSC上检测到一系列针对SwapNet和Aperture Finance部署的受害者合约的可疑交易,总损失超过1700万美元。总体而言,这两个事件的根本原因很简单,并且已经在我们最初的警报中进行了概述 [1, 2]: 受害者合约由于输入验证不足而暴露了任意调用能力,允许攻击者滥用现有的代币批准并调用transferFrom来耗尽资产。
然而,这两组受害者合约都是闭源的,一旦反编译,就会扩展到数千行代码,并具有深度嵌套和复杂的逻辑分支,大大增加了分析的难度。此外,受影响项目发布的后续事后分析 [3, 4] 主要关注修复和恢复,对底层技术细节的讨论有限。因此,一些重要的问题仍未得到解答,包括易受攻击的调用路径是如何构建的,以及为什么现有的检查未能阻止漏洞利用。
在本报告中,我们将基于反编译的字节码和链上执行跟踪提供更详细的技术分析。虽然缺乏源代码限制了可见性,但字节码级别的分析足以重建易受攻击的逻辑,并揭示了关于合约设计的一些有趣的观察结果,这些观察结果在高级警报中并不显而易见。
我们将首先深入分析SwapNet事件,然后详细分析Aperture Finance事件。
SwapNet事件
背景
SwapNet [5] 是一个DEX聚合器,旨在通过聚合来自多个链上来源(包括AMM和私人做市商)的流动性来寻找最佳交易路线。该协议还允许用户在执行交易时指定自定义路由器或池,提供额外的灵活性。
根本原因分析
此事件源于对用户提供输入的验证不足,这允许攻击者触发带有任意参数的transferFrom()调用。因此,先前已批准给受害者合约(例如,0x616000e384Ef1C2B52f5f3A88D57a3B64F23757e)的资产可以转移给攻击者。
根据反编译的字节码,0x87395540()函数似乎缺乏对关键输入的适当验证。通过将预期的路由器或池地址替换为代币地址(例如,USDC),合约会错误地将代币视为有效的执行目标。这导致使用攻击者控制的calldata执行低级调用。
因此,受害者合约执行形如:approvedAsset.transferFrom(victim, attacker, amount)的调用,允许攻击者窃取所有已批准的资产。
攻击流程
观察到针对SwapNet的多起攻击。在此,我们以Base上的交易0xc15df1d131e98d24aa0f107a67e33e66cf2ea27903338cc437a3665b6404dd57为例。
攻击者简单地调用了受害者合约的0x87395540()函数,并使用了恶意输入。此调用包含两个主要步骤。
- 一个关键的内部变量(例如,
v51)被设置为USDC,绕过了预期的路由逻辑。
- 使用攻击者控制的calldata执行了一个低级调用,导致调用
USDC.transferFrom()并耗尽所有已批准的USDC。
损失汇总、交易和受影响的合约
SwapNet事件估计造成跨多个链的总损失约为1341万美元。下表总结了主要的利用交易和涉及的受害者合约地址。
Aperture Finance事件
背景
Aperture Finance [6] 是一个DeFi协议,代表用户管理集中式流动性头寸,如Uniswap V3 LP。其闭源合约(例如,0xD83d960deBEC397fB149b51F8F37DD3B5CFA8913)允许用户使用原生代币铸造和管理Uniswap V3头寸。
铸造UniswapV3头寸的预期工作流程
当通过0x67b34120()函数铸造Uniswap V3头寸时,合约遵循预期的三步工作流程:
-
包装原生代币
-
通过内部函数
0x1d33()兑换原生代币 -
铸造UniswapV3头寸
问题出现在第二步:0x1d33()通过低级调用执行自定义交易,其中关键参数(例如,调用目标和calldata)似乎由用户控制且未经充分验证,从而导致了意外的外部调用。后续部分提供了更多细节。
根本原因分析
与SwapNet案例类似,Aperture Finance事件是由低级调用上的输入验证不足引起的。当调用0x67b34120()时,内部函数0x1d33()使用用户提供的calldata执行低级调用,而没有强制执行对调用目标或函数选择器的严格限制。
如下图所示,用于触发低级调用的calldata完全基于攻击者的输入。
这使得攻击者能够构建恶意calldata,从而导致在受害者合约的上下文中执行:approvedToken.transferFrom(victim, attacker, amount)。因此,不仅ERC20代币,而且已批准的Uniswap V3头寸NFT也可以被窃取。
攻击流程
观察到针对Aperture Finance的多起攻击。在本节中,我们以以太坊交易0x8f28a7f604f1b3890c2275eec54cd7deb40935183a856074c0a06e4b5f72f25a为例。
-
攻击者创建了一个攻击合约
0x5c92884dFE0795db5ee095E68414d6aaBf398130。 -
攻击合约调用了
0x67b34120()函数,并使用了恶意输入和100 wei的ETH(即msg.value==100)。- a) 原生ETH通过
WETH.deposit()函数被包装成WETH。
- b) 调用内部函数
0x1d33()执行低级调用。在此步骤中,在受害者合约的上下文中调用了WBTC.transferFrom(victim, attacker, amount),允许攻击者窃取已批准的代币。值得注意的是,在0x1d33()函数末尾通过了余额检查。具体来说,0x1d33()函数将余额变化与攻击者指定的交易输出值(即varg2.word2)进行了比较。因此,交易成功执行,但并未收到任何东西。
- a) 原生ETH通过
-
最后,调用
NonfungiblePositionManager.mint()函数,使用100 wei的WETH为攻击者铸造了一个头寸。
有趣的发现
通过比较正常和异常的铸造交易,我们发现批准给同一被批准者的代币(例如,OKX DEX: TokenApprove)但指定了不同的路由器地址(即DexRouter和WBTC)。这表明该合约可能强制执行对批准被批准者的验证,但未能验证实际的执行目标,留下了一个可以通过任意调用利用的关键漏洞。
损失汇总、交易和受影响的合约
Aperture Finance事件估计造成跨多个链的总损失约为367万美元。下表总结了主要的利用交易和相关的受害者合约地址。
结论
尽管SwapNet和Aperture Finance事件影响了不同的协议和链,但这两个事件的根本原因并不复杂:用户控制的低级调用与持有代币批准的合约中的输入验证不足相结合。这些事件提醒我们,在合同设计中的灵活性必须与严格的调用约束仔细权衡,尤其是在外部审查受限的闭源系统中。
参考
[1] https://x.com/Phalcon_xyz/status/2015614087443697738
[2] https://x.com/Phalcon_xyz/status/2015624519898234997
[3] https://meta.matcha.xyz/SwapNet-Incident-Post-Mortem
[4] https://x.com/ApertureFinance/status/2015938720453820752
[6] https://x.com/ApertureFinance
关于BlockSec
BlockSec是一家全栈区块链安全与加密合规提供商。我们构建产品和服务,帮助客户在协议和平台的整个生命周期内进行代码审计(包括智能合约、区块链和钱包)、实时拦截攻击、分析事件、追踪非法资金,并满足AML/CFT要求。
BlockSec在著名会议上发表了多篇区块链安全论文,报告了多个DeFi应用的零日攻击,阻止了多次黑客攻击并挽回了超过2000万美元的损失,并保护了数十亿美元的加密货币。
-
官方Twitter账号:https://twitter.com/BlockSecTeam



