整数溢出不溢出?

Posted

技术标签:

【中文标题】整数溢出不溢出?【英文标题】:Integer overflow not overflowing? 【发布时间】:2022-01-21 14:29:09 【问题描述】:

我正在做来自247CTF“不可能的数字”的 ctf 挑战。 挑战是关于整数溢出,由以下文件组成:

#include <stdio.h>
int main() 
    int impossible_number;
    FILE *flag;
    char c;
    if (scanf("%d", &impossible_number)) 
        if (impossible_number > 0 && impossible_number > (impossible_number + 1)) 
            flag = fopen("flag.txt","r");
            while((c = getc(flag)) != EOF) 
                printf("%c",c);
            
        
    
    return 0;

您可以在以下位置尝试挑战:

$ nc 1765a1cbe1629dfc.247ctf.com 50458

很简单,你需要触发这个案例:

if (impossible_number > 0 && impossible_number > (impossible_number + 1))

您通过输入 2147483647 执行的操作,然后在 impossible_number + 1 行中溢出。 这对我有用,但我也尝试在 vs 代码中本地运行它,这里没有触发 if 语句。 经过一番调试,我得出结论,这是失败的比例:

impossible_number > (impossible_number + 1)

这对我来说真的很奇怪,我什至尝试添加一些值的打印:

#include <stdio.h>
int main() 
    int impossible_number;
    FILE *flag;
    char c;
    if (scanf("%d", &impossible_number)) 
        printf("impossible nr: %d \n", impossible_number);
        printf("plus one nr: %d \n",impossible_number + 1 );
        if (impossible_number > 0 && impossible_number > (impossible_number + 1)) 
            flag = fopen("flag.txt","r");
            while((c = getc(flag)) != EOF) 
                printf("%c",c);
            
        
    
    return 0;

打印这个:

impossible nr: 2147483647 
plus one nr: -2147483648 

这对我来说毫无意义,为什么这在 247CTF 服务器上有效,但在我运行时无效?

【问题讨论】:

if (scanf("%d", &amp;impossible_number)) 如果出现错误或存在EOF 将无法正常工作(当scanf 返回EOF 时)。请记住,只有 zero 被认为是错误的,而 EOF 不为零。 由于未定义有符号溢出,因此允许编译器完全优化该条件。您是否启用了优化? 整数溢出是未定义的行为 - 得出关于它如何或为什么“工作”(或不工作)的结论实际上是一个毫无意义的练习。以任何方式依赖于任何类型的未定义行为的代码永远不能说是“工作” - anyone 可以说调用未定义行为的 C 代码是“我还没有观察到它失败 - 。”即便如此,“失败”的概念也毫无意义。 有符号整数在溢出时的行为是未定义的,因此在比较结果时不应期待可预测的结果。 247CTF 游戏是关于黑客攻击的,因此您应该期望它会利用一些您可能无法自行复制的未定义行为。真正的黑客也不总是能够访问他们试图访问的确切硬件和配置。 【参考方案1】:

正如 cmets 中所述,有符号整数溢出是 C 中未定义的行为。

游戏的程序版本显然是用一个天真地处理它的编译器构建的:实际上将 1 加到 impossible_number(使用普通的补码加法),然后将结果与 impossible_number 进行比较并执行 @987654324 @如果它更少。如您所见,在这种情况下,输入 2147483647 有效。在我的测试中,没有优化的 clang 的行为是这样的。

但还有其他可能性。例如,最新版本的 GCC,即使使用 -O0,请注意,在不发生溢出的任何情况下,测试都不会为真。如果确实发生了溢出,则行为是未定义的,因此编译器完全可以在这种情况下做任何它喜欢的事情。因此可以假设测试永远不可能是真的,这就是它的作用:它优化了整个if 块,包括现在多余的测试本身。 Try on godbolt;请注意,生成的程序集根本不包含对 fopen 的调用。所以这个用 GCC 编译的程序是没有漏洞的。如果启用优化(-O1 或更高版本),clang 也是如此。

(您可以通过使用-fwrapv 编译来强制任一编译器中的“幼稚”行为。还有-ftrapv,如果发生有符号整数溢出,它会强制程序中止;它具有相当大的运行时性能成本,但当安全性至关重要时可能是可取的。)

因此,对于这样的攻击,你不仅要阅读易受攻击程序的源代码,还要能够发现或猜测受害者实际使用的编译代码中的内容。

【讨论】:

以上是关于整数溢出不溢出?的主要内容,如果未能解决你的问题,请参考以下文章

整数溢出问题

将枚举转换为具有溢出的整数时会出现警告

假设分母<>0,整数除法是不是会溢出/下溢? [复制]

整数溢出漏洞小结

软件安全实验——pre6(整数溢出堆溢出栈溢出漏洞预习)

RandomizedSearchCV 溢出错误:无法将“int”放入索引大小的整数中