首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >为什么这段代码容易受到缓冲区溢出的攻击?

为什么这段代码容易受到缓冲区溢出的攻击?
EN

Stack Overflow用户
提问于 2015-04-28 12:46:21
回答 5查看 26.6K关注 0票数 150
代码语言:javascript
复制
int func(char* str)
{
   char buffer[100];
   unsigned short len = strlen(str);

   if(len >= 100)
   {
        return (-1);
   }

   strncpy(buffer,str,strlen(str));
   return 0;
}

这段代码容易受到缓冲区溢出的攻击,我正在尝试找出原因。我认为这与len被声明为short而不是int有关,但我不是很确定。

有什么想法吗?

EN

回答 5

Stack Overflow用户

回答已采纳

发布于 2015-04-28 12:50:04

在大多数编译器上,unsigned short的最大值是65535。

任何高于该值的值都会被绕回,因此65536变成了0,65600变成了65。

这意味着正确长度的长字符串(例如65600)将通过检查,并使缓冲区溢出。

使用size_t存储strlen()的结果,而不是unsigned short,并将len与直接编码buffer大小的表达式进行比较。举个例子:

代码语言:javascript
复制
char buffer[100];
size_t len = strlen(str);
if (len >= sizeof(buffer) / sizeof(buffer[0]))  return -1;
memcpy(buffer, str, len + 1);
票数 194
EN

Stack Overflow用户

发布于 2015-04-28 13:00:42

问题出在这里:

代码语言:javascript
复制
strncpy(buffer,str,strlen(str));
                   ^^^^^^^^^^^

如果字符串大于目标缓冲区的长度,strncpy仍会将其复制过来。您将字符串的字符数作为要复制的数量,而不是缓冲区的大小。正确的方法如下:

代码语言:javascript
复制
strncpy(buffer,str, sizeof(buff) - 1);
buffer[sizeof(buff) - 1] = '\0';

这样做的目的是将复制的数据量限制为缓冲区的实际大小减去空终止字符的1。然后我们将缓冲区中的最后一个字节设置为空字符,以此作为额外的保护措施。这样做的原因是,如果strlen(str) < len - 1,strncpy将复制最多n个字节,包括终止null。如果不是这样,则null不会被复制,并且您有一个崩溃场景,因为现在您的缓冲区有一个未终止的字符串。

希望这能有所帮助。

编辑:在进一步检查和其他人的输入后,函数的可能编码如下:

代码语言:javascript
复制
int func (char *str)
  {
    char buffer[100];
    unsigned short size = sizeof(buffer);
    unsigned short len = strlen(str);

    if (len > size - 1) return(-1);
    memcpy(buffer, str, len + 1);
    buffer[size - 1] = '\0';
    return(0);
  }

因为我们已经知道字符串的长度,所以可以使用memcpy将字符串从str引用的位置复制到缓冲区中。请注意,对于strlen(3)的手册页(在FreeBSD 9.3系统上),说明如下:

函数的作用是:返回终止NUL字符之前的字符数。strnlen()函数返回与strlen()或maxlen ()相同的结果,取较小的值。

我将其解释为字符串的长度不包括null。这就是为什么我复制len +1个字节来包含null,并且测试检查以确保长度< size of buffer - 2.减1,因为缓冲区从位置0开始,并减去另一个以确保有容纳null的空间。

EDIT:结果是,某些东西的大小从1开始,而access从0开始,所以前面的-2是不正确的,因为它会为任何大于98字节的内容返回错误,但它应该大于99字节。

编辑:虽然关于无符号短整型的答案通常是正确的,因为可以表示的最大长度是65,535个字符,但这并不重要,因为如果字符串超过这个长度,值就会换行。这就好比取75,231 ( 0x000125DF),去掉前16位,得到9695 (0x000025DF)。我看到的唯一问题是超过65,535个字符的前100个字符,因为长度检查将允许复制,,但在所有情况下,它只复制字符串的前100个字符,并且null终止字符串。因此,即使存在环绕问题,缓冲区仍然不会溢出。

这本身可能会也可能不会带来安全风险,这取决于字符串的内容以及使用它的目的。如果它仅仅是人类可读的纯文本,那么通常没有问题。你只会得到一个截断的字符串。但是,如果它类似于URL,甚至是SQL命令序列,那么您可能会遇到问题。

票数 28
EN

Stack Overflow用户

发布于 2015-04-28 12:50:57

即使您使用的是strncpy,截止值的长度仍然取决于传递的字符串指针。您不知道该字符串有多长(即空结束符相对于指针的位置)。因此,仅调用strlen就会使您容易受到攻击。如果您想更安全,请使用strnlen(str, 100)

更正后的完整代码为:

代码语言:javascript
复制
int func(char *str) {
   char buffer[100];
   unsigned short len = strnlen(str, 100); // sizeof buffer

   if (len >= 100) {
     return -1;
   }

   strcpy(buffer, str); // this is safe since null terminator is less than 100th index
   return 0;
}
票数 11
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/29910520

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档