查找与整数处理相关的溢出、截断等漏洞

luochicun
最近有一些发现引起了我的注意,它们属于整数漏洞处理引起的漏洞。整数溢出也是一种常见的软件漏洞,由此引发的漏洞可能比格式化字符串缺陷和缓冲区溢出缺陷更难于发现。

许多漏洞发生的根本原因是对整数的漏洞处理。标准int类型可以从0x7FFFFFFF一直到-0x80000000(注意负数),并带有整数溢出。或者,它可以被截断并将数字从正数更改为负数。整数在C语言中可能是一个噩梦,并且多年来造成了许多内存攻击漏洞。

整数溢出漏洞(integer overflow):在计算机中,整数分为无符号整数以及有符号整数两种。其中有符号整数会在最高位用0表示正数,用1表示负数,而无符号整数则没有这种限制。另外,我们常见的整数类型有8位(单字节字符、布尔类型)、16位(短整型)、32位(长整型)等。关于整数溢出,其实它与其它类型的溢出一样,都是将数据放入了比它本身小的存储空间中,从而出现了溢出。由此引发的一切程序漏洞都可以成为整数溢出漏洞。

最近有一些发现引起了我的注意,它们属于整数漏洞处理引起的漏洞。整数溢出也是一种常见的软件漏洞,由此引发的漏洞可能比格式化字符串缺陷和缓冲区溢出缺陷更难于发现。溢出类型及表现:

1、溢出。只有符号的数才会发生溢出,对于signed整型的溢出,C的规范定义是“undefined behavior”,也就是说,编译器爱怎么实现就怎么实现。对于大多数编译器来说,仍然是回绕。

2、回绕。无符号数会回绕(常绕过一些判断语句)。

3、截断。将一个较大宽度的数存入一个宽度小的操作数中,高位发生截断。

简单了解整数溢出的危害:

1、整数回绕之后,会导致索引越界,取到不确定的数据。

2、或者判断失效,形成死循环。

3、回绕之后,导致分配超大内存。

需要指出的是Qualys发现的Linux内核中的整数截断漏洞、GHSL的BSD管理程序中的符号转换漏洞以及Checkpoint发现的Kindle中缓冲区分配大小的整数溢出。然后,我的伙伴seiraib发现了一个整数溢出漏洞模糊测试,但我们无法找到溢出实际发生的位置。我想知道,这漏洞会在编译时被发现吗?还是会在运行时立即崩溃?”

GCC和Clang有编译标志,可以在编译时查找几个漏洞类。此外,还有一些运行时保护可能会导致崩溃,从而使root操作更容易。本文是关于通过编译器警告和动态检测发现漏洞的。所有带有额外示例的源代码片段都可以在mdulin2/integer_compile_flags中找到。

漏洞类

目标是以一种程序预期不会导致内存损坏的方式更改数字。C语言中将重点关注三个数量(但主要是整数)漏洞类:

溢出/下溢:如果超过c中该类型的最大值或最小值,整数将简单地环绕。例如,有符号整数的溢出将从0x7FFFFFFF一直到-0x80000000。下溢以相反的方向进行,是在减去数值时发生的。

截断:缩小数字的存储容量。例如,从uint64_t到uint32_t会将数字的存储容量减少一半。这有可能彻底改变数字的值和符号。

有符号类型(signedness)转换:数字要么是有符号的,要么是无符号的(例如unsigned int和int)。当从负有符号数转换为无符号数或从非常大的无符号数转换为有符号数时,在这些之间进行转换可能会产生可怕的后果。例如,将0xFFFFFFFF的无符号整数转换为有符号整数将是-1而不是非常大的数字。

静态分析

其中两个漏洞类可以在编译时通过特定的编译标志来确定,尽管可能会出现大量不可利用的漏洞,但还是有一些很好的线索可以用来发现漏洞。

截断

查找与整数处理相关的溢出、截断等漏洞

截断代码

2345截图20211028093243.png

有符号类型转换代码

在编译期间使用Wconversion标志将输出警告“可能改变值的隐式转换”。这直接引用了截断和转换漏洞!

例如,截断情况的代码可以在图1中看到。该代码有一种正在转换为int的long long类型。由于这会将存储容量从64位更改为32位,因此可能会导致损坏。当使用Wconversion标志编译此代码时,将出现一条警告消息,指出截断问题!这个警告可以在下图中看到。当执行从size_t(unsigned long long)到int的转换时,可以使用这个确切的漏洞消息来查找上面提到的Linux Kernel内核漏洞。

2345截图20211028093243.png

截断警告消息

另一个需要考虑的有趣事项是float和double的情况。因为double是float大小的2倍,所以相同类型的截断可以通过相同的编译标志检测到,此处显示了代码中的一个示例。

有符号类型

Wconversion标志也可以用于检测符号转换漏洞。当从有符号转换为无符号或从无符号整数转换为有符号整数时,就会发生这种情况,这两个问题都显示在上图中。下图显示了编译时漏洞消息的一个示例。

2345截图20211028093243.png

有符号类型转换警告消息

静态分析总结

这些标志只检查隐式转换。有时,需要将一个值从无符号整数转换为有符号整数,以便进行一些数学计算,这是有效的和预期的C语言。当一个数字被显式转换时,这些漏洞消息将不会显示,因此。为了找到显式的强制类型转换,可以使用类似于CodeQL的东西、手动检查或动态测试。

尽管到目前为止我们只使用Wconversion进行静态分析,但实际上构成了这个单一标志的标志过多。例如,Wsign-conversion标志警告可能会更改整数值符号的隐式转换。有关这些的更多信息,请访问GCC文档。

动态仪表

静态分析很可能会得到大量误报,不过其中还是有一些真正的漏洞。然而,如果可以触发代码路径,动态分析总是能找到真正的漏洞。下面,我们将展示在与整数相关的漏洞上崩溃/通知的检测选项。我们将讨论GCC和Clang中的一些特定标志,而不是讨论漏洞类。

ftrapv

此选项为加法、减法和乘法运算的有符号溢出生成漏洞。同样,由于这是动态测试,因此每当发生这种情况时都会导致程序崩溃!可以在下面看到此代码的示例:

2345截图20211028093243.png

当行a=a+1时,上面的代码将导致整数溢出;,因为C中整数的最大大小是0x7FFFFFFF。会发生什么呢?程序被终止,并且永远不会到达print语句。通过在发生溢出时强制崩溃,我们可以保证检测到漏洞,并且更容易找到溢出的原因。这在模糊测试和尝试找到崩溃根本原因时非常有用。

还应该注意的是,该标志也可以检测有符号整数下溢。

fsanitize=integer

用于查找整数漏洞的UBSAN(未初始化行为清理器)非常棒!此标志特定于Clang,用于与整数相关的漏洞。

虽然ftrapv只捕获有符号整数溢出,但fsanitize=integer将在无符号整数和有符号整数溢出(有符号整数溢出和无符号整数溢出)时崩溃。这意味着所有整数溢出,无论符号如何,通过加法、减法或乘法都将在运行时被捕获。

除了发现程序中的溢出/下溢外,我们还可以找到用这个标志提到的另外两个漏洞类:截断(隐式有符号整数截断和隐式无符号整数截断)和转换(隐式整数符号改变)。与上面的静态方法不同,必须发生一个糟糕的数学运算才能触发UBSAN使程序崩溃。

不过,让我们看看它的实际效果!我们将使用原始的符号问题。当运行代码时,值为LONG_MAX(0x7FFFFFFFFFFFFFFF)的long long将由于转换为整数而被分为两半(被截断)。因此,这将是0xFFFFFFFF或-1作为有符号整数。由于添加了额外的检测,程序在截断发生时崩溃。这样,设备就不需要改变标志,它检查long long中的值是否适合int。这个崩溃可以在下图中看到。

2345截图20211028093243.png

截断转换崩溃

我们已经提到了对整数溢出/下溢、整数截断和符号转换问题的动态和静态检查。但是,动态检测缺少一件事:浮点数学运算。

当浮点数学运算在C中溢出时,它会变为无穷大或inf。奇怪的是,由于浮点数学处理精度的方式,浮点数从未真正发生环绕;它只是趋近于无穷大!可以在此处查看此溢出的示例。

另一个未捕获的漏洞是浮动截断。例如,在运行时不会捕获从double到float的转换。有一个误导性的UBSAN标志(-fsanitize=float-cast-overflow),它只能发现漏洞的双精度/浮点数到整数的转换,但不会发现浮点数之间的截断漏洞。这方面的一个例子可以在这里看到。

了解浮点数变为inf和NaN可能很有用,例如Unreal游戏引擎中的Jack Bakers NaN传播漏洞确实会发生。但是,目前没有检测到C语言中浮点数在运行时的溢出、下溢、截断或NaN/Inf使用。

总结

在尝试查找漏洞时,任何来自自动化工具或仪器的帮助都非常有用。除了上面提到的整数漏洞类之外,还有许多其他有趣的标志和工具,例如众所周知的ASAN(地址清理器),还有许多其他编译标志和检测选项可以帮助查找特定的漏洞类,本文只关注查找与整数相关的漏洞类。

参考及来源:https://maxwelldulin.com/BlogPost?post=9715056640

THEEND

最新评论(评论仅代表用户观点)

更多
暂无评论