CWE-676: Use of Potentially Dangerous Function
Overview
Dangerous functions (strcpy, gets, system, eval, exec) lack bounds checking, enable code execution, or have inherent security flaws. Using them creates buffer overflows, command injection, code injection, and other vulnerabilities that modern alternatives prevent.
OWASP Classification
A06:2025 - Insecure Design
Risk
High: Dangerous functions cause buffer overflows (strcpy, gets), command injection (system, exec), code injection (eval), race conditions (access), and predictable randomness (rand), with exploits well-documented and widely used in attacks.
Remediation Strategy
Replace Unsafe String Functions (Primary Defense)
// DANGEROUS - no bounds checking
char dest[10];
strcpy(dest, user_input); // Buffer overflow!
gets(dest); // Extremely dangerous
strcat(dest, more_input); // No bounds check
sprintf(dest, "%s", input); // No bounds check
// SAFE - bounds-checked alternatives
strncpy(dest, user_input, sizeof(dest) - 1);
dest[sizeof(dest) - 1] = '\0';
fgets(dest, sizeof(dest), stdin); // Safe
snprintf(dest, sizeof(dest), "%s", input); // Safe
strlcpy(dest, input, sizeof(dest)); // BSD
Replace Command Execution Functions
# DANGEROUS - command injection
import os
filename = request.args.get('file')
os.system(f'cat {filename}') // Injection!
# Attacker: file=file.txt; rm -rf /
# SAFE - use subprocess with array
import subprocess
result = subprocess.run(
['cat', filename], # Arguments as array
capture_output=True,
text=True,
check=True
)
Replace Dynamic Code Execution
# DANGEROUS
code = request.form['code']
eval(code) # Remote code execution!
exec(code) # Remote code execution!
# SAFE - use safe alternatives
# For math expressions
import ast
import operator
safe_ops = {
ast.Add: operator.add,
ast.Sub: operator.sub,
ast.Mult: operator.mul,
ast.Div: operator.truediv
}
def safe_eval(expr):
tree = ast.parse(expr, mode='eval')
# Validate only safe operations
return eval_tree(tree.body, safe_ops)
Replace Weak Random Functions
// DANGEROUS - predictable
srand(time(NULL));
int token = rand(); // NOT cryptographically secure
// SAFE - cryptographic random
#include <openssl/rand.h>
unsigned char token[32];
RAND_bytes(token, sizeof(token));
// Or /dev/urandom on Unix
int fd = open("/dev/urandom", O_RDONLY);
read(fd, token, sizeof(token));
close(fd);
Remediation Steps
Core principle: Ban inherently dangerous functions in security-sensitive code; enforce via tooling and code review.
- Identify dangerous function calls
- Determine the risk
- Locate data flow to dangerous function
- Replace with safe alternatives: Use bounds-checked functions (strncpy, snprintf), parameterized execution (subprocess with arrays), crypto random (RAND_bytes)
- Add validation where replacement isn't possible: If dangerous function must be used, add strict input validation and bounds checking
- Test with attack payloads: Try buffer overflows, command injection, code injection to verify safe replacement blocks attacks
- Grep codebase for other dangerous functions: Search for all instances and replace systematically
Dangerous Functions and Replacements
C String Functions:
- strcpy() → strncpy(), strlcpy(), strcpy_s()
- strcat() → strncat(), strlcat()
- gets() → fgets()
- sprintf() → snprintf()
- scanf("%s") → scanf("%99s") or fgets()
Command Execution:
- system() → execve() with sanitized args
- popen() → fork()+exec() with pipes
- exec() (Python) → Don't use with user input
- eval() → JSON parsing, safe expression evaluators
File Operations:
- access() → open() (avoids TOCTOU)
- tmpnam() → mkstemp()
- tempnam() → mkstemp()
Random:
- rand(), random() → /dev/urandom, RAND_bytes()
- Math.random() (JS) → crypto.getRandomValues()
- Random() (Python) → secrets module
Memory:
- alloca() → malloc() or VLA
- realpath() → Check return value
Examples of Vulnerable Code
// Buffer overflow
void process_name(char *name) {
char buffer[64];
strcpy(buffer, name); // Overflow if name > 63 chars
printf("%s\n", buffer);
}
// Command injection
void backup_file(char *filename) {
char cmd[256];
sprintf(cmd, "cp %s %s.bak", filename, filename);
system(cmd); // Shell interprets special chars!
}
// Code injection
void calculate(char *expression) {
eval(expression); // Arbitrary code execution!
}
Secure Alternatives
// Bounded string copy
void process_name_safe(const char *name) {
char buffer[64];
snprintf(buffer, sizeof(buffer), "%s", name);
printf("%s\n", buffer);
}
// Safe command execution
void backup_file_safe(const char *filename) {
pid_t pid = fork();
if (pid == 0) {
// Child process
char *args[] = {"cp", filename,
generate_backup_name(filename), NULL};
execvp("cp", args);
_exit(1);
}
waitpid(pid, NULL, 0);
}
# Safe expression evaluation
import ast
class SafeEvaluator(ast.NodeVisitor):
def visit(self, node):
if not isinstance(node, (ast.Expression, ast.Num,
ast.BinOp, ast.Add, ast.Sub)):
raise ValueError("Unsafe operation")
return super().visit(node)
def safe_eval(expr):
tree = ast.parse(expr, mode='eval')
SafeEvaluator().visit(tree)
return eval(compile(tree, '<string>', 'eval'))