本文深入分析了ERC-4626 Vaults中存在的通胀攻击漏洞,攻击者可以通过极少量初始存款和后续的“捐赠”操作,操纵Vault的份额计算,从而窃取后续存款人的资产。文章详细解释了攻击原理、步骤,并通过实例进行了说明,同时探讨了针对此漏洞的多种防御措施,例如使用ERC4626 Router、内部跟踪总资产、创建“死亡份额”以及在初始化时注入初始资金等。
现在,让我们一起设想一下——一个新部署的金库,看起来很安全,等待用户存款。几分钟之内,攻击者只存入了 1 wei ——这个数额小到几乎可笑。但是,通过精确和计算好的一系列交互,他们拿走了比他们投入的价值更多的价值,让合法的用户持有毫无价值的份额,并破坏了信任。
这不是虚构的——这是一个潜伏在许多看似“安全”的智能合约架构中的真实漏洞。
在这项分析中,我们深入研究这种精妙但危险的漏洞,它滥用金库份额机制、捐赠时机和奖励同步,以耗尽诚实用户的价值。我们分解它是如何发生的,为什么它有效,以及你如何使你的合约免受攻击。
ERC-4626 金库是高效的,但如果设计不当,它们很容易受到通货膨胀攻击。让我们一步一步地来。
ERC-4626 是以太坊中代币化金库的标准。金库就像一个智能合约,持有代币并允许用户存款和取款。
把它想象成一个代币的银行账户:
该标准确保所有金库以一致的方式工作,从而使 DeFi 应用程序更容易与它们交互。
把金库想象成一个特殊的数字小猪存钱罐(称为“金库”),人们可以在其中存钱。这个小猪存钱罐不是简单地跟踪每个人存入了多少钱,而是分发纸质票据(称为“份额”),这些票据代表了对银行中物品的所有权。
2. 如果你稍后存款,小猪存钱罐会这样计算你的票:
3. 当你想取回你的钱时,你退回你的票,并获得你应得的份额:
以下是一个狡猾的人(我们称他们为“Bob”)如何窃取你的钱:
2. 通货膨胀步骤:然后 Bob 做了一些棘手的事情。Bob“捐赠”了 100 美元给这个小猪存钱罐,但没有得到任何新票。这与正常的存款不同。
3. 你的存款:现在你来了,存入了你的 100 美元。小猪存钱罐计算你的票:
4. 盗窃:Bob 现在使用他们的 0.01 张票提款,这占所有现有票的 100%。
假设你正在打开一个全新的饼干罐,人们可以安全地存放他们的饼干。当有人把饼干放进去时,他们会得到票,显示他们拥有多少饼干。这基本上就是“金库”——一个安全的地方来存储数字货币(比如加密货币),在那里你会得到特殊的代币(称为“份额”),代表你的所有权。
5. 攻击者现在拿走了所有的饼干,因为他们是唯一拥有票的人。
关键问题是当金库新建或接近空置时,份额是如何计算的。
在 ERC-4626 中,用于计算份额的公式基于金库中的总资产与已经发行的总份额之间的关系。公式如下:
用于在 ERC-4626 中计算份额的公式
其中:
2. 然后,他们秘密地将更多的代币直接发送到金库
3. 这为下一个存款人创造了一个完美的陷阱
例如,如果攻击者在他们的 1 wei 存款后捐赠了 100 个代币:
这之所以有效,是因为金库在计算份额时会向下舍入。攻击者本质上是在操纵公式中的分母,使其他所有人的存款几乎一文不值,同时保持他们微小的存款价值一切。
此攻击的关键在于,金库以相同的方式对待所有传入的代币,无论它们是通过正确的存款进入还是直接发送到合约的。这允许攻击者在不获得更多份额的情况下人为地抬高金库的资产,从而使他们现有的份额价值一切。
查看 solidit 上的此提交,我们看到它与我们的讨论相似。 这是在 prePO 在 code4rena 上的审计竞赛中发现的。 它被提交为高风险发现。 这就是为什么我最初称这种漏洞非常危险的原因。
Cyfrin Login | Solodit \ \ Profiles by Cyfrin\ \ solodit.cyfrin.io
此提交描述了一个类似于我们之前讨论的通货膨胀攻击。以下是简化的细分:
2. 操纵份额价格(大规模转移):
_strategyController
发送大量(100 万个代币)以夸大份额价格。3. 后续存款人(10,000 个代币):
此攻击直接利用了用于计算份额的数学公式,以及份额可以通过操纵金库中的总资产来人为抬高的事实。以下是相关的方式:
2. 后续存款人不发放份额:
3. 攻击者的优势:
现在,让我们分析推荐的缓解措施,以及它们如何解决 ERC-4626 金库中的漏洞:
totalSupply() == 0
时)将第一组代币(或份额)发送到 零地址,它本质上从一开始就稀释了份额供应。2. 确保非零份额铸造:
require(_shares != 0, "zero shares minted");
)确保如果没有份额要铸造,该函数将失败。这将防止在进行了存款的情况下发放 0 份额的情况,攻击者利用这种情况来接管金库。3. 用于初始化的外围合约:
initialize()
和 deposit()
函数的想法可以确保金库在其他任何人可以存款之前,通过一些存款和股份正确初始化。我们可以在 code4rena 中看到 GoGoPool 竞赛 的另一项提交。
此案例研究说明了生息代币实现中常见的漏洞模式,即可以利用合约的初始状态。对于使用 ERC4626 或类似金库标准的 DeFi 协议来说,尤其重要的是了解此攻击向量并实施适当的初始化程序。
该报告详细介绍了 GoGoPool 的 ggAVAX 代币实现中的一个高严重性漏洞,该漏洞允许恶意的第一个存款人人为地膨胀份额价格,并从随后的存款人那里窃取资金。
该漏洞存在于 ERC4626 金库实现中,其中资产 (AVAX) 和份额 (ggAVAX) 之间的汇率计算为份额的 totalSupply
和 totalAssets()
之间的比率。攻击按以下方式进行:
该报告确定了两个严重的后果:
ZeroShares()
还原。该报告包括一个全面的 Foundry 测试,证明:
该漏洞利用了实施的两个具体方面:
convertToShares()
中的份额价格计算,该计算使用总供应量与总资产的比率syncRewards()
中的奖励周期时间,它允许攻击者在条件有利时快速更新帐户推荐的解决方案是在初始化期间用初始资金为金库播种,从而更难以操纵份额价格。这通过将初始存款作为合约初始化的一部分来防止抢先交易。
该报告已得到 GoGoPool 团队的确认,并且该漏洞已通过实施推荐的初始化存款来缓解。
从 opezeppelin 的博客 针对 ERC4626 通货膨胀攻击的新颖防御 中,以下是防御这种攻击的一些方法。
这是一种相对简单的方法,但它可能只是规避问题,而不是直接解决问题。
第二种策略旨在通过在内部跟踪金库持有的资产来消除直接转移的影响。这意味着捐赠的代币不被考虑在内,这有效地消除了通货膨胀攻击的风险。
该策略的灵感来自 Uniswap V2,它在首次存入流动性时创建了死亡 LP 份额。在 ERC4626 金库中,可以通过在首次存款/铸造时铸造死亡份额来实施类似的方法。
在该博客中,你将找到每种缓解措施的优缺点。
关于死亡份额,你将在 此处 找到完整的讨论。
我们还可以从我们分析的提交中看到一些缓解措施和建议。
此分析表明,DeFi 中微小的数学错误可能有多么危险。微小的 1 wei 差异——看起来无害——可以打开严重漏洞的大门,耗尽价值或阻止用户公平访问。
该课程是什么?永远不要因为你的数学看起来精确就认为它是安全的。金库、份额计算和定价逻辑都需要像潜在的攻击点一样进行测试——因为它们确实如此。
如果我们希望 DeFi 是安全且值得信赖的,我们必须像对待重要的事情一样对待即使是最小的细节。因为在这个领域,它们确实非常重要。
我喜欢称之为小数字,大风险。
感谢你今天加入我。下次见。
- 原文链接: coinsbench.com/erc-4626-...
- 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!