简体   繁体   中英

Buffer overflow vulnerability

The following program:

#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");
 }
}

At line strcpy(password_buffer, password); it has a buffer overflow vulnerability. If we want to make this program secure without taking out the strcpy , how is it possible?

Easy. You change the verification that the user input is as expected else where. With strlen so that you can check that the string is shorter than 16 bytes.

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

I have found this on 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.

There's a few ways to deal with this.


First, and perhaps most important, is to avoid fixed memory allocations especially when dealing with input. In your example above instead of copying to a fixed size buffer you can make the buffer the size you need.

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

But that's not always possible. Maybe you've got already allocated memory. Maybe you do want to truncate the input (though 16 is far too small for a password ).


One is to not use the C string handling functions at all, they're riddled with flaws and security holes. Instead use a library like Gnome Lib which features the G_String type . G_Strings track their length and allocated size. This takes a little more memory, but it's faster. Finding the length of the string, something that happens a lot, doesn't require iterating through every byte of the string.

G_Strings have their own set of string handling functions which are much handier than the C ones. It also can grow strings as necessary or allocate new strings for you.

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

For compatibility with regular string functions password_copy->str returns a normal char * .

This is IMO the best way. You no longer have to remember to check string lengths and allocated sizes and worry about null bytes everywhere you use strings. You will forget. Let the computer do that.


If you must use C standard functions, don't use 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 .

For maximum portability, efficiency, and safety use strlcpy and provide a fallback using memmove and #ifndef strlcpy so it will only be used if strlcpy is not already available.

#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] = '\0';

    /* 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;
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM