整数溢出不溢出?
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", &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
,如果发生有符号整数溢出,它会强制程序中止;它具有相当大的运行时性能成本,但当安全性至关重要时可能是可取的。)
因此,对于这样的攻击,你不仅要阅读易受攻击程序的源代码,还要能够发现或猜测受害者实际使用的编译代码中的内容。
【讨论】:
以上是关于整数溢出不溢出?的主要内容,如果未能解决你的问题,请参考以下文章