flawopen.com/Command Injection/C/C++
Imagine a train operator who announces stations. If an attacker slips a note saying 'Next stop Central; derail train', command injection is the system executing the derail instruction because it didn't separate the station name from the train controls.
In C and C++, system(const char *command) and popen() are the simplest ways to run a sub-process, but both invoke /bin/sh -c. Concatenating user buffers with sprintf or strcat results in full remote command execution.
Embedded devices, IoT firmware, and networking appliances (routers, firewalls, IP cameras) overwhelmingly suffer from command injection in C-based CGI binaries and web management interfaces.
CISA KEV (Known Exploited Vulnerabilities) Catalog.// system() invokes /bin/sh with sprintf buffer #include#include void ping_host(const char *user_input) { char buffer[256]; // Attacker input: "127.0.0.1; reboot" snprintf(buffer, sizeof(buffer), "ping -c 1 %s", user_input); system(buffer); }
// fork() and execvp() execute binary directly without a shell #include#include void ping_host(const char *user_input) { pid_t pid = fork(); if (pid == 0) { // Child process: arguments are discrete pointers char *args[] = {"ping", "-c", "1", (char *)user_input, NULL}; execvp("ping", args); _exit(1); } else { waitpid(pid, NULL, 0); } }
execvp passes the pointer array directly to the Linux/Unix kernel. The kernel replaces the child process image with ping; no shell interpreter exists, so metacharacters have no effect.
popen(cmd, "r") has the exact same vulnerability as system() because it also launches /bin/sh to manage the pipe.
std::string prevents buffer overflows, but passing an std::string.c_str() to system() is still 100% vulnerable to command injection.
grep -rn "system(" --include="*.c" --include="*.cpp" .
grep -rn "popen(" --include="*.c" --include="*.cpp" .
security.insecureAPI.system in your compiler flags.system() and popen() with fork() and execvp()-Wall -WextraCreate an OS pipe using pipe(), redirect stdout in the child process using dup2(), and read from the pipe in the parent process after fork().