本文作者分享了参与Burve协议安全审计竞赛的经验,通过导师的帮助,将大型代码库分解为小模块,结合现代工具进行代码探索和动态测试,最终取得第八名的成绩。作者还总结了在审计过程中未能发现的漏洞,包括累积精度误差、外部依赖风险以及经济模型缺陷等,强调了团队合作和持续学习的重要性。
在竞争激烈的安全审计领域中,每一个新项目都是一次考验。我的旅程一直是由规模可控的审计项目铺就的,通常徘徊在 500 行非注释源代码 (nSLOC) 左右。它们具有挑战性,但可以理解。后来,Burve 协议竞赛出现在 Sherlock 上,局面发生了巨大的变化。这是一个 4,192 nSLOC 的庞然大物,并配有 80,500 USDC 的奖金池。这个规模令人生畏,但机会是不可否认的。
我很自豪地说,我最终获得了第 8 名,发现了 9 个发现中的 3 个。但这个成功故事不是关于一个孤独的审计员孤立地与一个复杂的代码库作斗争。它证明了一种强大组合的有效性:系统的个人方法、现代工具,以及——或许最重要的是——我通过指导收到的宝贵指导。我的导师,Zealynx Security 的 Carlos (bloqarl) 的支持,对这一成就绝对至关重要,将潜在的障碍变成了垫脚石。
本文是对这段旅程的分解。它与其说是关于漏洞本身,不如说是关于过程——策略、工具和促成这一结果的协作精神。
面对一个比我之前处理过的任何代码库大八倍的代码库,第一个障碍纯粹是心理上的。“分而治之”策略是众所周知的,但要有效地应用它,需要对系统的架构有深入的了解。在这里,我遇到了我的第一个主要学习曲线,并且导师指导被证明是一个游戏规则改变者。
Burve 是使用 EIP-2535 Diamond Standard 实现的。这是我第一次直接接触 diamond 合约,坦率地说,最初的复杂性令人生畏。我可以看到该标准使用单个代理合约将调用委托给多个逻辑合约(“facets”),但理解其存储、其升级模式及其潜在的攻击面的细微差别感觉像是攀登一座陡峭的山。
这就是导师指导的力量变得非常明显的地方。来自 Zealynx Security 的 Carlos (bloqarl) 提供了关键的见解和资源,揭开了 Diamond Standard 的神秘面纱。他没有让我花费数天时间仅从文档和文章中拼凑在一起,而是加速了我对代理如何工作以及如何进行 facets 审计的理解。这种支持是根本性的;它将原本可能需要数天的障碍转变为战略优势。
有了这种新发现的清晰度,前进的道路就清晰了:
VaultE4626 facet、交换逻辑和其他组件几乎可以被隔离地分析。在导师指导的帮助下,这一高招将 4,192 nSLOC 的怪物变成了一系列更小的、更熟悉的 ~500 nSLOC 的审计。最初的时间投入,在专家指导的推动下,是非常宝贵的。下次我面对 Diamond 合约时,我将准备好直接投入到安全研究中,充满信心和经过验证的方法。
最大的一笔收益来自于一个发现,它不是通过盯着代码几个小时发现的,而是通过研究构建代码的组件发现的。职位头衔是安全研究员,而这次经历强调了第一个词的重要性。
我对 Ethereum Request for Comments (ERCs) 非常着迷。它们是 DeFi 的基石,理解它们的细微差别、功能,以及最重要的是,它们常见的陷阱,对于审计员来说是一种超能力。Burve 协议使用了 ERC4626 vault,这是一个我之前研究过的标准。
与 ERC4626 相关的一个特殊漏洞一直让我难以忘怀:首次存款/通货膨胀攻击。我的研究过程很简单:
ERC4626。这让我回到了 Arbitrary Execution 在 Medium 上发表的杰出的三部分系列文章,我认为这是任何使用 tokenized vault 的人都必须阅读的文章:
有了这些文章中的知识,我就不仅仅是在阅读代码了;我还在主动寻找一种特定的模式。我检查了 Burve 围绕底层 vault 的 wrapper 实现是否对此已知的通货膨胀攻击进行了缓解。它没有。
发现:攻击者可以通过对底层 vault 进行 ERC4626 通货膨胀攻击来窃取用户资金 wrapper 合约根据底层 vault 的
totalAssets来计算要铸造的份额。攻击者可以直接将资产转移到底层 vault,人为地膨胀totalAssets值,而不铸造任何新的份额。当受害者随后存款时,他们的资金将根据这个膨胀的总量进行估值,导致他们收到的份额远少于他们应该收到的份额(通常为零)。然后,攻击者可以提取他们的小额初始赌注,索取不成比例的底层资产,包括受害者的资金。
这一发现强化了一个核心原则:你不必从第一性原理出发找到每一个错误。通过研究其他人的研究成果来站在巨人的肩膀上。我的好奇心现在驱使我去调查像 OpenZeppelin 这样的标准实现是否已经集成了针对这个众所周知问题的默认缓解措施。
虽然链下研究获得了关键发现,但另外两个中等严重程度的发现是通过一种更动手、更具互动性的代码方法发现的。为此,我严重依赖两种强大的技术:LLM 辅助的代码探索和 Foundry 的高详细程度测试。
我使用像 Cursor 和 Windsurfer 这样的 AI 工具不是作为自动错误查找器,而是作为可以帮助我“vibe 测试”代码的非常快速的助手。我的流程如下:
commit 函数。commit 函数。”生成的代码并不总是完美的,但它是一个节省我几分钟样板代码的起点。它使代码变得更少抽象和更多有形,向我展示了如何与它进行交互。正是在这个过程中,LLM 帮助我发现了一个微妙但至关重要的逻辑缺陷。
发现:合约逻辑缺陷将不匹配内部和外部 vault 份额,可能会困住用户资金。 在
commit函数中,当提款大于存款时,代码应该计算提款净额。但是,它错误地在从assetsToWithdraw中减去之前将assetsToDeposit变量归零。这意味着合约总是从外部 vault 中提取总额,燃烧的外部份额多于其内部会计跟踪的份额,导致缓慢的去同步化,最终会困住用户资金。
对于最后一个漏洞,功劳归功于 Foundry 强大的日志记录功能。在使用 LLM 快速生成一个测试桩之后,我以最大的详细程度运行它:foundry test -vvvv。此命令提供了一个完整的执行跟踪,显示了每一个状态变化、变量赋值和内部调用。
发现:提款功能中的零税收漏洞 跟踪显示,合约正确计算了要从提款中扣除的税额。但是,在后续步骤中,从未从发送给用户的最终金额中扣除此计算出的税款。他们收到了全部的、税前的提款金额,有效地耗尽了协议中应该作为费用收取的资金。该缺陷在
-vvvv输出中显而易见。
这种方法将审计从对代码的静态分析转变为对其执行的动态分析,通常会揭示在仅阅读时容易错过的逻辑错误。
获得第 8 名是一个了不起的成绩,但我成长过程中的一个关键部分是谦虚地回顾最终的审计报告,并分析我没有发现的漏洞。这些失误不是失败;它们是宝贵的教训,可以形成新的思维模式,并成为未来每次审计中都要检查的基础“断点”。
一个未发现的发现涉及储备金份额计算中的一个沉默的舍入攻击。在一个可以重复调用的函数(trimBalance)中的小的、看似无关紧要的舍入误差可能会被利用来逐渐膨胀份额计数器,最终导致溢出并破坏整个储备金的会计核算。
我错过的另一个漏洞源于对外部依赖项的错误假设。协议逻辑假设对底层 vault 进行 withdraw 和随后的 deposit 总是能完美地净额。但是,它没有考虑到底层 vault 可能会暂时禁用提款的情况。在这种“糟糕的情况”下,协议的内部逻辑将不正确地进行,导致协议费用被错误地发送给用户。
最具启发性的遗漏是一个高层次的经济设计缺陷。该协议使用一个单一的、同质化的 ValueToken 来代表多个独立的流动性池(“closures”)的所有权,这些流动性池持有不同的资产并具有不同的估值。这创造了一个巨大的套利机会:攻击者可以从低价值或崩溃的池中铸造廉价的 ValueToken 份额,然后使用它们从健康的、高价值的池中赎回有价值的资产,从而以牺牲其他 LP 为代价耗尽它。
这次审计是一个重要的里程碑,它加强了这样一个认识:在这个领域取得成功是个人的技能和社区力量的结合。我现在的关键收获更加丰富:
虽然个人努力至关重要,但这次经历教会我,在这个领域中的成长很少是孤立的追求。来自像 Zealynx Security 这样的社区和像 Carlos 这样的导师的支持是一种力量倍增器,可以将艰巨的挑战转化为可实现的里程碑和深刻的学习体验。
- 原文链接: zealynx.io/blogs/A-Zealy...
- 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!