编译器模糊测试一年回顾:Solidity及其他语言的漏洞挖掘之旅

本文详细记录了一年多来对Solidity等编译器进行持续模糊测试的成果与方法,发现并修复了88个漏洞,探讨独立模糊测试的价值与不同技术策略的实践效果。

编译器模糊测试一年回顾:Solidity及其他语言的漏洞挖掘之旅

2020年夏季,我们描述了对Solidity编译器solc进行模糊测试的工作。现在,我们重新审视这个项目,因为模糊测试活动往往会“饱和”,随着时间的推移发现的新结果越来越少。Solidity模糊测试是否已经耗尽?对一个高风险项目进行模糊测试是否值得,特别是当它已经拥有自己活跃且有效的模糊测试工作时?

该模糊测试活动的第一批漏洞于2020年2月使用afl变体提交。从那时起,我们提交了74份报告。其中67份被确认为漏洞,其中66个已被修复。有7份是重复的或不被认为是真正的漏洞。

鉴于其中21个漏洞是自去年12月以来提交的,可以说我们的模糊测试活动有力地证明了为什么独立模糊测试仍然重要,并且可以发现不同的漏洞,即使涉及基于OSSFuzz的测试。

为什么可能无限期地对这样一个项目进行模糊测试是有用的?答案有三部分。首先,更多的模糊测试覆盖了更多的代码执行路径,长时间的模糊测试运行尤其有帮助。对于我们知道的任何模糊测试工具来说,很难接近路径覆盖饱和。即使运行afl 30天或更长时间,我们的测试仍然每1-2小时发现新路径,有时发现新边缘。我们报告的一些漏洞只有在模糊测试一个月后才被发现。

生产编译器非常复杂,因此深入模糊测试是有用的。生成输入需要时间,例如我们最近发现的模糊测试solc的Solidity级别的漏洞:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
pragma experimental SMTChecker;
contract A {
    function f() internal virtual {
        v();
    }
    function v() internal virtual {
    }
}

contract B is A {
    function f() internal virtual override {
        super.f();
    }
}

contract C is B {
    function v() internal override {
        if (0==1)
            f();
    }
}

这段代码应该编译无误,但没有。在漏洞修复之前,它导致SMT检查器崩溃,抛出致命的“超级合约不可用”错误,原因是分支内虚拟调用中用于变量访问的合约上下文不正确。

编译器应该进行长时间的模糊测试,因为它们的复杂性和可能的执行路径数量。一个经验法则是,afl在任何非平凡目标上真正开始认真工作之前需要达到一百万次执行,而编译器可能需要更多。根据我们的经验,编译器运行速度从每秒不到一次执行到每秒多达40次执行不等。仅仅达到一百万次执行可能需要几天时间!

我们想要与OSSFuzz一起独立进行模糊测试的第二个原因是从不同角度接近目标。我们没有严格使用基于字典或语法的方法与传统的模糊测试变异操作符,而是使用来自任何语言变异测试的想法,向afl添加“代码特定”的变异操作符,并主要(但不完全)依赖这些,而不是afl更通用的变异,后者往往专注于二进制格式数据。做不同的事情可能是解决模糊测试饱和的好方法。

最后,我们不断获取最新代码并开始对新版本的solc进行模糊测试。由于OSSFuzz持续集成不包括我们的技术,对其他模糊测试工具困难但对我们代码变异方法容易的漏洞有时会出现,我们的模糊测试工具几乎会立即找到它们。

但我们不会获取每个新版本并重新开始,因为我们不想失去通过长期模糊测试活动获得的基础。我们也不会持续从上次停止的地方开始,因为afl可以生成的数万个测试语料库可能充满了无趣的路径,这可能使在新代码中找到漏洞更容易。我们有时会从现有运行中恢复,但只是偶尔。

在像solc这样经过大量模糊测试的程序中找到漏洞并不容易。仅次于我们的最佳独立模糊测试工作,即Charalambos Mitropoulos的工作,也被solc团队在他们关于OSSFuzz模糊测试的帖子中提到,只发现了8个漏洞,尽管它自2019年10月以来一直在进行。

其他语言,其他编译器

我们在solc上的成功激励我们模糊测试其他编译器。首先,我们尝试模糊测试Vyper编译器——一种旨在为编写以太坊区块链智能合约提供更安全、类似Python的Solidity替代方案的语言。我们之前的Vyper模糊测试使用 essentially 基于语法的方法与TSTL(模板脚本测试语言)Python库通过python-afl发现了一些有趣的漏洞。我们在这个活动中发现了一些漏洞,但由于检测的Python测试速度慢和吞吐量差,选择不走极端。

相比之下,我的合作者,Sourcegraph的Rijnard van Tonder,在模糊测试Diem项目的Move语言方面取得了更大的成功——这是以前称为Facebook Libra的区块链的语言。在这里,编译器速度快,检测成本低。Rijnard已经报告了编译器中的14个漏洞,到目前为止,所有这些都被确认和分配,其中11个已被修复。鉴于模糊测试仅仅两个月前开始,这是一个令人印象深刻的漏洞收获!

使用Rijnard关于使用afl.rs模糊测试Rust代码的笔记,我尝试了我们的工具在Fe上,这是一种由以太坊基金会支持的新智能合约语言。Fe在某种意义上是Vyper的继承者,但受到Rust的更多启发,编译器更快。我在其第一个alpha版本发布之日开始模糊测试Fe,并在九天后提交了我的第一个问题。

为了支持我的模糊测试活动,Fe团队改变了Yul后端的失败,该后端使用solc编译Yul,以产生对afl可见的Rust恐慌,我们开始了比赛。到目前为止,这项工作产生了31个问题,略高于Fe所有GitHub问题的18%,包括功能请求。其中,14个被确认为漏洞,其中10个已被修复;剩余的漏洞仍在审查中。

我们不仅仅模糊测试智能合约语言。Rijnard模糊测试了Zig编译器——一种旨在简单和透明的新系统编程语言,并发现了两个漏洞(已确认,但未修复)。

我们模糊测试活动的未来

我们在afl编译器模糊测试活动中发现了88个已修复的漏洞,另外还有14个已确认但尚未修复的漏洞。

有趣的是,模糊测试工具没有使用字典或语法。除了测试用例中适度示例程序语料库所表达的内容外,它们对这些语言一无所知。那么我们如何能如此有效地模糊测试编译器呢?

模糊测试工具在正则表达式级别操作。它们甚至不使用上下文无关语言信息。大多数模糊测试使用快速的基于C字符串的启发式方法来进行“类似代码”的更改,例如删除括号之间的代码,更改算术或逻辑操作符,或者只是交换代码行,以及将if语句更改为while并删除函数参数。换句话说,它们应用变异测试工具会进行的那种更改。这种方法效果很好,即使Vyper和Fe不是很像C,并且只有Python的空白、逗号和括号用法被表示。

自定义字典和语言感知变异规则可能更有效,但目标是为编译器项目提供有效的模糊测试,而不需要太多资源。我们还希望看到好的模糊测试策略在项目开发早期阶段的影响,就像Fe语言一样。我们报告的一些漏洞比可能的情况更早地突出了开发人员的棘手角落情况。我们希望像这样的讨论将有助于产生更健壮的语言和编译器,减少为适应设计缺陷而进行的黑客行为,这些缺陷发现得太晚,难以轻易更改。

我们计划继续模糊测试这些编译器中的大多数,因为solc努力表明模糊测试活动可以长期保持可行,即使有其他针对同一编译器的模糊测试工作。

编译器复杂,大多数也在快速变化。例如,Fe是一种全新的语言,还没有完全设计,而Solidity以其对用户面对语法和编译器内部的巨大变化而闻名。

我们还在与Bhargava Shastry交谈,他领导Solidity的内部模糊测试工作,并应用他们在Yul优化级别的protobuf模糊测试中应用的一些语义检查我们自己。我们开始通过solc的严格汇编选项直接模糊测试Yul,并且我们已经发现了一个有趣的漏洞,该漏洞被迅速修复并引起了相当多的讨论!我们希望找到不仅仅是使solc崩溃的输入的能力将把这种模糊测试带到下一个水平。

更大的问题是模糊测试是否限于由于其无法检测许多错误代码错误而能找到的漏洞。两个编译器的差异比较,或编译器与其在优化关闭时的自身输出的差异比较,通常需要程序的一种更受限的形式,这限制了您发现的漏洞,因为程序必须编译和执行以比较结果。

解决这个问题的一种方法是使编译器更频繁地崩溃。我们设想一个世界,编译器包括类似测试选项的东西,启用激进和昂贵的检查,这些检查在正常运行中不实用,例如寄存器分配的健全性检查。尽管这些检查对于正常运行可能太昂贵,但它们可以为一些模糊测试运行打开,因为编译的程序通常很小,而且,也许更重要的是,在极其关键代码(火星探测器代码、核反应堆控制代码——或高价值智能合约)的最终生产编译中,以确保没有错误代码漏洞潜入这些系统。

最后,我们想教育编译器开发人员和其他以源代码为输入的工具的开发人员,有效的模糊测试不必是耗费大量开发人员时间的高成本努力。为编译器找到崩溃输入通常很容易,只需使用一些空闲的CPU周期、一组体面的语言源代码示例和afl-compiler-fuzzer工具!

我们希望您喜欢了解我们的长期编译器模糊测试项目,并且我们很想在Twitter @trailofbits上听到您自己的模糊测试经验。

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

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