Echidna 2.1.0 版本引入了直接检索链上数据的新功能,如合约代码和存储槽值。文章展示了如何使用 Echidna 复现 2022 年 Stax Finance 被攻击事件,该事件是由于 StaxLPStaking
合约中缺少验证检查,导致价值 230 万美元的 xLP 代币被盗。
随着 Echidna 2.1.0 版本的发布,我们以太坊智能合约的 Fuzzing 工具,我们引入了直接检索链上数据的新功能,例如合约代码和存储槽值。这些数据可用于 Fuzzing 已部署合约的链上状态,或测试新代码如何与现有合约集成。
Echidna 现在有能力通过 Fuzzing 合约接口和链上代码来重现真实世界的攻击。在这篇博文中,我们将演示如何仅使用 Echidna 重现 2022 年的 Stax Finance 攻击来发现并利用漏洞。该事件涉及 StaxLPStaking
合约中缺少验证检查,导致 321,154 个 xLP 代币被盗,当时价值约为 230 万美元。
Echidna 的“优化模式”将自动发现最大化或最小化自定义函数结果的交易序列。在这种情况下,我们将简单地要求它最大化攻击者的余额,然后让它完成其余的工作。
要使用 Echidna 重现 Stax Finance 漏洞,我们需要:
图 1 显示了 Fuzzing 合约的简化版本,图 2 显示了配置文件。你可以在此处找到完整的合约和配置文件。
contract StaxExploit {
IStaxLP StaxLP = IStaxLP(0xBcB8b7FC9197fEDa75C101fA69d3211b5a30dCD9);
IStaxLPStaking StaxLPStaking =
IStaxLPStaking(0xd2869042E12a3506100af1D192b5b04D65137941);
...
constructor() {
// Using HEVM to set the block.number and block.timestamp
// 使用 HEVM 设置 block.number 和 block.timestamp
hevm.warp(1665493703);
hevm.roll(15725066);
// setting up initial balances
// 设置初始余额
...
}
function getBalance() internal returns (uint256) {
return StaxLP.balanceOf(address(this));
}
function stake(uint256 _amount) public {
_amount = (_amount % getBalance()) + 1;
StaxLPStaking.stake(_amount);
}
// Other functions wrappers ...
// 其他函数包装器...
function migrateStake(
address oldStaking,
uint256 amount
) public {
StaxLPStaking.migrateStake(oldStaking, amount);
}
function migrateWithdraw(
address staker,
uint256 amount
) public {
StaxLPStaking.migrateWithdraw(staker, amount);
}
fallback() external payable {}
// The optimization function
// 优化函数
function echidna_optimize_extracted_profit() public returns (int256) {
return (int256(StaxLP.balanceOf(address(this))) -
int256(initialAmount));
}
}
图 1:攻击者合约
在 Fuzzing 合约中,我们添加了一个名为 echidna_optimize_extracted_profit()
的函数,允许 Echidna 监视当前交易序列的利润,并识别利润最高的交易序列。
testMode: optimization
testLimit: 1000000
corpusDir: corpus-stax
rpcUrl: https://.../
rpcBlock: 15725066
图 2:Echidna 配置文件
如配置文件所示,我们将 Echidna 设置为在优化模式下运行,以最大化利润函数。
接下来,我们使用图 3 中的命令在 Fuzzing 合约上运行 Echidna。
$ echidna ./StaxExploit.sol --contract StaxExploit --config echidna-config.yaml
图 3:用于执行 Echidna 的命令
Echidna 的优化器生成具有不同参数的函数调用的随机序列,计算每个序列的 echidna_optimize_extracted_profit()
函数的返回值。在运行结束时,它会丢弃交易序列中任何不必要的或恢复的调用,只留下那些最大化利润的调用。
因此,通过我们的 Fuzzing 合约和利润函数,Echidna 可以迅速发现正确的交易序列来重现攻击,而无需事先了解实际的合约漏洞。
图 4:使用本文代码运行 Echidna 的结果
现在我们已经对 Echidna 如何重现漏洞进行了高级概述,让我们深入了解一些技术细节,以供有兴趣自己尝试的读者参考。
为了设置 Fuzzing 合约,我们使用了 Slither 的代码生成实用程序。这使我们能够从 Etherscan 获取目标合约的接口和部署地址,以及其他必要的接口和地址(例如,ERC-20 代币、其他合约和用户定义的数据类型)。我们还创建了包装器,供 Echidna 调用合约函数,并添加了我们的 echidna_optimize_extracted_profit()
函数。
我们利用了 Echidna 使用 hevm 欺骗码来操纵执行环境的能力。这涉及将区块号和区块时间戳设置为实际攻击发生之前的某个时间点。为了简化 hevm 欺骗码 的使用,我们使用了 properties
仓库 中的 helper,并导入了 HEVM.sol
helper。
在设置配置文件时,我们将 testMode
配置为 optimization
。我们还为 Echidna 分配了 RPC 提供程序和区块号(分别由 rpcUrl
和 rpcBlock
参数指示)以获取链上信息。为了防止 Echidna 在未找到漏洞的情况下无限期运行,我们通过 testLimit
参数设置了 100 万次测试运行的上限。生成的语料库存储在 corpus-stax
目录中,如 corpusDir
参数中指定的那样。
虽然 Echidna 是一个强大的工具,但它并非没有局限性和挑战:
为了克服这些挑战,请遵循最佳实践,例如将 Echidna 与其他安全测试工具结合使用,彻底了解智能合约的功能,并在必要时咨询安全专家。
Echidna 中新功能的引入,例如链上合约检索、数据获取和多核 Fuzzing,为在现实场景中提高代码的安全性开辟了新途径。通过将 Fuzz 测试添加到你的项目中,可以通过覆盖单元测试或集成测试可能忽略的边缘情况来提高代码的安全性。
有关使用 Echidna 的更多指导,包括详细的文档和实际示例,请访问我们的 “构建安全合约”网站。如果你喜欢视觉学习,请查看我们在 YouTube 上提供的 Echidna 直播。
立即下载 Echidna,开始探索其所有功能。请访问 我们的官方仓库 获取最新版本和安装说明。我们鼓励你重现此漏洞,以熟悉新的链上 Fuzzing 功能,并深入了解它如何帮助你使合约更安全。
- 原文链接: blog.trailofbits.com/202...
- 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!