Command Injection - pallavitewari21/Secure-Code GitHub Wiki

Command injection is an attack in which the goal is execution of arbitrary commands on the host operating system via a vulnerable application. Command injection attacks are possible when an application passes unsafe user supplied data (forms, cookies, HTTP headers etc.) to a system shell. In this attack, the attacker-supplied operating system commands are usually executed with the privileges of the vulnerable application. Command injection attacks are possible largely due to insufficient input validation. It will typically include using append characters like ; and &. The idea of this appending is so that when you are in a shell, like bash shell, and you want to cd to a location, then list the directory, then create a new empty file called chuckie.txt, you can do it all in one line by using a semicolon between each command. Example: cd /var/www/html; ls; touch chuckie.txt. this will accomplish what we just mentioned all in one command. So if the site uses a shell to process what the user input and does not validate what the user is passing, you could allow an attacker to run dangerous shell commands

Case Study 1:

Consider a shopping application that lets the user view whether an item is in stock in a particular store. This information is accessed via a URL like:

https://insecure-website.com/stockStatus?productID=381&storeID=29

To provide the stock information, the application must query various legacy systems. For historical reasons, the functionality is implemented by calling out to a shell command with the product and store IDs as arguments:

          stockreport.pl 381 29

This command outputs the stock status for the specified item, which is returned to the user. Since the application implements no defenses against OS command injection, an attacker can submit the following input to execute an arbitrary command:

          & echo aiwefwlguh &

If this input is submitted in the productID parameter, then the command executed by the application is:

          stockreport.pl & echo aiwefwlguh & 29

The echo command simply causes the supplied string to be echoed in the output, and is a useful way to test for some types of OS command injection. The & character is a shell command separator, and so what gets executed is actually three separate commands one after another. As a result, the output returned to the user is:

          Error - productID was not provided
          aiwefwlguh
          29: command not found

The three lines of output demonstrate that:

  1. The original stockreport.pl command was executed without its expected arguments, and so returned an error message.
  2. The injected echo command was executed, and the supplied string was echoed in the output.
  3. The original argument 29 was executed as a command, which caused an error.

Placing the additional command separator & after the injected command is generally useful because it separates the injected command from whatever follows the injection point. This reduces the likelihood that what follows will prevent the injected command from executing.

Java specific API’s that may introduce this vulnerability:

          Runtime.exec()
          getParameter
          getRuntime.exec()
          ProcessBuilder.start()
          setAttribute putValue getValue
          java.net.Socket java.io.fileInputStream java.io.FileReader 

Case Study 2:

The developer of the example PHP application wants the user to be able to see the output of the Windows ping command in the web application. The user needs to input the IP address and the application sends ICMP pings to that address. Unfortunately, the developer trusts the user too much and does not perform input validation. The IP address is passed using the GET method and then used in the command line.

          <?php
          $address = $_GET["address"];
          $output = shell_exec("ping -n 3 $address");
          echo "<pre>$output</pre>";
          ?>

The attacker abuses this script by manipulating the GET request with the following payload.

http://example.com/ping.php?address=8.8.8.8%26dir

The shell_exec function executes the following OS command: ping -n 3 8.8.8.8&dir. The & symbol in Windows separates OS commands. As a result, the vulnerable application executes an additional command (dir) and displays the command output (directory listing) on-screen:

          Pinging 8.8.8.8 with 32 bytes of data:

          Reply from 8.8.8.8: bytes=32 time=30ms TTL=56

          Reply from 8.8.8.8: bytes=32 time=35ms TTL=56

          Reply from 8.8.8.8: bytes=32 time=35ms TTL=56

          Ping statistics for 8.8.8.8:

            Packets: Sent = 3, Received = 3, Lost = 0 (0% loss),

          Approximate round trip times in milli-seconds:

            Minimum = 30ms, Maximum = 35ms, Average = 33ms

          Volume in drive C is OS

          Volume Serial Number is 1337-8055

          Directory of C:\Users\Noob\www

          (...)

You can use different special characters to inject an arbitrary command. The simplest and most common one for Linux is the semicolon (;) and for Windows, the ampersand (&). However, the following payloads for the ping.php script will also work:

        address=8.8.8.8%3Bwhoami (; character, Linux only)
        address=8.8.8.8&26whoami (& character, Windows only)
        address=8.8.8.8%7Cwhoami (| character)
        address=invalid%7C%7Cwhoami (|| characters, the second command is executed only if the first command fails)
        address=8.8.8.8&26&26whoami (&& characters)
        %3E(whoami) (> character, Linux only)
        %60whoami%60 (` character, Linux only, the result will be reported by the ping command as an error)

Mitigation

Any web interface that is not properly sanitized is subject to this exploit. With the ability to execute OS commands, the user can upload malicious programs or even obtain passwords. OS command injection is preventable when security is emphasized during the design and development of applications.

Avoid calling OS commands directly

The primary defense is to avoid calling OS commands directly. Built-in library functions are a very good alternative to OS Commands, and they cannot be manipulated to perform tasks other than those it is intended to do.

For example use mkdir() instead of system("mkdir /dir_name"). If there are available libraries or APIs for the language you used, this is the preferred method.

Escape values added to OS commands specific to each OS

For examples, see escapeshellarg() or escapeshellcmd() in PHP.

Code Review

Check if any command execute methods are called and in unvalidated user input are taken as data for that command. Outside of that, appending a semicolon to the end of a URL query parameter followed by an operating system command, will execute the command. %3B is URL encoded and decodes to semicolon. This is because the ; is interpreted as a command separator.

Example: http://sensitive/something.php?dir=%3Bcat%20/etc/passwd

If the application responds with the output of the /etc/passwd file then you know the attack has been successful. Many web application scanners can be used to test for this attack as they inject variations of command injections and test the response.

Parameterization

If available, use structured mechanisms that automatically enforce the separation between data and command. These mechanisms can help to provide the relevant quoting, encoding.

Input Validation

The values for commands and the relevant arguments should be both validated. There are different degrees of validation for the actual command and its arguments:

  1. When it comes to the commands used, these must be validated against a whitelist of allowed commands.
  2. In regards to the arguments used for these commands, they should be validated using the following options: a. Positive or "whitelist" input validation - where are the arguments allowed explicitly defined b. White list Regular Expression - where is explicitly defined a whitelist of good characters allowed and the maximum length of the string. Ensure that meta characters like & | ; $ > < \ \ !` and white-spaces are not part of the Regular Expression. For example, the following regular expression only allows lowercase letters and numbers, and does not contain meta characters. The length is also being limited to 3-10 characters: ^[a-z0-9]{3,10}$

Additional Defenses

On top of primary defenses, parameterizations and input validation, we also recommend adopting all of these additional defenses in order to provide defense in depth.

  1. Applications should run using the lowest privileges that are required to accomplish the necessary tasks.
  2. If possible, create isolated accounts with limited privileges that are only used for a single task.

Java Code Examples

In Java, use ProcessBuilder and the command must be separated from its arguments.

There are many sites that will tell you that Java's Runtime.exec is exactly the same as C's system function. This is not true. Both allow you to invoke a new program/process. However, C's system function passes its arguments to the shell (/bin/sh) to be parsed, whereas Runtime.exec tries to split the string into an array of words, then executes the first word in the array with the rest of the words as parameters. Runtime.exec does NOT try to invoke the shell at any point and do not support shell metacharacters.

The key difference is that much of the functionality provided by the shell that could be used for mischief (chaining commands using &, &&, |, ||, etc, redirecting input and output) would simply end up as a parameter being passed to the first command, and likely causing a syntax error, or being thrown out as an invalid parameter.

Incorrect usage:

          ProcessBuilder b = new ProcessBuilder("C:\DoStuff.exe -arg1 -arg2");

In this example, the command together with the arguments are passed as a one string, making easy to manipulate that expression and inject malicious strings.

Correct Usage:

          ProcessBuilder pb = new ProcessBuilder("TrustedCmd", "TrustedArg1", "TrustedArg2");
          Map<String, String> env = pb.environment();
          pb.directory(new File("TrustedDir"));
          Process p = pb.start();

Here is an example that starts a process with a modified working directory. The command and each of the arguments are passed separately. This make it easy to validated each term and reduces the risk to insert malicious strings.

Code Review Tips

  1. Many developers believe text fields are the only areas for data validation. This is an incorrect assumption. Any external input must be data validated:

  2. Text fields, List boxes, radio buttons, check boxes, cookies, HTTP header data, HTTP post data, hidden fields, parameter names and parameter values. … This is not an exhaustive list.

  3. “Process to process” or “entity-to-entity” communication must be investigated also. Any code that communicates with an upstream or downstream process and accepts input from it must be reviewed.

  4. All injection flaws are input-validation errors. The presence of an injection flaw is an indication of incorrect data validation on the input received from an external source outside the boundary of trust, which gets more blurred every year.

  5. Basically for this type of vulnerability we need to find all input streams into the application. This can be from a user’s browser, CLI or fat client but also from upstream processes that “feed” our application.

  6. An example would be to search the code base for the use of APIs or packages that are normally used for communication purposes.

  7. The java.io, java.sql, java.net, java.rmi, java.xml packages are all used for application communication. Searching for methods from those packages in the code base can yield results. A less “scientific” method is to search for common keywords such as “UserID”, “LoginID” or “Password”.

What we should be looking for are relationships between the application and the operating system; the application-utilising functions of the underlying operating system. In Java using the Runtime object, java.lang.Runtime does this. In .NET calls such as System.Diagnostics.Process.Start are used to call underlying OS functions. In PHP we may look for calls such as exec() or passthru().

Case Study:

We have a class that eventually gets input from the user via a HTTP request. This class is used to execute some native exe on the application server and return a result.

          public class DoStuff {
          public string executeCommand(String userName)
          {	try {
	String myUid = userName;
	Runtime rt = Runtime.getRuntime();
	rt.exec("cmd.exe /C doStuff.exe " +”-“ +myUid); // Call exe with userID
          }catch(Exception e)
	{
          e.printStackTrace();
	}
          	}
	}

The method executeCommand calls doStuff.exe (utilizing cmd.exe) via the java.lang.runtime static method getRuntime(). The parameter passed is not validated in any way in this class. We are assuming that the data has not been data validated prior to calling this method. Transactional analysis should have encountered any data validation prior to this point. Inputting “Joe69” would result in the following MS DOS command: doStuff.exe –Joe69

Lets say we input Joe69 & netstat –a we would get the following response: The exe doStuff would execute passing in the User Id Joe69, but then the DOS command netstat would be called. How this works is the passing of the parameter “&” into the application, which in turn is used as a command appender in MS DOS and hence the command after the & character is executed. This wouldn't be true, if the code above was written as (here we assume that doStuff.exe doesn't act as a command interpreter, such as cmd.exe or /bin/sh);

public class DoStuff {
public string executeCommand(String userName)
{	try {
		String myUid = userName;
		Runtime rt = Runtime.getRuntime();
		rt.exec("doStuff.exe " +”-“ +myUid); // Call exe with userID
	}catch(Exception e)
		{
e.printStackTrace();
		}
	}
}

Why? (from Java Docs).. More precisely, the given command string is broken into tokens using a StringTokenizer created by the call new StringTokenizer(command) with no further modification of the character categories. The tokens produced by the tokenizer are then placed in the new string array cmdarray, in the same order ...

The produced array contains the executable (the first item) to call and its arguments (the rest of the arguments). So, unless the first item to be called is an application which parses the arguments and interprets them, and further calls other external applications according to them, it wouldn't be possible to execute netstat in the above code snippet. Such a first item to be called would be cmd.exe in Windows boxes or sh in Unix-like boxes.

Most of the out-of-box source code/assembly analyzers would (and some wouldn't!) flag a Command Execution issue when they encounter the dangerous APIs; System.Diagnostics.Process.Start, java.lang.Runtime.exec. However, obviously, the calculated risk should differ. In the first example, the "command injection" is there, whereas, in the second one without any validation nor escaping what can be called as "argument injection" vulnerability exists. The risk is still there, but the severity depends on the command being called. So, the issue needs analysis.

Demonstration

Example:

  1. So here we see a dns lookup page. My demo is not connected online so it wont connect to a dns server, but the idea is whatever you type in, it will do an nslookup or dig type command in the servers shell to return a dns result which it prints to the page

  2. We know it is using a shell, so what if we did ;ls. In other words: nslookup www.nsa.gov; ls PIC

  3. ;pwd && cd ../../ && ls PIC

  4. Here we start by printing working directory (/app) then we change directories to two directories up from where we are at. Then we list that directory

  5. You can also utilize the reading of files using built in tools

  6. ;cat aim.php PIC

  7. Here we see we can read the file directly on the webpage

  8. So lets take a look at the conditionals in the source code PIC

  9. Here we see they are doing different command checks for the security level you're on PIC

  10. Here we see they are taking our POST input and assigning it to $target. Then it invokes a shell_exec call to use the commandline nslookup and appends the input to it. PIC

  11. And here we see at level medium security (commandi_check1) they are doing string replacing. So If I use & or ; it is supposed to replace it with empty string. Then in the check_2, we see it uses the php function escapeshellcmd. Following characters are preceded by a backslash: &#;`|*?~<>^()[]{}$, \x0A and \xFF. ' and " are escaped only if they are not paired. In Windows, all these characters plus % and ! are replaced by a space instead.

More command injection:

  1. So here we are presented with an input box asking us to put in an ip address. Putting in localhost and submitted shows us that it is pinging the localhost address, therefore we know its calling on a system shell PIC

  2. So what about other commands like ls and pwd?

  3. So submitting ;ls ;pwd in the box gives the pic above where it first lists the directory then tells us what directory we are in. The semicolon is a common character used to concatenate multiple commands together in a shell. You can also try & and | as well. Lets try something a little more malicious, lets try ;cat /etc/passwd

  4. As we see that displays with no issue. Looking at the source code, we see that it just takes whatever gets assigned to the target value and runs it through shell_exec, no real checks going on here

  5. So bumping it up a security level to medium, we can see they set a blacklist banning the use of && and ;. However as most blacklists go, there are other ways to get around it, such as the use of the pipe symbol |. So by putting in the box: | ls, we are able to get our injection again. This is why you should always use a whitelist over a blacklist.

  6. Looking at the source for high security setting we see they have increased the blacklist

  7. However, something of note, notice that there is a space after the pipe symbol. So if we go to the page and type |ls with no spaces, we get through with no problem.

⚠️ **GitHub.com Fallback** ⚠️