Use of Potentially Dangerous Function

Description

Use of Potentially Dangerous Function is a vulnerability where a program invokes library functions that can introduce security vulnerabilities when used incorrectly, even though they can be used safely with proper precautions. Unlike inherently dangerous functions (CWE-242) that can never be guaranteed safe, these functions pose risks primarily through improper usage. The classic example is strcpy()—it functions safely when the destination buffer is larger than the source, but its frequent misuse has led many security-conscious organizations to prohibit it entirely. Other examples include sprintf(), gets(), scanf() with %s, and various string manipulation functions that don't perform bounds checking.

Risk

Using potentially dangerous functions without proper precautions leads to buffer overflows, format string vulnerabilities, and other memory corruption issues. Buffer overflow vulnerabilities enable attackers to execute arbitrary code, crash applications, or escalate privileges. Even when developers believe they've validated input, edge cases or future code changes may introduce vulnerabilities. The high frequency of misuse makes these functions targets for security auditors and attackers alike. Legacy code using these functions represents ongoing security debt. Static analysis tools flag these patterns, but addressing them requires careful refactoring.

Solution

Maintain a list of prohibited or restricted functions with documented safer alternatives. Use safer replacement functions: strncpy() or strlcpy() instead of strcpy(), snprintf() instead of sprintf(), fgets() instead of gets(). When using restricted functions, implement rigorous input validation and buffer size checking. Enable compiler warnings that flag dangerous function usage. Use static analysis tools to detect banned functions during development. Consider memory-safe languages or safe string libraries for new development. Reference resources like Microsoft's banned.h or CERT Secure Coding Standards for comprehensive function restrictions.

Common Consequences

ImpactDetails
AvailabilityScope: Availability

DoS: Crash, Exit, or Restart - Buffer overflows from dangerous function misuse can crash applications.
IntegrityScope: Integrity

Modify Application Data - Memory corruption can alter program data and behavior.
ConfidentialityScope: Confidentiality, Integrity, Availability

Execute Unauthorized Code or Commands - Buffer overflow exploitation enables arbitrary code execution.

Example Code

Vulnerable Code

// Vulnerable: Using strcpy without bounds checking
#include <string.h>
#include <stdio.h>

void vulnerable_strcpy(char* input) {
    char buffer[64];

    // Vulnerable: No bounds checking - buffer overflow if input > 63 chars
    strcpy(buffer, input);

    printf("Copied: %s\n", buffer);
}

// Vulnerable: Using sprintf without bounds checking
void vulnerable_sprintf(const char* name, int id) {
    char message[128];

    // Vulnerable: Could overflow if name is very long
    sprintf(message, "User: %s, ID: %d, Status: Active, Role: Administrator",
            name, id);

    log_message(message);
}

// Vulnerable: Using gets (always dangerous)
void vulnerable_gets() {
    char input[100];

    printf("Enter command: ");
    // Vulnerable: gets() has no bounds checking at all
    gets(input);  // NEVER USE - deprecated and removed in C11

    process_command(input);
}

// Vulnerable: Using scanf with %s
void vulnerable_scanf() {
    char username[32];

    printf("Username: ");
    // Vulnerable: %s has no length limit
    scanf("%s", username);  // Buffer overflow possible

    authenticate(username);
}

// Vulnerable: Using strcat without bounds checking
void vulnerable_strcat(char* prefix, char* suffix) {
    char result[64];

    strcpy(result, prefix);
    // Vulnerable: No check if result has room for suffix
    strcat(result, suffix);  // Buffer overflow if combined length > 63

    use_result(result);
}

// Vulnerable: Using vsprintf
void vulnerable_log(const char* format, ...) {
    char buffer[256];
    va_list args;

    va_start(args, format);
    // Vulnerable: No bounds checking
    vsprintf(buffer, format, args);  // Overflow possible
    va_end(args);

    write_log(buffer);
}

// Vulnerable: Using realpath with fixed buffer (on some systems)
void vulnerable_realpath(const char* path) {
    char resolved[PATH_MAX];

    // Some implementations may overflow if PATH_MAX is insufficient
    // or if the resolved path exceeds buffer size
    char* result = realpath(path, resolved);

    if (result) {
        process_path(resolved);
    }
}

Fixed Code

// Fixed: Using strncpy/strlcpy with bounds checking
#include <string.h>
#include <stdio.h>

// Fixed: Safe string copy with explicit bounds
void secure_string_copy(char* input) {
    char buffer[64];

    // Option 1: strncpy (note: may not null-terminate!)
    strncpy(buffer, input, sizeof(buffer) - 1);
    buffer[sizeof(buffer) - 1] = '\0';  // Ensure null termination

    // Option 2: strlcpy (BSD/modern systems - preferred)
    // size_t result = strlcpy(buffer, input, sizeof(buffer));
    // if (result >= sizeof(buffer)) {
    //     // Truncation occurred
    //     handle_truncation();
    // }

    printf("Copied: %s\n", buffer);
}

// Fixed: Using snprintf with bounds checking
void secure_sprintf(const char* name, int id) {
    char message[128];

    // Fixed: snprintf respects buffer bounds
    int written = snprintf(message, sizeof(message),
                          "User: %s, ID: %d, Status: Active, Role: Administrator",
                          name, id);

    if (written >= (int)sizeof(message)) {
        // Truncation occurred - handle appropriately
        handle_truncation();
    }

    if (written > 0) {
        log_message(message);
    }
}

// Fixed: Using fgets instead of gets
void secure_gets() {
    char input[100];

    printf("Enter command: ");
    // Fixed: fgets has bounds checking and handles newline
    if (fgets(input, sizeof(input), stdin) != NULL) {
        // Remove trailing newline if present
        size_t len = strlen(input);
        if (len > 0 && input[len - 1] == '\n') {
            input[len - 1] = '\0';
        }
        process_command(input);
    }
}

// Fixed: Using scanf with width specifier
void secure_scanf() {
    char username[32];

    printf("Username: ");
    // Fixed: Width specifier limits input length
    if (scanf("%31s", username) == 1) {  // 31 = size - 1 for null
        authenticate(username);
    }

    // Better: Use fgets for user input
    // if (fgets(username, sizeof(username), stdin)) {
    //     username[strcspn(username, "\n")] = '\0';
    //     authenticate(username);
    // }
}

// Fixed: Using strncat with bounds checking
void secure_strcat(char* prefix, char* suffix) {
    char result[64];
    size_t prefix_len = strlen(prefix);
    size_t suffix_len = strlen(suffix);

    // Fixed: Check total length before concatenation
    if (prefix_len + suffix_len >= sizeof(result)) {
        handle_error("Combined string too long");
        return;
    }

    strcpy(result, prefix);  // Safe: we checked lengths
    strcat(result, suffix);  // Safe: we checked lengths

    // Or use strlcat:
    // strlcpy(result, prefix, sizeof(result));
    // strlcat(result, suffix, sizeof(result));

    use_result(result);
}

// Fixed: Using vsnprintf
void secure_log(const char* format, ...) {
    char buffer[256];
    va_list args;

    va_start(args, format);
    // Fixed: vsnprintf respects bounds
    int written = vsnprintf(buffer, sizeof(buffer), format, args);
    va_end(args);

    if (written > 0 && written < (int)sizeof(buffer)) {
        write_log(buffer);
    } else if (written >= (int)sizeof(buffer)) {
        // Truncation - log what we have
        write_log(buffer);
        write_log("[truncated]");
    }
}

// Fixed: Using realpath safely
void secure_realpath(const char* path) {
    // Option 1: Let realpath allocate (POSIX.1-2008)
    char* resolved = realpath(path, NULL);
    if (resolved) {
        process_path(resolved);
        free(resolved);  // Must free allocated memory
    }

    // Option 2: Use PATH_MAX buffer with validation
    // char resolved[PATH_MAX];
    // char* result = realpath(path, resolved);
    // if (result) {
    //     process_path(resolved);
    // }
}

// Example: Safer string library usage
// Using a safe string library (conceptual)
typedef struct {
    char* data;
    size_t length;
    size_t capacity;
} SafeString;

SafeString* safe_string_create(size_t initial_capacity) {
    SafeString* ss = malloc(sizeof(SafeString));
    if (ss) {
        ss->data = malloc(initial_capacity + 1);
        if (ss->data) {
            ss->data[0] = '\0';
            ss->length = 0;
            ss->capacity = initial_capacity;
        } else {
            free(ss);
            ss = NULL;
        }
    }
    return ss;
}

int safe_string_copy(SafeString* dest, const char* src) {
    size_t src_len = strlen(src);
    if (src_len > dest->capacity) {
        // Reallocate or return error
        char* new_data = realloc(dest->data, src_len + 1);
        if (!new_data) return -1;
        dest->data = new_data;
        dest->capacity = src_len;
    }
    memcpy(dest->data, src, src_len + 1);
    dest->length = src_len;
    return 0;
}

void safe_string_free(SafeString* ss) {
    if (ss) {
        free(ss->data);
        free(ss);
    }
}
// Banned function reference header (like Microsoft's banned.h)
#ifndef _BANNED_H_
#define _BANNED_H_

// Completely banned - no safe usage
#pragma deprecated(gets)
#pragma deprecated(gets_s)  // Still problematic

// Conditionally banned - use safer alternatives
#ifndef ALLOW_DANGEROUS_FUNCTIONS
#pragma deprecated(strcpy, "Use strlcpy or strncpy instead")
#pragma deprecated(strcat, "Use strlcat or strncat instead")
#pragma deprecated(sprintf, "Use snprintf instead")
#pragma deprecated(vsprintf, "Use vsnprintf instead")
#pragma deprecated(scanf, "Use fgets or specify width")
#pragma deprecated(sscanf, "Specify width limits")
#pragma deprecated(strncpy, "Use strlcpy for safer semantics")
#pragma deprecated(strncat, "Use strlcat for safer semantics")
#endif

#endif // _BANNED_H_

CVE Examples

  • CVE-2007-1470: Multiple buffer overflows via sprintf() and strcpy().
  • CVE-2009-3849: Buffer overflow from strcat() usage.
  • CVE-2006-2114: Buffer overflow through strcpy().
  • CVE-2008-5005: strcpy() buffer overflow vulnerability.
  • CVE-2011-0712: Vulnerable strcpy() replaced with strlcpy() as fix.

References

  1. MITRE Corporation. "CWE-676: Use of Potentially Dangerous Function." https://cwe.mitre.org/data/definitions/676.html
  2. Microsoft. "Security Development Lifecycle (SDL) Banned Function Calls."
  3. CERT C Coding Standard. "STR07-C. Use the bounds-checking interfaces for string manipulation."