使用Echidna对链上合约进行模糊测试:重现Stax Finance漏洞

本文介绍了Echidna 2.1.0版本的新功能,包括直接获取链上数据的能力,并通过重现2022年Stax Finance漏洞案例,详细演示了如何使用Echidna进行链上合约模糊测试和漏洞利用。

使用Echidna对链上合约进行模糊测试 - The Trail of Bits博客

随着Echidna(我们的以太坊智能合约模糊测试工具)2.1.0版本的发布,我们引入了直接获取链上数据的新功能,例如合约代码和存储槽值。这些数据可用于在链上状态下对已部署合约进行模糊测试,或测试新代码如何与现有合约集成。

Echidna现在能够通过模糊测试合约接口和链上代码来重现真实世界的黑客攻击。在这篇博客文章中,我们将演示如何仅使用Echidna来重现2022年Stax Finance黑客攻击,发现并利用漏洞。该事件涉及StaxLPStaking合约中缺少验证检查,导致321,154个xLP代币被盗,当时价值约230万美元。

Echidna的“优化模式”将自动发现能够最大化或最小化自定义函数结果的交易序列。在这种情况下,我们只需要求它最大化攻击者的余额,并让它完成其余工作。

重现Stax Finance漏洞利用

要使用Echidna重现Stax Finance漏洞利用,我们需要:

  • 一个由Echidna进行模糊测试的合约,该合约包装了目标Stax合约和相关合约(图1)
  • 一个Echidna配置文件,其中包含攻击发生前的区块编号和用于获取链上信息的RPC提供者(图2)

图1显示了模糊测试合约的简化版本,图2显示了配置文件。您可以在此处找到完整的合约和配置文件。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
contract StaxExploit {

    IStaxLP StaxLP = IStaxLP(0xBcB8b7FC9197fEDa75C101fA69d3211b5a30dCD9);
    IStaxLPStaking StaxLPStaking =
        IStaxLPStaking(0xd2869042E12a3506100af1D192b5b04D65137941);

    ...

    constructor() {
        // 使用HEVM设置block.number和block.timestamp
        hevm.warp(1665493703);
        hevm.roll(15725066);

        // 设置初始余额
        ...
    }

    function getBalance() internal returns (uint256) {
        return StaxLP.balanceOf(address(this));
    }

    function stake(uint256 _amount) public {
        _amount = (_amount % getBalance()) + 1;
        StaxLPStaking.stake(_amount);
    }

    // 其他函数包装器...

    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 {}

    // 优化函数
    function echidna_optimize_extracted_profit() public returns (int256) {
        return (int256(StaxLP.balanceOf(address(this))) -
            int256(initialAmount));
    }
}

图1:攻击者合约

在模糊测试合约中,我们添加了一个名为echidna_optimize_extracted_profit()的函数,允许Echidna监控当前交易序列的利润并识别最有利可图的序列。

1
2
3
4
5
testMode: optimization
testLimit: 1000000
corpusDir: corpus-stax
rpcUrl: https://.../
rpcBlock: 15725066

图2:Echidna配置文件

如配置文件所示,我们将Echidna设置为在优化模式下运行,以最大化利润函数。

接下来,我们使用图3中的命令在模糊测试合约上运行Echidna。

1
$ echidna ./StaxExploit.sol --contract StaxExploit --config echidna-config.yaml

图3:用于执行Echidna的命令

Echidna的优化器生成具有不同参数的随机函数调用序列,计算每个序列的echidna_optimize_extracted_profit()函数的返回值。在运行结束时,它会从交易序列中丢弃任何不必要或回滚的调用,只保留那些最大化利润的调用。

因此,通过我们的模糊测试合约和利润函数,Echidna可以快速发现正确的交易序列以重现黑客攻击,而无需事先了解实际的合约漏洞利用。

图4:使用本文代码的Echidna运行

技术细节

既然我们已经概述了Echidna如何重现漏洞利用,让我们深入一些技术细节,供有兴趣自己尝试的读者参考。

为了设置模糊测试合约,我们使用了Slither的代码生成工具。这使我们能够从Etherscan获取目标合约的接口和部署地址,以及其他必要的接口和地址(例如,ERC-20代币、其他合约和用户定义的数据类型)。我们还为Echidna创建了包装器来调用合约函数,并添加了我们的echidna_optimize_extracted_profit()函数。

我们利用了Echidna使用hevm作弊码来操纵执行环境的能力。这涉及将区块编号和区块时间戳设置为实际漏洞利用之前的时间点。为了简化hevm作弊码的使用,我们使用了我们属性存储库中的帮助程序,并导入了HEVM.sol帮助程序。

在设置配置文件时,我们将testMode配置为优化。我们还为Echidna分配了RPC提供者和区块编号(分别由rpcUrlrpcBlock参数指示),以获取链上信息。为了防止Echidna未找到漏洞利用时运行时间无限,我们通过testLimit参数设置了一百万次测试运行的上限。生成的语料库存储在corpus-stax目录中,如corpusDir参数所指定。

限制和挑战

虽然Echidna是一个强大的工具,但它并非没有限制和挑战:

  • Echidna可能无法找到所有漏洞。由于模糊测试无法保证完全覆盖,因此必须用其他安全测试方法(如静态分析、形式验证甚至单元测试(例如,100%分支覆盖、测试边缘情况、正面和负面测试等)来增强Echidna,以进行全面的分析。
  • 复杂合约可能需要更多时间。根据智能合约的复杂性,Echidna可能需要更长时间来发现漏洞。
  • 从网络获取合约和存储槽可能很慢。API速率限制可能会阻碍为使用大量存储槽的合约获取链上信息的过程。关于如何缓解这个问题的讨论正在进行中。
  • 可能需要自定义。在某些情况下,您可能需要调整Echidna的配置或测试工具以适应您的特定用例。

为了克服这些挑战,请遵循最佳实践,例如将Echidna与其他安全测试工具结合使用,彻底了解您的智能合约功能,并在必要时咨询安全专家。

Echidna提高合约安全性

Echidna中新功能的引入,如链上合约检索、数据获取和多核模糊测试,为在现实场景中提高代码安全性开辟了新途径。将模糊测试添加到您的项目中,通过覆盖单元或集成测试可能忽略的边缘情况,提高了代码的安全性。

有关使用Echidna的更多指导,包括详细文档和实际示例,请访问我们的“构建安全合约”网站。如果您喜欢视觉学习,请查看我们在YouTube上提供的信息丰富的Echidna直播流。

立即下载Echidna并开始探索其所有功能。访问我们的官方存储库以获取最新版本和安装说明。我们鼓励您重现此漏洞利用,以熟悉新的链上模糊测试功能,并了解它如何帮助使您的合约更安全。

如果您喜欢这篇文章,请分享: Twitter LinkedIn GitHub Mastodon Hacker News

comments powered by Disqus
使用 Hugo 构建
主题 StackJimmy 设计