本文讨论了在云中进行模糊测试的不同选项,特别是在Solidity合约上的实施,介绍了多种云服务提供商和工具,包括DigitalOcean, GitHub CI, Consensys和Recon,帮助读者选择适合的云模糊测试环境。文中详细阐述了每个选项的优缺点、难度及背景要求,并附有必要的CLI命令和示例配置,以便更好地进行模糊测试。
对不同基于云的 Solidity 合约模糊测试选项的审查
模糊测试本质上是一项计算资源密集型任务,需要数小时(通常是数天/周)才能实现足够的逻辑覆盖,以提供强有力的保证,确保属性在所有可能的系统状态下成立。这就是为什么将这些长期的模糊测试任务分配到独立机器上,通常是一个明智的选择,因为这样可以不间断地运行,而不必与本地机器竞争计算资源,从而增加任务因资源争用而崩溃的风险。
有两种主要选项可以将这些长期运行转移出去:
拥有一台独立的机器来进行运行。
将其上传到云服务的远程机器上。
使用云服务的主要好处在于,你可以以拥有硬件的很小一部分成本,访问性能更强的企业级硬件。
在这篇文章中,我们将查看不同的云模糊测试运行选项,以及每种选项的好处和权衡。
为了公平比较不同的云模糊测试选项,我们将使用经过修改的 create-chimera-app 仓库,并添加 失败的断言作为基准,因为它允许我们使用 Echidna、Medusa 和 Foundry 执行不变性测试。
由于存在大量的云服务提供商,设置云模糊测试环境的方式几乎是无限的,因此为了简化本帖的内容,我们将查看大多数安全研究人员使用的主要方法,从最复杂的(需要了解云资源的配置)到最简单的(只需了解正在使用的模糊工具):
在 DigitalOcean 上使用 CloudExec 模糊测试
使用 GitHub CI 进行模糊测试
在 Consensys 上进行模糊测试
使用 Recon 进行模糊测试
难度: 中等
所需背景: 是(了解设置/管理云资源)
设置难度: 4/10(大部分设置与配置运行者相关)
CloudeExec 是 Trail of Bits 的工具,通过命令行在 DigitalOcean 云中运行通用计算任务。它在后台设置一台服务器,安装在设置 bash 脚本中传入的任何依赖,并将输出数据和日志存储到 DigitalOcean 的对象存储(称为 Spaces,等同于 AWS S3 存储桶)。此外,当作业完成时,它会自动销毁服务器,以避免产生额外的使用费用。
CloudExec README 的 开始使用 部分描述了从终端下载 CloudExec CLI 工具以将作业上传到 DigitalOcean 的步骤。
下载工具后,如果你还没有 DigitalOcean 账户,你需要创建一个,并 创建一个 Spaces 存储桶 以允许 CloudExec 将模糊测试运行的输出数据和日志写入该存储桶。你还需要创建一个 访问Token,使 CloudExec 能够使用你的账户预配新的服务器实例。
要设置你创建的 Spaces 存储桶和 CloudExec 的访问Token的凭证,你需要运行:
cloudexec configure
并按照命令行提示传递所需的配置信息。
完成后,运行:
cloudexec check
将验证你是否已正确配置 CloudExec 以访问你的 DigitalOcean 资源。
如果上述检查通过,你可以使用以下命令为项目配置新作业:
cloudexec init
这会在当前目录中创建一个新的 cloudexec.toml
配置文件。
作为我们的基准,我们将使用以下添加到 create-chimera-app 仓库的 配置:
配置好 CloudExec 后,我们可以使用以下命令运行我们的作业:
cloudexec launch
通过运行
cloudexec logs
我们可以看到 CloudExec 在 DigitalOcean 上为我们启动的 VM 日志的输出:
CloudExec 日志提供了它们上传到 s3://cloudexec/job-34/cloudexec.log 的位置
这些日志也会存储在包含作业编号的 Spaces 对象存储桶中。然后,我们可以使用存储桶中 cloudexec.log 对象的快速分享选项与合作者共享这些输出日志:
这将为我们提供整个运行的输出日志的可共享链接。
值得注意的是,如果你在完成模糊测试后忘记删除你的 Spaces 存储桶,你仍会因在 DigitalOcean 上维护一个活跃资源而产生费用,而 Droplet 计算资源在作业完成后将自动销毁,因此不会产生维护费用。
难度: 中等
所需背景: 是(了解 GitHub actions 配置设置和处理依赖关系)
设置难度: 6/10(需要一些设置)
GitHub actions 允许我们设置一个托管运行者,每次我们向仓库提交更改时执行一个 Echidna 作业。GitHub 托管的运行者是由 GitHub 托管的 VM,我们将将 Echidna 添加为应用程序,GitHub 会处理 VM 的维护和升级。
我们只需提供一个配置文件给 GitHub 运行者设置,它将负责为每个我们开始的作业预配一个新的 VM,并在作业完成时解除分配它(请参见 本指南 了解有关 GitHub 托管运行者的更多信息)。
Trail of Bits(Echidna 和 Medusa 的维护者)已经创建了一个包含 GitHub actions 设置的仓库,这个设置触发在 这里 运行 Echidna。使用 GitHub Action 配置文件,我们只需进行一些小的更改,以便它以与 create-chimera-app README 中定义的相同命令运行 Echidna,并将 这个 文件添加到我们仓库的 .github/workflows
目录中。
现在每当我们对主分支进行 PR 或提交时,这个动作将触发一个对其进行的 Echidna 作业。
在分析测试运行结果时,Echidna 运行的工作流步骤在没有失败测试的情况下只需 43 秒即可执行 50000 次调用。
如果我们引入一个失败的测试,我们可以看到 "Run Echidna" 步骤失败,且失败的测试在 GitHub actions 作业细节中可见,带有反例:
我们还可以通过修改工作流配置文件,包含 output-file
配置来保存语料库和重现器,以便我们可以在本地重现失败的测试进行调试。然后,我们还可以与任何有权限访问该仓库的人共享失败测试的链接。
总体而言,GitHub 使得轻松实现 Echidna/Medusa 的所有功能成为可能,主要的权衡在于设置附加功能(如语料库重用或分叉测试)所需的时间。
难度: 低
所需背景: 否(对 Python 环境的基本理解有帮助)
设置难度: 7/10(需要一些设置)
Consensys Fuzzing 工具允许你为 Foundry 进行无状态模糊测试和有状态不变性测试。
与使用返回布尔值的特殊函数来评估不变性的 Echidna/Medusa 不同,Foundry 不变性测试使用来自 forge 标准库的断言,因此我们必须编写一个新的不变性测试,以复制我们之前例子中的断言,这些断言可以被 Foundry 验证,我们将其添加到 CryticToFoundry
合约中:
遵循 本指南,我们可以看到使用 Consensys Fuzzing 设置模糊测试运行相当简单。我们所需的工作是安装 diligence-fuzzing CLI 工具:
pip3 install diligence-fuzzing
然后使用 UI 创建 CLI 的 API 密钥,并使用以下命令将其添加到本地(记得将 .env 添加到我们的 gitignore,以免如果我们推送到远程仓库,就将 API 密钥提交进去):
echo FUZZ_API_KEY='your api key here' > .env
然后我们使用
fuzz forge test
来运行我们的作业,这会将其上传到 Consensys Fuzzing 云运行器。
云运行器仪表盘在运行过程中提供我们模糊测试的指标,并在运行结束时提供有关失败属性的结果。
覆盖图有助于可视化何时覆盖可能因我们定义的模糊测试而被阻塞(通常表明它们需要夹紧输入值),或者作为间接指示某些路径未被我们的测试覆盖,因此需要扩展以包括这些路径。
更仔细地查看结果时,我们可以看到我们定义的不变性在这里作为 Foundry 不变性测试被标记为失败(绿色指示符表示模糊测试覆盖的行数):
在扩展失败断言的细节时,我们可以看到导致其失败的调用序列:
总体而言,Consensys Fuzzing 选项为运行长期 Foundry 不变性测试提供了简单的设置和直观的 UI。此外,类似于 Echidna/Medusa,Consensys Fuzzing 允许 Foundry 语料库重用( 详见此处),使你可以在多个运行中逐渐建立行/逻辑覆盖。使用免费套餐,你的模糊测试运行时间上限为 5 分钟,但使用 Builder Pro 计划,你可以访问无上限的模糊测试运行,并且可以在运行作业的机器上使用最多 2 核心,从而允许并行模糊测试和更快的运行。
由于 Consensys Fuzzing 不支持 Echidna 和 Medusa,因此如果你的测试套件仅设置为使用这些,你需要对其进行重构以使其兼容,或者使用 create-chimera-app 以便轻松与这三者协同工作。
难度: 低
所需背景: 否
设置难度: 9/10(私有仓库需要一些设置)
Recon 旨在简化实施和运行 Solidity 智能合约不变性测试的过程。因此我们的工具提供了一种设置你的不变性测试并在云中运行它们的方式。
要在 Recon 云运行器上运行作业,必须使用 Foundry 框架(Hardhat + Foundry 仓库也支持,只需在作业设置页面选择 自定义预安装过程: yarn install —ignore-scripts),并且你必须注册一个 Pro 账户。
如果你要运行作业的仓库是公开的,可以通过导航到作业页面来启动作业,并选择要用于运行作业的模糊测试器(Echidna、Medusa 或 Foundry),然后将你要测试的分支的仓库链接粘贴进来:
在粘贴仓库链接后,以下表单字段会自动填充。如果 Foundry 运行的目录不同于根目录,请在 Directory 字段中指定。
注意:关于私有仓库请参见 本指南,了解如何首先授予 Recon 对你仓库的读取权限。
在右侧菜单中,我们只需指定提供所有必要配置以执行测试的 Echidna 配置文件:
单击 Run Job 按钮后,作业将上传到 Recon 的云运行器,我们可以通过单击 所有作业 列表中的作业的 查看详情 按钮查看作业日志和覆盖报告的详细信息:
在我们的作业完成后,我们可以在此页面查看日志中断言失败的位置,并根据 resulting failure 自动生成一个 Foundry 测试,我们可以将其添加回项目中并用作本地测试,以识别问题的根本原因。此外,我们可以使用 共享作业结果 按钮轻松共享运行结果,也可以使用 下载语料库 按钮在本地重放评估的调用序列。我们还可以通过在 语料库重用作业 ID 字段中传递我们要使用的语料库的作业 ID 来积累多个运行的语料库。
正如我们所看到的,在云中运行模糊测试作业有很多选项,每种选项都有其在易用性和需要处理的抽象方面的权衡。
在撰写本文的过程中,我还尝试实施通过 AWS Fargate 在 docker 容器上运行模糊测试作业的最低抽象设置,但在经过多个小时(至少 7+)的尝试和许多挫折后,我最终放弃了。虽然我绝对不是云方面的专家,但我确实有 AWS 的 CCP 认证,并且由于经验对他们的云有基本的理解,但不得不学习理解 docker 容器并在 AWS 上进行部署,而这并不是我的主要目标,这段经历我希望不久的将来能避免再次经历。
在 AWS 的这段经历是 Recon 团队构建我们工具的原因,因为我们发现自己在配置测试套件和运行者上花费的时间比真正运行它们的时间还要多。然后我们意识到,这种挫败感和所花费的时间可能是任何尝试对协议进行不变性测试的人所经历的结果,因此会导致更少的人实施它们,从而导致更多的协议漏洞,使整个加密货币环境变得不那么安全。
这就是 Recon 存在的原因,以及我们为什么每天仍在努力改进它,如果你有任何问题或建议,请随时在我们的 discord 服务器 中与我们联系。
- 原文链接: getrecon.substack.com/p/...
- 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!