Abstract:
Command injection is a cyber attack that involves executing arbitrary commands on a host operating system (OS). Typically, the threat actor injects the commands by exploiting an application vulnerability, such as insufficient input validation.
Explanation:
Command injection vulnerabilities take two forms:
- An attacker can change the command that the program executes: the attacker explicitly controls what the command is.
- An attacker can change the environment in which the command executes: the attacker implicitly controls what the command means.
Command injection vulnerabilities of this type occur when:
1. Data enters the application from an untrusted source.
2. The data is part of a string that is executed as a command by the application.
3. By executing the command, the application gives an attacker a privilege or capability that the attacker would not otherwise have.
Example 1: The following simple program accepts a filename as a command line argument and displays the contents of the file back to the user. The program is installed setuid root because it is intended for use as a learning tool to allow system administrators in-training to inspect privileged system files without giving them the ability to modify them or damage the system.
int main(char* argc, char** argv) {
char cmd[CMD_MAX] = "/usr/bin/cat ";
strcat(cmd, argv[1]);
system(cmd);
}
Because the program runs with root privileges, the call to system() also executes with root privileges. If a user specifies a standard filename, the call works as expected. However, if an attacker passes a string of the form ";rm -rf /", then the call to system() fails to execute cat due to a lack of arguments and then plows on to recursively delete the contents of the root partition.
Example 2: The following code from a privileged program uses the environment variable $APPHOME to determine the application's installation directory and then executes an initialization script in that directory.
...
char* home=getenv("APPHOME");
char* cmd=(char*)malloc(strlen(home)+strlen(INITCMD));
if (cmd) {
strcpy(cmd,home);
strcat(cmd,INITCMD);
execl(cmd, NULL);
}
...
As in Example 1, the code in this example allows an attacker to execute arbitrary commands with the elevated privilege of the application. In this example, the attacker may modify the environment variable $APPHOME to specify a different path containing a malicious version of INITCMD. Because the program does not validate the value read from the environment, by controlling the environment variable the attacker may fool the application into running malicious code.
The attacker is using the environment variable to control the command that the program invokes, so the effect of the environment is explicit in this example. We will now turn our attention to what can happen when the attacker may change the way the command is interpreted.
Example 3: The following code is from a web-based CGI utility that allows users to change their passwords. The password update process under NIS includes running make in the /var/yp directory. Note that since the program updates password records, it has been installed setuid root.
The program invokes make as follows:
system("cd /var/yp && make &> /dev/null");
Unlike the previous examples, the command in this example is hardcoded, so an attacker cannot control the argument passed to system(). However, since the program does not specify an absolute path for make and does not scrub any environment variables prior to invoking the command, the attacker may modify their $PATH variable to point to a malicious binary named make and execute the CGI script from a shell prompt. And since the program has been installed setuid root, the attacker's version of make now runs with root privileges.
On Windows, additional risks are present.
Example 4: When invoking CreateProcess() either directly or via a call to one of the functions in the _spawn() family, care must be taken when there is a space in an executable or path.
...
LPTSTR cmdLine = _tcsdup(TEXT("C:\\Program Files\\MyApplication -L -S"));
CreateProcess(NULL, cmdLine, ...);
...
Because of the way CreateProcess() parses spaces, the first executable the operating system will try to execute is Program.exe, not MyApplication.exe. Therefore, if an attacker is able to install a malicious application called Program.exe on the system, any program that incorrectly calls CreateProcess() using the Program Files directory will run this application instead of the intended one.
The environment plays a powerful role in the execution of system commands within programs. Functions like system(), exec(), and CreateProcess() use the environment of the program that calls them, and therefore attackers have a potential opportunity to influence the behavior of these calls.
Recommendations:
Do not allow users to have direct control over the commands executed by the program. If user input affects the command to be run, use the input only to select from a predetermined set of safe commands. If the input appears to be malicious, the value passed to the command execution function should either default to some safe selection from this set or the program should decline to execute any command.
If user input must be used as an argument to a command executed by the program, this solution can become impractical; the set of legitimate argument values may be too large or too hard to keep track of. In this situation, programmers often fallback on implementing a deny list to selectively reject or escape potentially dangerous characters before using the input. However, any such list of unsafe characters is likely to be incomplete and will be heavily dependent on the system where the commands are executed. A better approach is to create a list of characters that are permitted to appear in the input and accept input composed exclusively of characters in the approved set.
Another line of defense against maliciously crafted input is to avoid the use of functions that perform shell interpretation. For example, do not use system(), which executes its own command shell.
Be aware of the external environment and how it affects the behavior of the commands you execute. In particular, pay attention to how the $PATH, $LD_LIBRARY_PATH, and $IFS variables are used on Unix and Linux machines.
Be aware that the Windows APIs impose a specific search order that is based not only on a series of directories, but also on a list of file extensions that are automatically appended if none is specified. For example, functions in the _spawn() family, try executing file name extensions in the following order, if the command name argument does not have a file name extension or does not end with a period: first .com, then .exe, then .bat, and finally .cmd. Furthermore, additional risks exist on Windows due to the way command executing functions parse spaces in arguments that represent executables and paths.
Example 5: The following code re-writes Example 4 to avoid unintentionally executing a malicious application by using quotation marks around the executable path.
...
LPTSTR cmdLine[] = _tcsdup(TEXT("\"C:\\Program Files\\MyApplication\" -L -S"));
CreateProcess(NULL, cmdLine, ...);
...
Another way to achieve the same result is to pass the name of the executable as the first argument, instead of passing NULL.
Although it may be impossible to completely protect a program from an imaginative attacker bent on controlling the commands the program executes, be sure to apply the principle of least privilege wherever the program executes an external command: do not hold privileges that are not essential to the execution of the command.
0 comments:
Post a Comment