【问题标题】:Buffer overflows - overwriting a string缓冲区溢出 - 覆盖字符串
【发布时间】:2013-10-22 02:32:29
【问题描述】:

我在发现一个问题时遇到了一些麻烦。鉴于以下情况:

int match(char *s1, char *s2) {
    while( *s1 != '\0' && *s2 != '\0' && *s1 == *s2 ){
        s1++; s2++;
    }
    return( *s1 - *s2 );
}

int main() {
    char str1[8], str2[8];
    scanf("%s", str1);
    scanf("%s", str2);
    if (match(str1, str2) == 0)
        printf("They are the same.\n");
    else
        printf("They are not the same.\n");
}

哪两个不同值的输入字符串可用于使程序打印消息“它们是相同的”? (以上代码不可更改)

我知道当数组被添加到堆栈中时,它们会被“推入”其中,并且信息会以相同的方向写入。因此,如果我在 str2 中输入“AAAAAAAAA”(A x 9),它会溢出并且 str1 会打印“A”。

我的第一次尝试是为 str2 输入 A x 16,希望程序会用 8 个 A 覆盖 str1 中的值,并且程序只会读取 str2 中的 8 个值。 str1 确实有 A x 8 的值,但 str2 保留了 A x 16 的值。

有没有办法用它来解决这个问题?还是我想错了?

编辑:这个问题是在特定机器上运行的,它具有过时的、因此易受攻击的 Linux 版本。我通过 gdb 运行程序,它显示两个字符串在内存中彼此相邻,并且 str2 溢出到 str1。那么我的问题是,我可以使用它来使 str2 和 str1 在比较它们时看起来与程序相同吗?

【问题讨论】:

  • 为什么第二个变量的输入要覆盖第一个变量的内容?
  • 好吧,我认为这是解决此问题的唯一方法。程序怎么会被“愚弄”?

标签: c buffer-overflow


【解决方案1】:

哪两个不同值的输入字符串可用于使程序打印消息“它们是相同的”? (以上代码不可更改)

不存在这样定义明确的场景。这些要求毫无意义。

我知道当数组被添加到堆栈中时,它们会被“推入”其中,并且信息会以相同的方向写入。因此,如果我在 str2 中输入“AAAAAAAAA”(A x 9),它会溢出并且 str1 会打印“A”。

这是不正确的。缓冲区 str1(可以包含 7 个字母 + 1 个空终止符)会溢出,从那里 任何事情都可能发生,您会调用未定义的行为。可能的未定义行为的一些示例是:分段错误/崩溃和烧毁,或程序似乎正常工作。

不能保证 str2 与 str1 相邻分配。也不能保证 str1 在 str2 之前分配。甚至没有任何保证它们被分配到堆栈上,尽管这很有可能。

有没有办法用它来解决这个问题?

没有。

还是我想错了?

是的。

【讨论】:

  • 这个问题是为了在特定系统上使用。我已经通过 gdb 运行程序,它显示字符串在内存中彼此相邻,并且 str2 溢出到 str1。
  • @MrDiggles 当谈到未定义的行为时,不幸的是,“观察”完全无关紧要,因为它们仅适用于您的特定编译二进制文件的当前版本。改变一些东西,然后再次编译,堆栈布局可能会完全不同。因此,即使您不将程序移植到另一个编译器或系统,它仍然会在每次编译之间崩溃和烧毁。
  • 我认为这仍然是一个有效的问题。任何事情都可能发生并不意味着任何事情都会发生。当您开发软件时,您必须采取这种观点,并确保它不会暴露未定义的行为。但是在检查现有软件时,您可以假设即使是未定义的行为在特定系统上也是可以预测的。否则,不可能利用现有软件中的缓冲区溢出 - 我们都知道情况并非如此。
  • @MrDiggles 设计竞赛问题的人很可能缺乏更深入的编程知识。我建议忘记竞争,而是专注于了解程序中漏洞的实际原因,即在生产代码中使用 scanf。像往常一样,C FAQ 有一些关于该主题的好信息。
  • @Axel 如果是这样,那么问题应该分发二进制文件,而不是高级编程语言源代码。
【解决方案2】:

我有另一个想法:如果输入“A”和“A A”会发生什么?根据this,sscanf 需要 '%s' 的非空白字符,因此它将在 "A A" 中的第一个 "A" 之后停止读取(注意空格字符)。 “缓冲区溢出”标签具有误导性。

【讨论】:

  • 哇哦,原来这么简单。我只是花了最后一个小时研究 gdb 中的地址。非常感谢!
  • 确实...很好的答案!
【解决方案3】:

您是否尝试过相反的方法?这取决于堆栈的布局方式。我会为str1尝试“AAAAAAAAAA”,为str2尝试“AAAAAAA”(7x'A')。 AFAIR 堆栈从上到下增长。

请参阅here 了解说明。

编辑:我知道如果你写超出数组限制会发生邪恶(未定义)的事情,但问题明确告诉不要改变程序。看起来像是了解软件开发中的安全风险的练习。

【讨论】:

  • 据我观察,只有 str2 可以覆盖 str1。
【解决方案4】:

覆盖缓冲区会调用未定义的行为。任何事情都有可能发生,所以你绝不能依赖它。

另外,str1 和 str2 在内存中不一定是相邻的,你不能依赖它。

可能的解决方案

char buffer[16];
char * str1 = &buffer[8];
char * str2 = &buffer[0];

这样,您可以安全地从str2 溢出到str1(但不能从str1 溢出到str2)。不过,您仍然应该进行适当的长度检查。

【讨论】:

  • 等等,你为什么说“在内存中不一定相邻”。他们会的。
  • @icbytes 没有什么能阻止实现向缓冲区之间的堆栈添加额外数据。
  • 这个问题具体说的是程序不能被改动。而我通过gdb运行程序,发现两个数组在内存中是相邻的。
  • @MrDiggles 他们彼此相邻在那个系统上。它不会改变覆盖缓冲区是未定义行为的事实,如果不修复程序,您将无法可靠地修复它。
  • @MrDiggles;情况并非总是如此。
【解决方案5】:

哪两个不同值的输入字符串可用于使程序打印消息“它们是相同的”?

你的字符串比较函数是错误的。 return( *s1 - *s2 ); 在这里毫无意义。将函数更改为

int match(char *s1, char *s2) {
    while( *s1 != '\0' && *s2 != '\0') {
        if(*s1 != *s2 )
             return 1;
        s1++; s2++;
    }

    return 0;
}

【讨论】:

  • 再想一想。有用。使用 sscanf() 是有问题的。
猜你喜欢
  • 1970-01-01
  • 2022-11-30
  • 1970-01-01
  • 2021-12-10
  • 2022-01-03
  • 2014-11-07
  • 2013-04-12
  • 1970-01-01
  • 2011-12-22
相关资源
最近更新 更多