动态程序分析的局限与静态分析利器PointsTo

本文探讨动态程序分析工具如AddressSanitizer和Valgrind的局限性,介绍Trail of Bits开发的静态分析工具PointsTo如何通过数据流追踪技术检测百万行代码中的use-after-free漏洞,包括LLVM位码转换、数据流图构建和上下文敏感指针分析等关键技术细节。

动态程序分析的问题 - Trail of Bits博客

动态分析工具的局限性

开发者可以使用AddressSanitizer和Valgrind等工具来检测代码运行时是否访问未初始化内存、内存泄漏或释放后使用(use-after-free)等问题。尽管这些优秀工具已经可用,但内存漏洞仍然存在,仍然会被发布给用户,并仍在实际环境中被利用。

当今大多数漏洞发现工具都是动态的:它们在程序运行时识别漏洞。这很好,因为所有程序都有庞大的测试套件来覆盖每行代码……对吗?错了。大型测试套件是例外而非规则。测试套件确实有助于发现和减少漏洞,但漏洞仍然会漏网。

也许解决方案是付费请专业人士进行代码审计。让更多眼睛检查代码是好事™,但根本问题依然存在。专家头脑中运行的分析仍然是“动态的”:逐个思考所有代码路径根本不可行。

因此,动态分析可能遗漏漏洞,因为它们无法检查所有可能的程序路径。那么什么可以检查所有可能的程序路径呢?

在数百万行代码中发现释放后使用漏洞

我的2016年标准建议:卖出整数溢出,买入释放后使用,持有类型混淆 — John Lambert (@JohnLaTwC) 2016年1月6日

我们使用静态分析来分析数百万行代码,而无需运行代码。这种称为数据流追踪的分析技术使我们能够分析和总结所有可能程序路径的属性。这解决了前述因未执行某些程序路径而遗漏漏洞的问题。

全视角分析的实际工作原理

下面我们描述一个实际全程序静态分析工具的基本步骤,该工具是我们开发并经常使用的。这个名为PointsTo的工具在大型代码库中发现并报告潜在的释放后使用漏洞。

步骤1:转换为LLVM位码

PointsTo在程序的LLVM位码表示上运行。我们选择LLVM位码是因为它是执行程序分析的便捷中间表示。不出所料,我们分析流水线的第一阶段是将程序的源代码转换为LLVM位码数据库。我们使用名为CompInfo的内部工具来生成这些数据库。一个类似的替代开源工具是whole-program-llvm。

步骤2:创建数据流图

PointsTo背后的关键思想是分析指向已分配对象的指针如何在程序中流动。我们关心的是指针的赋值和复制、指针解引用以及指针释放。这些指针操作使用数据流图表示。

过程中最有趣的步骤是将分配和释放转换为特殊赋值的原理和方法。“为什么”是因为这种转换让我们能够重新利用现有的程序分析来查找从FREE定义到指针解引用的路径。方法更为微妙:PointsTo如何知道应该将“new A”更改为ALLOC,将“delete a”更改为FREE?

想象一个假设的嵌入式系统,程序内存严重不足,因此自然选择是使用名为ration_memory的自定义内存分配器。我们创建了一种Python建模语言来向PointsTo提供关于高级函数行为的信息。我们的建模脚本告诉PointsTo“new A”返回一个新对象,因此我们可以用它来对ration_memory说同样的话。

插曲:隐藏的数据流

从源代码到数据流图的转换看起来很简单,但那是因为我们开始的源代码很简单。它没有函数调用,更重要的是,它没有函数指针或方法调用!如果下面的callback是函数指针会发生什么?如果callback释放了x会发生什么?

1
2
3
int *x = malloc(4);
callback(x);
*x += 1;

这是PointsTo的秘方和同名特性:我们执行上下文和路径敏感的指针分析,告诉我们哪些函数指针指向哪些函数以及何时指向。总之,我们可以生成一个错误报告,跟踪x通过callback并再次返回的过程。

步骤3:结果呈现

是时候报告潜在错误供专家分析了。PointsTo搜索数据流图,查找从赋值到FREE再到解引用的流。这些流被转换为源代码行的程序切片,显示执行需要遵循的路径以产生释放后使用。以下是一个真实漏洞的程序切片示例:

(此处应有示例程序切片)

当向编译器人员描述这个系统时,通常的第一个问题是:但是误报怎么办?如果我们收到关于释放后使用的报告而实际上不是呢?这就是编译器程序分析和漏洞分析的优先级分岐之处。

编译器分析中的误报可能会引入漏洞,因此编译器通常是保守的。也就是说,它们用误报换取漏报。它们可能会错过一些优化机会,因为它们无法证明某些事情,但至少程序会被正确编译咳嗽

对于漏洞分析,这是一个糟糕的交易。漏洞分析中的误报很不方便,但当需要查看数百万行代码时,它们只是沧海一粟。然而,漏报是不可接受的。漏报是被遗漏并可能进入生产环境的漏洞。一个总能找到漏洞、有时警告你可疑但正确代码的工具是一项投资,可以在代码审计过程中节省时间和金钱。

总结

分析程序中的漏洞很难。应该遵循行业最佳实践,如拥有广泛的测试套件。开发者应定期通过动态分析工具运行他们的程序以摘取低垂的果实。但更重要的是,开发者应该理解测试套件和动态分析不是万能药。漏洞有一个讨厌的习惯,即隐藏在很少执行的代码路径后面。这就是为什么需要查看所有路径。这就是我们制作PointsTo的原因。

PointsTo是最近Empire Hacking(纽约双月聚会)的讨论话题。我在那里的演讲包含了关于PointsTo设计和实现的更多信息,对于好奇的读者,幻灯片和视频在下面重现。我们希望未来发布更多来自Empire Hacking的视频。

PointsTo最初是为Cyber Fast Track制作的,我们要感谢DARPA资助我们的工作。Trail of Bits的顾问使用PointsTo和其他内部工具进行应用程序安全审查。如果您有兴趣对代码进行详细审计,并得到PointsTo和我们的CRS等工具的支持,请联系我们。

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