作者:Guillermo Larregay and Elvis Skozdopolj 编译:Cointime.com 237
随着Echidna 2.1.0版本的发布,我们的以太坊智能合约模糊测试工具引入了新功能,可以直接获取链上数据,如合约代码和存储槽值。这些数据可以用于对已部署合约在链上状态下进行模糊测试,或测试新代码与现有合约的集成情况。
Echidna现在能够通过对合约接口和链上代码进行模糊测试来重现真实世界中的黑客攻击。在本博文中,我们将演示如何仅使用Echidna来找到并利用2022年Stax Finance黑客事件中的漏洞,从而重新制造出该事件。该事件涉及StaxLPStaking合约中缺失的验证检查,导致321,154个xLP代币被盗,当时价值约为230万美元。
Echidna的“优化模式”将自动发现最大化或最小化自定义函数结果的交易序列。在这种情况下,我们只需要求它最大化攻击者的余额,然后让它完成其他工作。
重新制造Stax Finance的攻击
要使用Echidna重新制造Stax Finance的攻击,我们需要:
1、一个由Echidna进行模糊测试的合约,该合约包装目标Stax合约和相关合约
2、一个Echidna配置文件,其中包含攻击发生前的区块号和用于获取链上信息的RPC提供程序
下文第一串代码显示了模糊测试合约的简化版本,第二串代码则显示了配置文件。您可以在此处找到完整的合约代码和配置文件。
contract StaxExploit {
IStaxLP StaxLP = IStaxLP(0xBcB8b7FC9197fEDa75C101fA69d3211b5a30dCD9);
IStaxLPStaking StaxLPStaking =
IStaxLPStaking(0xd2869042E12a3506100af1D192b5b04D65137941);
...
constructor() {
// Using HEVM to set the block.number and 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));
}
}
在模糊测试合约中,我们添加了一个名为echidna_optimize_extracted_profit()的函数,允许Echidna监视当前事务序列的利润并识别最具盈利性的序列。testMode: optimization
testLimit: 1000000
corpusDir: corpus-stax
rpcUrl: https://.../
rpcBlock: 15725066
如配置文件所示,我们将Echidna设置为在优化模式下运行,以最大化利润函数。
接下来,我们使用下面的命令,在模糊测试合约上运行Echidna。$ echidna ./StaxExploit.sol --config echidna-config.yaml
Echidna的优化器生成带有不同参数的随机函数调用序列,并计算每个序列中echidna_optimize_extracted_profit()函数的返回值。在运行结束时,它会从事务序列中丢弃任何不必要或回滚的调用,只保留那些能最大化利润的调用。
因此,通过我们的模糊测试合约和利润函数,Echidna可以迅速发现正确的事务序列来重现攻击,而无需事先了解实际合约的漏洞利用方法。
细节问题
现在我们已经对Echidna如何重现攻击进行了高层次的概述,让我们深入一些技术细节,以便对于有兴趣自己尝试的读者来说更加清楚。
为了设置模糊测试合约,我们使用了Slither的代码生成工具。这使我们能够从Etherscan获取目标合约的接口和部署地址,以及其他必要的接口和地址(例如ERC-20代币、其他合约和用户定义的数据类型)。我们还为Echidna创建了调用合约函数的包装器,并添加了echidna_optimize_extracted_profit()函数。
我们利用了Echidna使用hevm cheat codes来操纵执行环境的能力。这涉及将区块号和区块时间戳设置为实际攻击之前的某个时间点。为了简化hevm cheat codes的使用,我们使用了存储库中的辅助函数,并导入了HEVM.sol辅助库。
在设置配置文件时,我们将testMode配置为optimization。我们还为Echidna分配了RPC提供程序和区块号(分别由rpcUrl和rpcBlock参数指示),以便Echidna获取链上信息。为了防止Echidna在找不到漏洞时运行时间无限延长,我们通过testLimit参数设置了一百万次测试运行的上限。生成的语料库存储在corpusDir参数指定的corpus-stax目录中。
局限性和挑战
虽然Echidna是一个强大的工具,但它也有一些局限性和挑战:
1、Echidna可能无法找到所有的漏洞。由于模糊测试无法保证完全覆盖,关键是要将Echidna与其他安全测试方法(如静态分析、形式验证甚至单元测试(例如100%分支覆盖率、边界情况测试、正向和负向测试等))相结合,进行全面的分析。
2、复杂的合约可能需要更多时间。根据智能合约的复杂程度,Echidna可能需要更长的时间来发现漏洞。
3、从网络获取合约和槽位可能会很慢。API速率限制可能会阻碍获取使用许多存储槽的合约的链上信息的过程。关于如何缓解这个问题正在进行讨论。
4、可能需要进行定制化。在某些情况下,您可能需要调整Echidna的配置或测试工具以适应特定的用例。
为了克服这些挑战,遵循最佳实践,例如将Echidna与其他安全测试工具相结合,充分理解智能合约的功能,并在必要时咨询安全专家。
Echidna提升了合约安全性
Echidna引入的新功能,例如链上合约检索、数据获取和多核模糊测试,为改善你的代码在实际场景中的安全性提供了新的方式。将模糊测试添加到项目中通过覆盖可能被单元测试或集成测试忽视的边界情况,提高了代码的安全性。
所有评论