Skip to content

CWE-242: Use of Inherently Dangerous Function

Overview

Use of inherently dangerous functions occurs when code uses functions known to be unsafe (strcpy, gets, eval, exec, system) that lack bounds checking, enable code injection, or bypass security controls, leading to buffer overflows, code execution, or injection attacks.

Risk

High: Dangerous functions cause buffer overflows (strcpy, sprintf), arbitrary code execution (eval, exec, system), format string bugs (printf with user input), race conditions (tmpnam), and SQL injection (string concatenation). These are commonly exploited in attacks.

Remediation Steps

Core principle: Avoid dangerous APIs/functions; replace with safe alternatives and enforce bans.

Locate Dangerous Function Usage

When reviewing security scan results:

  • Find unsafe C functions: strcpy, sprintf, gets, strcat, scanf
  • Check dynamic code execution: eval(), exec(), system(), shell_exec()
  • Identify format string bugs: printf/sprintf with user input as format
  • Look for SQL string building: String concatenation for queries
  • Check temp file functions: tmpnam, mktemp (race conditions)

Common dangerous functions:

// C/C++ - Buffer overflow risks
strcpy(dest, src);      // No bounds checking
sprintf(buf, "%s", user);  // No size limit
gets(buffer);           // Always unsafe
strcat(dest, src);      // No bounds checking
scanf("%s", buf);       // No size limit

// Dynamic code execution
eval(user_input);       // JavaScript/PHP/Python
exec(command);          // Python/PHP
system(cmd);            // C/PHP/Ruby
shell_exec($cmd);       // PHP

// Format strings
printf(user_input);     // Format string vulnerability
syslog(LOG_INFO, user); // Format string vulnerability

Replace with Safe Alternatives (Primary Defense)

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

// VULNERABLE - strcpy has no bounds checking
void copy_string_bad(const char *src) {
    char dest[64];

    // Attack: src longer than 64 bytes = buffer overflow
    strcpy(dest, src);  // NO BOUNDS CHECKING!
}

// SECURE - use strncpy with explicit size
void copy_string_safe(const char *src) {
    char dest[64];

    // Copy at most sizeof(dest)-1 bytes
    strncpy(dest, src, sizeof(dest) - 1);

    // Always null-terminate
    dest[sizeof(dest) - 1] = '\0';
}

// BETTER - use strlcpy (BSD) if available
void copy_string_better(const char *src) {
    char dest[64];

    // strlcpy always null-terminates and returns source length
    size_t len = strlcpy(dest, src, sizeof(dest));

    if (len >= sizeof(dest)) {
        // Source was truncated
        fprintf(stderr, "String truncated\n");
    }
}

// BEST - use C11 strcpy_s if available
void copy_string_best(const char *src) {
    char dest[64];

    // strcpy_s checks bounds and returns error
    errno_t result = strcpy_s(dest, sizeof(dest), src);

    if (result != 0) {
        fprintf(stderr, "String copy failed\n");
        return;
    }
}

// VULNERABLE - sprintf has no size limit
void format_string_bad(const char *username) {
    char message[100];

    // Attack: username > 93 chars = buffer overflow
    sprintf(message, "Hello, %s!", username);
}

// SECURE - snprintf with size limit
void format_string_safe(const char *username) {
    char message[100];

    // snprintf limits output to buffer size
    int written = snprintf(message, sizeof(message), "Hello, %s!", username);

    if (written >= sizeof(message)) {
        fprintf(stderr, "Output truncated\n");
    }
}

// VULNERABLE - gets() is always unsafe
void read_input_bad() {
    char buffer[100];

    // NEVER USE gets() - no way to limit input!
    gets(buffer);  // Removed from C11 standard
}

// SECURE - use fgets with size limit
void read_input_safe() {
    char buffer[100];

    // fgets limits input to buffer size
    if (fgets(buffer, sizeof(buffer), stdin)) {
        // Remove trailing newline
        buffer[strcspn(buffer, "\n")] = 0;
    }
}

Function replacement table: | Dangerous | Safe Alternative | |-----------|------------------| | strcpy | strncpy, strlcpy, strcpy_s | | sprintf | snprintf, sprintf_s | | strcat | strncat, strlcat, strcat_s | | gets | fgets | | scanf | fgets + sscanf, scanf_s | | tmpnam | mkstemp |

Avoid Dynamic Code Execution

// VULNERABLE - eval with user input
function calculate_bad(expression) {
    // Attack: expression = "process.exit()" or "require('fs').unlinkSync('/')"
    const result = eval(expression);  // ARBITRARY CODE EXECUTION!
    return result;
}

// SECURE - use safe parser
const math = require('mathjs');

function calculate_safe(expression) {
    try {
        // mathjs parser only allows math operations
        const result = math.evaluate(expression);
        return result;
    } catch (e) {
        throw new Error('Invalid expression');
    }
}

// VULNERABLE - system() with user input
const { exec } = require('child_process');

function process_file_bad(filename) {
    // Attack: filename = "file.txt; rm -rf /"
    exec(`cat ${filename}`, (error, stdout) => {
        // COMMAND INJECTION!
    });
}

// SECURE - use array form (no shell)
const { execFile } = require('child_process');

function process_file_safe(filename) {
    // Validate filename first
    if (!/^[a-zA-Z0-9_.-]+$/.test(filename)) {
        throw new Error('Invalid filename');
    }

    // Use execFile with array (no shell interpretation)
    execFile('cat', [filename], (error, stdout) => {
        if (error) {
            console.error('Error:', error);
            return;
        }
        console.log(stdout);
    });
}

// BETTER - use Node.js fs module directly
const fs = require('fs');

function process_file_better(filename) {
    // No shell involved at all
    fs.readFile(filename, 'utf8', (err, data) => {
        if (err) {
            console.error('Error:', err);
            return;
        }
        console.log(data);
    });
}

Fix Format String Vulnerabilities

#include <syslog.h>

// VULNERABLE - user input as format string
void log_message_bad(const char *user_message) {
    // Attack: user_message = "%s%s%s%s%s%s" = crash or info leak
    syslog(LOG_INFO, user_message);  // FORMAT STRING BUG!
}

// SECURE - use %s format specifier
void log_message_safe(const char *user_message) {
    // User input is data, not format string
    syslog(LOG_INFO, "%s", user_message);
}

// VULNERABLE - printf with user input
void display_error_bad(const char *error) {
    // Attack: error = "%x%x%x%x" = memory disclosure
    printf(error);  // FORMAT STRING BUG!
}

// SECURE - explicit format string
void display_error_safe(const char *error) {
    printf("%s\n", error);
}

Use Safe Temporary File Creation

#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>

// VULNERABLE - tmpnam has race condition
void create_temp_bad() {
    char tmpfile[L_tmpnam];

    // Race condition: file could be created between tmpnam and fopen
    tmpnam(tmpfile);

    FILE *f = fopen(tmpfile, "w");  // RACE CONDITION!
}

// SECURE - mkstemp atomically creates file
void create_temp_safe() {
    char template[] = "/tmp/myapp-XXXXXX";

    // mkstemp creates file atomically with unique name
    int fd = mkstemp(template);

    if (fd == -1) {
        perror("mkstemp failed");
        return;
    }

    // Use file descriptor
    FILE *f = fdopen(fd, "w");

    // Remember to unlink when done
    unlink(template);
}

Test for Dangerous Function Usage

// Test buffer overflow prevention
#include <assert.h>
#include <string.h>

void test_safe_string_copy() {
    char dest[10];
    const char *long_string = "This is a very long string that exceeds buffer";

    // Test 1: Short string fits
    const char *short_string = "hello";
    strncpy(dest, short_string, sizeof(dest) - 1);
    dest[sizeof(dest) - 1] = '\0';
    assert(strcmp(dest, "hello") == 0);
    printf("✓ Short string copy safe\n");

    // Test 2: Long string truncated
    strncpy(dest, long_string, sizeof(dest) - 1);
    dest[sizeof(dest) - 1] = '\0';
    assert(strlen(dest) == 9);  // Truncated to fit
    printf("✓ Long string truncated safely\n");

    // Test 3: Always null-terminated
    assert(dest[9] == '\0');
    printf("✓ String always null-terminated\n");
}

void test_safe_format() {
    char buffer[20];

    // Test 1: Normal case
    int written = snprintf(buffer, sizeof(buffer), "Count: %d", 42);
    assert(written < sizeof(buffer));
    assert(strcmp(buffer, "Count: 42") == 0);
    printf("✓ snprintf normal case\n");

    // Test 2: Truncation detected
    written = snprintf(buffer, sizeof(buffer), "Very long message: %d", 12345);
    assert(written >= sizeof(buffer));  // Indicates truncation
    assert(buffer[sizeof(buffer) - 1] == '\0');  // Still null-terminated
    printf("✓ snprintf truncation handled\n");
}

int main() {
    test_safe_string_copy();
    test_safe_format();
    printf("All dangerous function replacement tests passed!\n");
    return 0;
}

Static analysis checks:

# Enable compiler warnings for dangerous functions
gcc -Wall -Wextra -Wformat-security -Werror=format-security \

    -D_FORTIFY_SOURCE=2 program.c

# Use clang static analyzer
clang --analyze program.c

# Use cppcheck
cppcheck --enable=warning,style program.c

# Bandit for Python (checks eval, exec, etc.)
bandit -r .

# ESLint for JavaScript (no-eval rule)
eslint --rule 'no-eval: error' .

Common Vulnerable Patterns

  • strcpy() without bounds checking
  • gets() - always unsafe, no bounds
  • sprintf() without size limits
  • eval() with user input
  • system() with unsanitized input

Security Checklist

  • No strcpy - replaced with strncpy/strlcpy/strcpy_s
  • No sprintf - replaced with snprintf
  • No gets - replaced with fgets
  • No eval() with user input
  • No system()/exec() with user input
  • No user input as format strings
  • No tmpnam - replaced with mkstemp
  • Compiler warnings enabled and treated as errors
  • Static analysis tools report no dangerous functions

Additional Resources