意外踩踏DeFi乐高——Trail of Bits博客
Sam Sun | 2020年8月5日
区块链、漏洞利用、漏洞披露
yVault的初始版本包含计算yUSDC价格的逻辑,攻击者可通过操纵该逻辑耗尽资金池中大部分(甚至全部)资产。幸运的是,开发者Andre反应迅速,立即禁用了问题代码,保障了当时约40万美元的资金安全。然而,该漏洞仍凸显了DeFi领域因可组合性导致的复杂性风险。
什么是yVault?
2020年7月25日,yEarn推出了名为yVault的新服务:用户可将代币存入金库,金库会将其提供给能最大化利息收益的DeFi协议。初始版本支持USDC并与USDC/MUSD Balancer池集成。金库持有的所有USDC将作为流动性提供给Balancer池,并获取BPT代币作为回报。
用户通过存入USDC获取yUSDC,也可通过销毁yUSDC提取USDC。这两项操作依赖动态计算的汇率,即合约持有的BPT价值与yUSDC总供应量的比率。由于交易者支付费用时BPT价值上升,每个yUSDC代币的价值会随时间缓慢增长。
yVault发布一小时内,用户已存入约40万USDC,这促使我亲自审查代码。
漏洞详情
初始版本与Balancer集成,需先了解Balancer的运行机制。Balancer通过激励理性市场参与者自动再平衡,消除了流动性提供者手动调整投资组合的需求。若代币价格上涨,资金池将失衡。通常流动性提供者需支付费用出售增值代币,但Balancer激励外部用户支付费用以优惠价格购买代币获利,所付费用随后分配给流动性提供者。
图1展示了基于Balancer池状态和发送代币数量计算接收代币数量的公式。以MUSD/USDC 50/50池为例,交易费率为0.05%。
|
|
图1:输入代币对应的输出计算
首先分析理性市场参与者将资金池恢复平衡与非理性参与者破坏平衡时的函数行为。
假设资金池失衡,持有1,100,000 USDC和900,000 MUSD。若理性市场参与者支付90,000 MUSD,将获得99,954 USDC,盈利9,954 USDC。
若资金池平衡且持有1,000,000 USDC和1,000,000 MUSD,非理性参与者支付100,000 USDC将获得90,867 MUSD,亏损9,133 MUSD。
尽管第二笔交易立即亏损看似无用,但与第一笔交易结合会产生有趣现象。用户先执行亏损交易:将100,000 USDC转换为90,867 MUSD,损失9,133 USD;再执行盈利交易:将90,867 MUSD转换为99,908 USDC,盈利9,041 USD,净损失92 USD。虽不理想,但远低于9,200 USD的亏损。
此时分析BPT估值过程:若持有1%的BPT,交易开始时价值20,000 USD(1% × 2,000,000),结束时价值20,000.96 USD(1% × 2,000,092)。但在交易中间时刻,BPT价值曾短暂达到20,091.33 USD(1% × 2,009,133),这正是漏洞关键。
将此过程应用于yVault:亏损交易前,金库持有一定价值的BPT;盈利交易后,BPT价值略增;但在两交易之间,BPT价值显著升高。由于yUSDC价值与BPT价值直接相关,若在亏损交易前购买yUSDC并在盈利交易前出售,可瞬时获利。重复操作即可耗尽金库。
修复方案
准确计算BPT真实价值并防止攻击者利用滑点获利是复杂难题。开发者Andre部署了新策略,将USDC转换为MUSD并存入mStable储蓄账户,随后激活该方案。
未来建议
DeFi可组合性难度高,新协议易暴露于意外风险。若集成多种代币,任一代币可能危及整个平台安全;若集成多平台,协议可能受复杂交互影响。
安全工具可帮助预防大多数简单代码漏洞:
- Crytic:使用增强版Slither自动检测多达90类漏洞
- Echidna:通过模糊测试验证特定属性
- Manticore:对代码进行符号化分析
当然,工具并非安全万能药。研究《重要智能合约中的实际缺陷(及如何发现)》表明,即使技术显著改进,近50%的问题仍难以通过工具检测。对于复杂代码库和DeFi项目,建议联系我们安排安全评估或注册以太坊安全咨询时间。
若喜欢本文,请分享至:Twitter | LinkedIn | GitHub | Mastodon | Hacker News