Cointime

扫码下载App
iOS & Android

由Vyper编译器0.2.15版本漏洞引发的Curve攻击分析

项目方

背景

近期,一些使用 Vyper 0.2.15 版本编写的稳定池(alETH/msETH/pETH)遭受了重入攻击。这是由于该版本编译器实现错误导致的重入锁故障问题。此漏洞导致了约5000万美金的损失。本文将分析该漏洞的根本原因和攻击原理。同时,将提供优化建议以防止类似事件再次发生。

根因分析

漏洞根源来自于编译器对@nonreentrant(<str>)修饰器语义的错误实现。

正确语义描述

@nonreentrant(<str>)是用于限制函数的重入,其语义则类似于多线程中的互斥锁:

单函数修饰:当一个函数被该修饰器修饰,它的多个调用无法同时执行。

多函数修饰:当多个函数都使用该修饰器,且重入限制键相同(字符串值相同),它们的多个调用仍然无法同时执行。

一个正确的实现是,对每个重入限制键设置一个布尔值,0代表有一个函数在执行,1代表没有函数执行。多个被该键限制的函数根据该变量来进行同步(获得键-1,释放键+1,尝试获得键需检查值是否为1)。

实际语义实现

在Vyper编译器0.2.15版本及之前,对于该语义的实现是错误的。错误代码如下所示。

for node in vyper_module.get_children(vy_ast.FunctionDef): type_ = node._metadata["type"] if type_.nonreentrant is not None: type_.set_reentrancy_key_position(StorageSlot(storage_slot)) # TODO this could have better typing but leave it untyped until # we nail down the format better variable_name = f"nonreentrant.{type_.nonreentrant}" ret[variable_name] = { "type": "nonreentrant lock", "location": "storage", "slot": storage_slot, } # TODO use one byte - or bit - per reentrancy key # requires either an extra SLOAD or caching the value of the # location in memory at entrance storage_slot += 1# Error Here​

分析 代码中的storage_slot对应上文提到的同步变量。错误在于它的值域,该变量一开始为0,每当解释器处理到一个函数声明中的@nonreentrant,便将同步变量 +1,这样是重复的。当代码满足上文中的多函数修饰 情况时,由于解释器处理了多个函数,同步变量的值域不再满足布尔值,与正确语义不符合(即存在锁失效)。其实,代码中的TODO 注释已经反映了开发者的顾虑。

修复 在patch中,检查了该键是否已被创建,若是则不再+1

结果 些合约因为该修饰器的实际实现与预期不符而受到攻击,即使这些合约在理论上是正确的。比如接下来介绍的案例。

合约攻击案例

所有满足上文提及的多函数修饰合约,都有可能被攻击。比如被攻击的一个合约,代码有5个函数都被同一个键@nonreentrant(“lock”)修饰,因此相互可以重入。

  • add_liquidity
  • remove_liquidity
  • exchange
  • remove_liquidity_imbalance
  • remove_liquidity_one_coin

黑客的攻击利用了前两个函数。它们分别用于往流动池中添加和移除资产,流动性提供者从中可以获得交易费用和其他奖励。攻击的过程类似于一般的重入攻击:

  • 第一次调用add_liquidity存资产。
  • 调用remove_liquidity移除这些资产。

其中remove_liquidity 会通过外部调用,返还eth给黑客。此时黑客通过在fallback函数内调用add_liquidity完成重入。

在重入到add_liquidity 后,由于remove_liquidity 尚未减少代币总量total_supply ,因此在计算为黑客铸造的代币数量时,超出了原本应有的数量。

remove_liquidity()

add_liquidity()

解决方案与未来发展

针对这则事故,我们对于未来的Web3安全开发解决方案有一些思考:

  • 1. 协议开发者的责任与敏感性

协议开发者需要积极关注核心供应链的更新和安全问题,及时获取相关信息,增加对底层实现的理解。

  • 2. DevSecOps的引入

如何在合约开发流程中更好的引入一些安全测试和验证的工具,无论是静态分析还是动态Fuzz,嵌入SDLC(软件开发生命周期)中,使得一些问题能够更早的被暴露并解决。

  • 3. Software Composition Analysis (SCA) 工具的引入

我们注意到,在Vyper漏洞被曝出之后,其实给几个被攻击的协议团队有大约半天到一天的时间去做出补救措施去反应,但遗憾的是,最终并没能成功挽救被盗资产。我们认为对于这种供应链问题,应该引入完善的供应链管理平台,也就是Web2我们常说的制品库管理,当供应链出现问题的第一时间,可以快速告警并给出升级方案。

  • 4. 交易底层范式的创新

在基础设施层面,考虑在运行时层面进行自动化防护,例如动态的安全检查,或者在执行关键操作时自动执行防护措施。我们也很开心看到了许多在这个层面上的一些趋势,例如Uniswap V4,通过引入Hook,大大增加了交易功能的扩展性,开发者可以在交易的多个阶段中去提供一些防护来增强安全性。类似的还有Artela,能够让开发者在Runtime层面实现多个阶段的扩展,天然的就能够解决重入这个大问题,具体可以参考https://medium.com/@artela_chinese/该篇文章。

结论

智能合约开发的复杂性和困难性使得漏洞的出现几乎不可避免。但通过更好地理解和使用现有的工具,持续关注和参与开源社区活动,优化和改进基础设施,我们可以降低漏洞的风险,提高整个系统的安全性和健壮性。未来的区块链安全不仅需要编译器和开发者的共同努力,更需要依赖于静态分析、动态Fuzz等先进工具的支持和推动。同样也更需要有创新者能够打破束缚,提出更好的底层交易模型以及方案,让整个Web3世界更加安全。

评论

所有评论

推荐阅读

  • 美国现货比特币ETF昨日净流出2.614亿美元

    10月15日,据Farside Investors监测数据,美国现货比特币ETF昨日净流出2.614亿美元。

  • 美国现货以太坊ETF昨日净流出1.295亿美元

    10月15日,据Farside Investors监测数据,美国现货以太坊ETF昨日净流出1.295亿美元。

  • 过去1小时全网爆仓4.76亿美元,主爆多单

    数据显示,过去1小时全网爆仓4.76亿美元,其中多单爆仓4.57亿美元,空单爆仓1979.45万美元,主爆多单。其中ETH爆仓2.00亿美元,BTC爆仓1.16亿美元。

  • BTC跌破81000美元

    行情显示,BTC跌破81000美元,现报80989.9美元,24小时跌幅达到2.87%,行情波动较大,请做好风险控制。

  • ETH跌破2400美元

    行情显示,ETH跌破2400美元,现报2395.62美元,24小时跌幅达到4.47%,行情波动较大,请做好风险控制。

  • 美国考虑放宽对影子船队的制裁,以降低海运费率

    10月15日,一名美国官员当地时间周三表示,特朗普政府正在考虑放宽针对所谓“影子船队”船只的制裁。这些船只曾运输来自俄罗斯、伊朗和委内瑞拉等国的石油及石油产品。此举旨在降低目前处于历史高位的全球海运费率。此前不久,美国总统特朗普刚刚暂时解除了针对俄罗斯柴油的制裁。这一决定招致了乌克兰总统泽连斯基、欧洲多国政府,以及特朗普所属的共和党内部分参议员的强烈批评。该美国官员透露,目前的讨论重点在于,美国是否允许此前遭制裁的船只运输“合法石油”。这里的“合法石油”是指未受到美国制裁的原油或石油产品,而非受到制裁措施限制的俄罗斯和伊朗石油。该官员表示,此举旨在推动更多成品油和原油从海湾地区运出。(金十)

  • 韩国股指开盘大跌逾5%

    10月15日,韩国KOSPI指数跌破6000点,日内跌5.59%。SK海力士跌超7%,三星电子跌超6%。(金十)

  • 美元指数抹去CPI报告公布以来的所有涨幅

    10月15日,美元指数DXY在亚盘初缓慢下跌,已完全抹去昨日美国9月CPI数据公布以来的所有涨幅(最高累计上涨约70点),现报102.16。英镑兑美元GBP/USD及欧元兑美元EUR/USD涨约0.3%,现分别报1.3231和1.1197。(金十)

  • BTC跌破82000美元

    行情显示,BTC跌破82000美元,现报81984.48美元,24小时跌幅达到2.01%,行情波动较大,请做好风险控制。

  • BTC跌破83000美元

    行情显示,BTC跌破83000美元,现报83015.81美元,24小时跌幅达到0.46%,行情波动较大,请做好风险控制。