缓冲区溢出漏洞

Buffer overflow vulnerability

以下程序:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
 int check_authentication(char *password) 
{
 int auth_flag = 0;
 char password_buffer[16];
 strcpy(password_buffer, password);
 if(strcmp(password_buffer, "unipi") == 0)
    auth_flag = 1;
 if(strcmp(password_buffer, "SSL") == 0)
    auth_flag = 1;
 return auth_flag;
}

int main(int argc, char *argv[]) {
 if(argc < 2) {
    printf("Usage: %s <password>\n", argv[0]);
    exit(0);
 }
 if(check_authentication(argv[1])!=0)
 {
    printf("\n-=-=-=-=-=-=-=-=-=-=-=-=-=-\n");
    printf(" Access Granted.\n");
    printf("-=-=-=-=-=-=-=-=-=-=-=-=-=-\n");
 }
 else
 {
     printf("\nAccess Denied.\n");
 }
}

在第 strcpy(password_buffer, password); 行它有一个缓冲区溢出漏洞。如果我们不去掉strcpy就想让这个程序安全,那怎么可能呢?

我在 Wiki 上找到了这个:

To prevent the buffer overflow from happening in this example, the call to strcpy could be replaced with strncpy, which takes the maximum capacity of A as an additional parameter and ensures that no more than this amount of data is written to A:

strncpy(password_buffer, password, sizeof(A));

Note that the above code is not free from problems either; while a buffer overrun has been prevented this time, the strncpy library function does not null-terminate the destination buffer if the source string's length is greater than or equal to the size of the buffer (the third argument passed to the function), therefore A is, in this case, not null-terminated and cannot be treated as a valid C-style string.

简单。 您在其他地方更改了用户输入符合预期的验证。 使用 strlen 以便您可以检查字符串是否短于 16 个字节。

if(strlen(argv[1]) > 15)
{
    printf("too long password\n");
    exit(0);
}

有几种方法可以解决这个问题。


首先,也许是最重要的是,避免固定内存分配,尤其是在处理输入时。在上面的示例中,您可以将缓冲区设置为您需要的大小,而不是复制到固定大小的缓冲区。

char password_copy[strlen(password) + 1];
strcpy(password_copy, password);

但这并不总是可能的。也许你已经分配了内存。也许您确实想截断输入(尽管 16 is far too small for a password)。


一个是根本不使用 C 字符串处理函数,它们充满了缺陷和安全漏洞。而是使用像 Gnome Lib 这样的库,它具有 the G_String type。 G_Strings 跟踪它们的长度和分配的大小。这需要更多的内存,但速度更快。查找字符串的长度,这是经常发生的事情,不需要遍历字符串的每个字节。

G_Strings have their own set of string handling functions 比 C 的方便多了。它还可以根据需要增加字符串或为您分配新字符串。

/* Allocate memory for the copy and copy the password */
G_String *password_copy = g_string_new(password);

为了与常规字符串函数兼容 password_copy->str returns 一个普通的 char *.

这是 IMO 的最佳方式。您不再需要记住检查字符串长度和分配的大小,也不必担心在您使用字符串的任何地方出现空字节。你会忘记的。让电脑来做。


如果必须使用 C 标准函数,请不要使用 strncpy because it fails to guarantee the truncated string will be null terminated. Instead use strlcpy. It's like strncpy but it guarantees the copied string will be null terminated. strlcpy is a BSD extension, so it's not guaranteed to be portable. glibc refuses to implement it

为了获得最大的便携性、效率和安全性,请使用 strlcpy 并使用 memmove#ifndef strlcpy 提供回退,因此仅当 strlcpy 尚未使用时才会使用它可用。

#include <string.h>
#include <stdio.h>

#ifndef strlcpy
size_t strlcpy(char * restrict dst, const char * restrict src, size_t dst_size) {
    /* size is the allocated size. len leaves space for the null byte */
    size_t dst_len = dst_size - 1;
    size_t src_len = strlen(src);

    /* Use the smaller of the two string lengths to avoid buffer overflow */
    size_t move_len = src_len > dst_len ? dst_len : src_len;

    /* Copy the string, truncate if necessary. It will work
     * even if src and dst overlap. */
    memmove(dst, src, move_len);

    /* Guarantee there's a null byte */
    dst[move_len] = '[=12=]';

    /* strlcpy returns the size of the string it tried to make.
     * This is used to detect truncation. */
    return src_len;
}
#endif

int main()
{
    char dst[10];
    char *src = "12345678901234567890";

    printf("%zu\n", strlcpy(dst, src, 10));
    printf("src: %s, dst: %s\n", src, dst);

    return 0;
}