Open In App

Security issues in C language

Improve
Improve
Like Article
Like
Save
Share
Report

C is a very powerful and popular programming language. It was first developed in the 1970s. C language is used in programming Network drivers, Interpreters, and Compilers, etc.
Even though the C language is widely used in different systems still it has many security flaws associated with it. This article focuses on discussing security vulnerabilities in the C language. Mainly these security issues are related to vulnerable library functions, No bound checking for array and Pointers.

Vulnerable Library Functions:
CWE Codes: CWE – 242 , CWE – 120, CWE-77

1. Buffer And Memory Related:

a. gets(): This function is a part of the standard input-output library of the C language. It does not have any check for buffer size and malicious input can easily cause a buffer overflow.
Below is the C program to demonstrate the above concept-

C




// C program to implement
// the above approach
#include <stdio.h>
 
// Driver code
int main()
{
    char buf[24];
    printf("Please enter your name and press <Enter>\n");
    gets(buf);
    printf("%s", buf);
    return 0;
}


Warning:

prog.c: In function ‘main’: 
prog.c:10:1: warning: implicit declaration of function ‘gets’ 
[-Wimplicit-function-declaration] 
gets(buf); 

/tmp/ccmwzcCQ.o: In function `main’: 
69c380139e12adbab30e0550af51f5a9.c:(.text+0x2e): warning: the `gets’ 
function is dangerous and should not be used. 

Output:

Input: 
GeeksforGeeks
Output: 
Please enter your name and press 
GeeksforGeeks 

Explanation:
In the above code, the attacker can give large chunk of data as input, and the gets() function will try to store all that data into the buffer without considering the buffer size which will eventually cause a buffer overflow situation that can further cause arbitrary code execution, information leak.

Mitigation:
To solve this issue with the gets() function. Programmers can use fgets() function. It limits the input length based on the buffer size.
Below is the C program to implement the above approach- 

C




// C program to implement
// the above approach
#include <stdio.h>
#define MAX 15
 
// Driver code
int main()
{
    char buf[MAX];
    fgets(buf, MAX, stdin);
    printf("%s", buf);
    return 0;
}


Output:

Input: GeeksforGeeks
Output: GeeksforGeeks 

Explanation:
In the above code, fgets() function is taking buffer size ‘MAX’ as an argument and it will only write data that can be safely stored into the buffer of that size without overflowing it.

b. strcpy(): This built-in function also doesn’t check for the buffer length and can overwrite memory locations based on the malicious input.If the buffer size of dest string is more than src string, then copy the src string to dest string with terminating NULL character. But if dest buffer is less than src then it will copy the content without terminating NULL character. The strings may not overlap, and the destination string must be large enough to receive the copy.
Below is the C program to demonstrate the above concept-

C




// C program to implement
// the above approach
#include <stdio.h>
#include <string.h>
 
// Driver code
int main()
{
    char str1[2];
    char str2[] = "GeeksforGeeks";
    strcpy(str1, str2);
    printf("Copied string is: %s\n", str1);
    return 0;
}


Output:

Input: 
GeeksforGeeks
Output: 
Copied string is: GeeksforGeeks 

Explanation:
In the above code, strcpy() function is trying to copy the string available on str2[] to str1[] which does not have enough space in a buffer allocated to handle that which will eventually result in a buffer overflow in the application.

Mitigation:
Use of strncpy() function instead of strcpy(). strncpy() limits the length of input based on the buffer size available.
Below is the C program to demonstrate the above concept-

C




// C program to implement
// the above approach
#include <stdio.h>
#include <string.h>
#define BUFFER_SIZE 24
 
// Driver code
int main()
{
    // 24 is the buffer size
    char str1[BUFFER_SIZE];
    char str2[] = "GeeksforGeeks";
 
    // Limits number of characters
    // to be copied
    strncpy(str1, str2, BUFFER_SIZE);
    printf("Copied string is: %s\n", str1);
    return 0;
}


Output:

Input: 
GeeksforGeeks
Output: 
Copied string is: GeeksforGeeks 

Explanation:
In the above code, strncpy() function is taking BUFFER_SIZE as an argument and it will only write data that can be safely stored into the str1[ ] without overflowing the buffer. Using strcpy() function to copy a large character array into a smaller one is dangerous, but if the string will fit, then it will not be worth the risk. If the destination string is not large enough to store the source string then the behavior of strcpy() is unspecified or undefined.

Note:
Other library functions with same type of vulnerability- calloc, malloc, realloc, strcat, memcpy.

2. Command execution Vulnerabilities:
If the attacker can control the command text or arguments to an external function call, then he can run arbitrary codes very easily.
Below is the C program to demonstrate the above concept- 

C




// C program to implement
// the above approach
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
 
// Driver code
int main()
{
    char str[40];
    fgets(str, 39, stdin);
    system(str);
    printf("%s", str);
}


Runtime Errors:

sh: 1: GeeksforGeeks: not found

Output:

Input: 
GeeksforGeeks
Output: 
GeeksforGeeks

Explanation:
In the above code, if the str is controlled by the attacker then he can execute any command to the system and can put the entire system at risk. Observed CVEs are CVE-1999-0067, CVE-2019-12921.

Mitigation:

  • Ensure that all external commands called from the program are statically created.
  • Use library calls rather than external processes to recreate the desired functionality.

Note:
Other library functions with the same type of vulnerability: execl, execle, popen. 

3. Format String Vulnerabilities:
Format specifiers like %d, %s if used in an improper way within the code than it may give the edge to the attacker for gaining access for arbitrary code execution, information leak and can also completely control the application. If successful, it returns the total number of characters written excluding null-character appended in the string, in case of failure a negative number is returned. 
Below is the C program to demonstrate the above concept-

C




// C program to implement
// the above approach
#include <stdio.h>
 
// Driver code
int main()
{
    char str[5];
    sprintf(str, "%s",
            "GGGGGGGGGGGGGG");
    printf("%s", str);
}


Runtime Error:

Abort signal from abort(3) (SIGABRT) 

Explanation:
In this above code, the sprintf function with format specifier %s may cause a buffer overflow because the output size is 15 which is greater than the size of buffer received that is of size 5 only. 
Besides this, the str[ ] can also be controlled by a external agent and cause all the above mentioned vulnerabilities (CWE – 13 ). Observed CVEs are CVE-2001-0717, CVE-2002-1788, CVE-2006-2480 etc.

Mitigation:

  • Choose a language not have such flaws.
  • Ensure that all format string functions are passed a static string that cannot be controlled by the user.
  • Use functions that do not support the %n operator in format strings.

Note:
Other library functions with the same type of vulnerability: fprintf, printf, sprintf, snprintf.

Concept of Pointer:
The pointer concept causes multiple security issues with the C programming language.

1. NULL Pointer Dereference: CWE CODE: CWE-476. If a program dereference a pointer that is expected to be valid but turns out as NULL then it causes program crash, exit. Mitigation to this is to do a check for the NULL pointer before performing any operation. Observed CVEs are CVE-2005-3274, CVE-2005-1912, CVE-2004-0079 etc.
Below is the C program to demonstrate the above concept-

C




// C program to implement
// the above approach
#include <stdio.h>
 
// Driver code
int main()
{
    int val = 1;
    int* p = NULL;
    *p = val;
    printf("%d", *p);
    return 0;
}


Runtime Error:

Segmentation Fault (SIGSEGV)

Explanation:
In this code, *p is a NULL pointer which means it does not point to a memory location and trying to dereference it results in unexpected behavior in the program or segmentation fault.

Mitigation:
Below is the C program to demonstrate the mitigation strategy for the above problem-

C




// C program to implement
// the above concept
#include <stdio.h>
 
// Driver code
int main()
{
    int val = 1;
    int* p = NULL;
    if (p == NULL) {
        printf("Pointer is NULL");
    }
    else {
        *p = val;
        printf("%d", *p);
    }
    return 0;
}


Output:

Pointer is NULL 

Explanation:
In this code, the first check is performed for a NULL pointer, before doing any operation on a pointer. Here is the if statement checks for the NULL condition and if it is found to be true then no further operation is getting executed.

2. Use after Free(Commonly referred to as Dangling pointer): CWE CODE: CWE-416. If a referencing memory is freed and then there is any attempt made to free that again, then it cause this situation. It can cause a program crash, information leak, and data corruption. Observed CVEs are CVE-2010-4168, CVE-2010-2941, CVE-2010-2547 etc.
Below is the C program to demonstrate the above concept-

C




// C program to implement
// the above approach
#include <stdio.h>
int* fun()
{
    int y = 10;
    return &y;
}
 
// Driver code
int main()
{
    int* p = fun();
    printf("%d", *p);
    return 0;
}


Output: 

Segmentation Fault (SIGSEGV)

Explanation:
In the above code, the main() function the  *p contains the return value of the fun(). With the call of fun() the control moves to the context of the int *fun(), the fun() returns the address of the ‘y’ variable but in the context of main() ‘y’ is no longer available after the program flow returned. Therefore, it can be said that the *p is a dangling pointer as it points to the de-allocated memory.

Mitigation:
After freeing pointers set them to NULL. Below is the C program to demonstrate the above concept- 

C




// C program to implement
// the above approach
#include <stdio.h>
#include <stdlib.h>
 
// Driver code
int main()
{
    int n = 5;
    int* ptr;
    ptr = (int*)malloc(n * sizeof(int));
 
    // Memory has been successfully allocated
    printf("Memory successfully allocated using malloc.\n");
 
    // Free the memory
    free(ptr);
    printf("Malloc Memory successfully freed.\n");
 
    // freed pointer set to NULL value
    ptr = NULL;
 
    return 0;
}


Output:

Memory successfully allocated using malloc. 
Malloc Memory successfully freed. 

Explanation:
In this above code, after free(ptr) operation the ptr pointer value is set to NULL and it will mitigate the chances of a dangling pointer.

No bound Checking for Array:

1. Out of Bounds Write: CWE CODE: CWE-787 In this, the software writes the data before or after the intended buffer. It may cause execution of unauthorized codes, Crash and restart. Observed CVEs are CVE-2020-0022, CVE-2009-0269, CVE-2009-1532 etc.
Below is the C program to demonstrate the above concept-

C




// Program to demonstrate
// accessing array out of bounds
#include <stdio.h>
int main()
{
    int arr[] = { 1, 2, 3, 4, 5 };
    printf("arr [0] is %d\n", arr[0]);
    printf("arr[10] is %d\n", arr[10]);
 
    // allocation memory to out of bound
    // element
    arr[10] = 11;
    printf("arr[10] is %d\n", arr[10]);
    return 0;
}


Runtime Error:

Segmentation Fault (SIGSEGV)

Explanation:
In this above code, array has valid indexes 0, 1, 2, 3, and 4 but there is an attempt to write the value on the arr[10]. As the C compiler will not check for the array bound it will write that data after the intended buffer. But when there is an attempt to print the value at index 10, it will show an error.

Mitigation:

  • Always make sure the buffer is large enough.
  • Functions used to accept input should have a buffer limit implementation.

Below is the C program to demonstrate the above concept-

C




// C program to implement
// the above approach
#include <stdio.h>
int arr[5];
 
// Driver code
int main()
{
    int size = sizeof(arr) / sizeof(arr[0]);
 
    for (int i = 0; i < size; i++) {
        scanf("%d", &arr[i]);
    }
 
    // Print elements of array
    for (int i = 0; i < size; i++) {
        printf("%d ", arr[i]);
    }
    return 0;
}


Output: 

Input: 1 2 3 4 5 6 7 8 9 10
Output: 1 2 3 4 5 

Explanation:
In the above code, the sizeof operator is being used to determine the size of the array so that one can get the valid range of the index. sizeof(arr) will give the entire size of the array and sizeof(arr[0]) will give the size of one element so using the divide operation one can easily find the number of elements in the array followed by the range of the index.

2. Out of Bounds Read: CWE CODE: CWE-125. In this, the software reads the data before or after the intended buffer. Attackers may use this to read sensitive information from other memory locations or may cause crashes.Observed CVEs are CVE-2014-0160, CVE-2009-2523, CVE-2004-0184 etc.
Below is the C program to demonstrate the above concept-

C




// C program to implement
// the above approach
#include <stdio.h>
int getValueFromArray(int* arr,
                      int size,
                      int index)
{
    int value = -1;
 
    // only maximum limit is there
    if (index < size) {
        value = arr[index];
    }
    return value;
}
 
// Driver code
int main()
{
    int arrtest[] = { 1, 2, 3, 4, 5 };
    int j = getValueFromArray(arrtest, 5, -1);
    printf("%d", j);
    return 0;
}


Output:

0

Explanation:
In this above code, the if statement within the function only checking the index value is less than the size of the array or not. The attacker may give a negative value as an index even that is not valid still the read operation will be executed which will eventually cause the reading of unintended data.

Mitigation:

  • Input validation.
  • Ensure to validate correct calculations for length of an argument, buffer size, etc.

Below is the C program to demonstrate the above concept-

C




// C program to implement
// the above approach
#include <stdio.h>
int getValueFromArray(int* arr,
                      int size,
                      int index)
{
    int value = -1;
 
    // only maximum limit and minimum
    // limit is there
    if (index >= 0 && index < size) {
        value = arr[index];
    }
    return value;
}
 
// Driver code
int main()
{
    int arrtest[] = { 1, 2, 3, 4, 5 };
    int j = getValueFromArray(arrtest, 5, -1);
    printf("%d", j);
    return 0;
}


Output: 

-1

Explanation:
In this code, both maximum and minimum index range is checked in the if statement. If the attacker tries to give invalid indexes then the read operation will not be performed.



Last Updated : 05 Dec, 2022
Like Article
Save Article
Previous
Next
Share your thoughts in the comments
Similar Reads