尧图精选

Sonne Finance被攻击:Compound v2分叉中的未注册市场漏洞解析

🕒 发布时间:2026/9/10 7:33:40 📁 来源:尧图网络
最近安全圈的朋友应该都被Sonne Finance被攻击的消息刷屏了。作为Compound v2分叉家族又一起典型的“影子市场”漏洞案例这次攻击在Optimism上直接造成约2000万美元的损失攻击手法非常典型几乎可以说把分叉协议里“未注册市场”这个坑完美踩了一遍。我花了两天时间翻完了公开的审计报告、链上数据和攻击交易记录决定把整个事件从代码底层到资金链路完整拆一遍希望能给做DeFi开发、审计或者单纯对链上安全感兴趣的朋友一些参考。这个案例的核心价值在于它不是那种复杂到看不懂的数学漏洞也不是需要深厚密码学背景才能理解的问题而是一个纯粹的“架构级疏忽”——一个市场被部署出来却没有被注册到协议的registry里导致整个清算和流动性检查机制对它完全失明。这类问题在分叉项目中其实非常常见只是严重程度不同而已。1. Sonne Finance一个标准的Compound v2分叉项目1.1 借贷协议基础Compound v2分叉的典型架构要理解这个漏洞得先弄清楚Sonne Finance的技术底座。Sonne是一个部署在Optimism和Base上的去中心化借贷协议核心逻辑几乎是从Compound v2分叉来的。在Compound v2的架构中系统有几个核心组件Comptroller控制器负责风险参数管理、市场注册、账户流动性检查。CToken借贷市场代币每个市场对应一个cToken合约比如cUSDC、cETH。Price Oracle价格预言机用于获取底层资产的价格通常是Chainlink聚合器或者自定义的桥接合约。在Compound v2的正常运行逻辑里每个新市场必须经过治理投票然后由管理员调用Comptroller._supportMarket()将市场注册到控制器中。只有注册过的市场才会被纳入账户流动性的计算范围。这个流程保证了当用户借款时系统能完整地计算其所有资产和负债确保抵押率健康。Sonne Finance基本沿用了这套架构但它在Optimism上部署时多了一个特殊的存在——SOMM市场。SOMM是Sonne自己的治理代币项目方为了让SOMM有借贷场景部署了一个SOMM的cToken市场。但问题就在这里这个SOMM市场虽然被部署出来了却始终没有被正式注册到Comptroller的市场列表中。1.2 攻击事件概述时间、损失、影响范围2024年5月15日攻击者通过一系列精心构造的交易从Sonne Finance在Optimism上的协议中盗取了约2000万美元的加密资产。主要被抽走的是USDC等稳定币和WETH攻击完成后协议立即暂停但资金已经无法追回。根据链上数据回溯攻击者的整个操作在高频交易下持续了不到二十分钟。这次攻击的影响范围主要集中在Optimism部署。Base上的Sonne实例并未受影响因为Base上虽然也有SOMM市场但它被正确注册到了Comptroller中。这个细节很关键同一个项目同样的代码只是一个注册遗漏的差别结果天壤之别。2. 漏洞根源未注册市场为何会让协议失明2.1 从代码层面看getAccountLiquidity的盲区现在我们从代码层面理解这个漏洞的根源。Compound v2的账户流动性检查函数getAccountLiquidity的核心逻辑是遍历该账户涉及的所有市场资产并计算总额function getAccountLiquidity(address account) public view returns (uint, uint, uint) { uint error; uint tokens; uint shortfall; uint liquidity; uint balance; uint borrowValue; uint collateralValue; // 遍历所有已注册市场 address[] memory assets assetsIn[account]; for (uint i 0; i assets.length; i) { address asset assets[i]; (error, balance, borrowValue, collateralValue) getAccountLiquidityInternal(asset, account); if (error ! 0) return (error, 0, 0); tokens tokens.add(balance); collateralValue collateralValue.add(collateralValue); borrowValue borrowValue.add(borrowValue); } liquidity collateralValue.sub(borrowValue); shortfall borrowValue.sub(collateralValue); return (error, liquidity, shortfall); }问题一目了然这个函数只会遍历assetsIn[account]数组中记录的市场而assetsIn数组是在用户进入市场enterMarkets时由Comptroller根据已注册市场列表来填充的。换句话说如果SOMM市场压根就不在Comptroller的markets映射里那么无论用户在这个市场里借了多少钱只要没有主动调用enterMarkets把SOMM市场加进来这笔债务在计算账户流动性时就是完全透明的。当然有人会问攻击者能不能主动调用enterMarkets把SOMM加进去答案是可以但调用后Comptroller会检查markets[sommCToken].isListed由于SOMM市场未注册isListed为false调用会直接失败。所以攻击者根本没有办法“告诉”协议自己在这个市场里有债务。2.2 为什么清算机制会彻底失效清算机制是整个借贷协议的“安全网”。正常情况下当用户抵押率跌破清算阈值时任何第三方都可以调用liquidateBorrow来代为偿还部分债务并拿走抵押品作为奖励。这个清算动作依赖的正是getAccountLiquidity来计算借款人的shortfall缺口金额。当SOMM市场未注册时攻击者的策略变得异常简单在SOMM市场借入大额资产这些债务完全不参与任何流动性计算因此攻击者的账户永远不会有shortfall清算人根本无法触发清算。这相当于攻击者在一个无人看管的金库里搬钱而警报系统只盯着隔壁的金库。这个问题的本质是协议的“状态记录”与“实际存在”发生了脱节。SOMM市场作为一个合约真实存在、真实运行用户确实可以往里存钱、借款但协议的风险控制系统却对这个市场一无所知。这种“影子市场”带来的账实不符是分叉协议中最隐蔽也最致命的问题之一。3. 攻击全过程复盘三个关键阶段3.1 第一幕在影子市场中悄悄积累债务攻击的第一步是利用SOMM市场的存在在不触发协议警报的情况下借出大量资产。根据链上分析攻击者先通过一个无抵押的新地址向SOMM市场存入了一小部分抵押品然后开始借出SOMM代币。具体来说攻击者利用flash loan从Base上的Sonne实例或其它去中心化交易平台获取了大量SOMM代币然后在Optimism的Sonne协议上向SOMM市场注入流动性。由于SOMM市场未注册借款时的抵押率检查被完全绕过——borrowAllowed中的getHypotheticalAccountLiquidity检查虽然会被调用但在遍历资产列表时根本找不到SOMM市场所以它认为这笔借款操作不会产生任何风险。这一步是整个攻击的关键跳板。如果没有这个未注册的SOMM市场攻击者在普通市场里借款必然要满足抵押率要求也就无法凭空创造出巨额债务。3.2 第二幕抵押品操纵与闪电贷的完美配合在影子市场中积累了大量借款额度后攻击者还需要把这些债务“变现”成真正的资产。这里分为两步第一步是借出资产。攻击者在SOMM市场中借出了大量SOMM代币然后在去中心化交易所上将SOMM代币抛售换取ETH。这个操作本身就会对SOMM代币的价格造成冲击但由于SOMM在Sonne上的价格预言机来自一个比较薄弱的流动性池攻击者甚至可以利用这种价格冲击进一步压低估资产价格。第二步是进入正常市场。攻击者在USDC等主流市场中存入一笔抵押品可能是从其它渠道借来的然后借出USDC和WETH。由于SOMM市场中的巨额债务没有计入账户总负债攻击者在正常市场中的抵押率看起来非常健康协议允许他借出大量资产。这一阶段用到的一个经典技巧是价格预言机操纵。Sonne的SOMM价格来自Uniswap V3的流动性池攻击者通过闪电贷向这个池子注入大量资金从而改变SOMM的瞬时价格。后续的借款和清算计算都会使用这个被操纵的价格进一步放大了攻击者的盈利空间。3.3 第三幕资金提取与链上追踪攻击者在同一笔交易中完成了从借款到资产提取的全流程。整个攻击交易非常紧凑大量操作依赖flash loan在同一个区块内完成。链上数据显示攻击者最终提取了约6500枚ETH和大量USDC总价值约2000万美元。攻击完成后Sonne Finance迅速暂停了协议并对攻击地址进行了标记。部分被盗资金被转移到多个地址进行混币操作但大部分资金在数个小时内就被转移到了多个交易所。目前该项目已经提出了对攻击者的赏金方案但资金追回的可能性不大。这次的攻击手法本质上不算复杂为什么能成功正是因为漏洞位于系统架构的“盲区”而攻击者精准地找到了这个盲区并加以利用。4. 修复方案与Compound v2分叉的通用风险4.1 官方修复暂停、注册、隔离Sonne Finance在攻击发生后立即暂停了所有市场操作这是止损最快的手段。随后团队紧急提交修复方案核心逻辑是在Comptroller中将SOMM市场正式注册并设置合理的风险参数。但这里暴露了另一个问题虽然注册市场看起来简单但一旦SOMM市场价格被严重操纵注册后反而可能让攻击者通过另一种方式获利。因此修复方案还必须包含价格源加固、抵押率重新设定、债务上限限制等配套措施。从公开的修复代码来看Sonne团队直接将SOMM市场从市场列表中隔离并设置isListed false来彻底禁用该市场。听起来奇怪但这确实是实用主义做法既然这个市场曾经造成问题且SOMM代币的流动性不足干脆禁用整个市场避免类似问题再次发生。4.2 分叉协议审计中的常见盲区Sonne Finance这个案例给所有Compound v2分叉项目敲响了警钟。在审计类似分叉协议时有几个盲区特别容易被忽视治理操作的完整性很多时候市场部署和注册分为两步操作如果两步之间存在时间窗口或者最终只有一步被提交上链市场就会变成“影子市场”。新模块与旧逻辑的兼容性Sonne Finance可能在原版Compound v2基础上新增了SOMM市场支持模块但这个模块没有完全遵守原有“先注册后交易”的规范。多链部署一致性Sonne在Optimism和Base上有两套部署但因为部署时间和流程不同一条链上注册了SOMM市场另一条链上却漏掉了导致同一项目在不同链上的表现完全不同。在审计实操中除了检查代码逻辑还需要用脚本遍历所有CToken合约比对实际部署状态与注册状态是否有差异。这个动作听起来很基础但很多审计机构在实际操作中并不会做如此彻底的核查特别是当项目方只把核心市场代码交给审计而忽略了已经部署的附加市场时。5. DeFi安全从业者的反思与实操建议5.1 排查清单给项目方和安全团队的五条建议经过这次事件我整理了适用于所有分叉借贷协议的风险排查清单强烈建议项目方和审计团队按这几条做一轮自查核查已部署市场与注册市场的一致性可以用脚本遍历所有cToken合约地址逐一确认它们是否在Comptroller的markets映射中isListed为true。这一步必须作为上线前强制检查项。清理长期未使用的市场如果一个市场已经部署但长期没有交易或者从未正式注册应该直接通过治理投票将其禁用而不是让它在链上“裸奔”。监控新增市场交易行为异常建立链上监控系统对未注册但存在资金流动的合约地址进行告警尤其是在借贷协议的上下文环境中。严格的多链部署流程管理每次在多链部署时使用相同的部署脚本和验证工具确保各链状态完全一致并做一次跨链对比校验。对治理操作进行时间锁保护任何涉及新增市场、修改风险参数的操作都应该通过时间锁执行给予社区和安全团队足够的观察窗口。5.2 为什么这个案例值得反复研究我刚接触这个案例时第一反应是“这不就是个简单的注册遗漏吗”。但深入拆解后会发现这次攻击其实展现了DeFi漏洞的三个层次第一层是代码层面的遗漏第二层是架构层面的错配第三层是业务流程层面的风险识别不足。对于安全研究人员来说这个案例是学习分叉代码审计绝佳的教材。它不涉及复杂的数学难题纯粹靠对系统结构的深入理解就能发现这也说明DeFi安全的核心不只是代码审计更是架构审计和治理审计。很多漏洞不是写出来的而是“搭”出来的。在我自己的审计工作中每次看到分叉项目时现在都会多问一句这个项目有没有在标准市场之外部署过任何非标准市场有没有通过治理投票创建但最终没有完成注册的资产这些问题看起来简单但确实是许多重大漏洞的根源。希望这次Sonne Finance的教训能给整个行业提个醒。
上一篇/下一篇内容由系统自动关联 返回资讯列表 →