Command Injection Basics


Executing a Command Injection attack simply means running a system command on someones server through a web application. Executing the command is of course the easy part, the hard part is finding and exploiting the vulnerable crack in the system which could be anything from an insecure form field on a web page to an open port in the network interface.

This is in fact the first reason why you should always set up your server with multiple user accounts, so different processes that don’t need access to each others files and even if your application is vulnerable, no exploit can do serious harm. A web application especially should never have root permissions.

Finding a vulnerability

First you need to think of all the ways your server and the outside world can communicate with each other. Pen testers would probably start with mapping out all available IPs and ports to find out what services are running on a server. We however are going to concentrate on the simplest approach - we’re going to try to attack a server through a website, because a submitted form sends information to the server and probability is high that the server will respond through the website with something in return.

To find a vulnerable form all you need to do is try a bash command in all of the inputs (just like XSS). For example ls; should list all files in whatever directory the webapp is at (Windows servers have different commands, like dir &). Depending on what command is executed in the server you may need to adjust the testing command to ; ls;. Sometimes even when the input is vulnerable you wont know it, because even though the command executes the server doesn’t reveal the results. In this case you could try pinging your server (; ping -c 4 172.521.43.11;) and checking your logs to see if the ping reaches your server.

Example vulnerability

The most obvious form that may be vulnerable is one that clearly executes some system command on the server. Take for example this pinging service. Enter an IP and it’ll ping 4 times and give you the results.

vulnerable input

The format of the returned (red) text sure looks like something from a Linux terminal. If you’re familiar with Unix Bash you’ll recognize this as the ping command which works just by supplying the IP: ping 104.244.42.1. In fact, that’s exactly what the server does in this case.

Not all vulnerabilities are so obvious though. Usually the input doesn’t look strange at all.

update profile image

Here you can upload a file or add an url directing to a file to update your profile image. What you don’t see is that in the background a command called convert is used to crop your image from a rectangle to a circle. To find this kind of a vulnerability you can insert a command separated by semicolons:

update profile error

You can see that the pwd printed out the web applications current directory in the errors field. Why does this work? Because there’s a PHP line in the server that looks like this: exec("convert {$_GET['filename']} -crop 200x200 {$newname}");. And when we insert a; pwd; as the url then what gets executed in bash is convert a; pwd; -crop 200x200 {$newname} which stops the convert file and runs our own.

Real world importance

Being able to run commands as a certain user on a system means having almost full access to that account. So everything from your code to database credentials may be available for the hacker. Or if the cyber criminal so chooses he or she can silently replace content on your site that suits his or her preferences.

There are large scale attacks going around that exploit specific vulnerabilities. For example in 2016 an exploit was found on a router that used the NTP servers ability to run commands remotely to build a botnet. Millions of routers were vulnerable to this and there are many more attacks like this against routers and other things connected to the internet.

What I’m saying is Command Injection is still very much a thing and your web application could be scanned for vulnerabilities at any time so make sure to protect yourself.

OMG save my server!

There are many ways to protect against this.

Don’t run system commands

Well obviously this is the perfect protection, but not exactly plausible in every situation. Just make sure you really need to execute system commands before implementing them. A good practice instead is to use methods specifically designed for use-cases like this - pcntl_fork and pcntl_exec. Many developers do not like to use these as implementation is not always straight forward. Additionally instead of wget developers should use PHP Client URL library for fetching files from external resources.

Validate user input

If there is no way around executing system commands then you need to make sure the user has not written any malicious code into a form field. The easiest way is to just check for suspicious symbols like ; and #. This is called blacklisting. To be really sure however you’ll need to instead make sure what kind of strings are allowed and whitelist the symbols. For example if your command accepts a filename then you need to whitelist letters, numbers and the dot ..

Here’s a PHP example with Regex where a user can delete a file:

if (preg_match('/^[a-zA-Z0-9]+\.[a-z]{1,5}$/g', $filename)) {
	exec('rm {$filename}'); // is filename so execute command
} else {
	throw new Exception('Not a filename');
}

Whitelisting filenames and URLs might turn out neigh to impossible with a simple regular expression. File names and URLs can contain valid characters which the reg-ex will match against and block.

The next example is vulnerable, as it does no sanitation of user input. The user can insert any malicious code that he can come up with and PHP will execute it on the server.

if (!empty($_POST['uri'])){
	$newfile = 'uploads/users/'.end(explode('/', $_POST['uri']));
	exec("wget -O {$newfile} {$_POST['uri']}", $o, $r);
	$errors = array_merge($errors, $o);
}

A quick fix for this can be the usage of escapeshellarg() method, that will add single quotes around all the user input. This approach is acceptable but not always the best.

As an additional precaution the developer should be aware of is that sometimes he needs to be get additional parameters from the website dynamically. A good example here is ImageMagick, which does image manipulation. The next example is the path shown to the enduser after something unexpected was encountered. Additionally to the filename, the address is indicating acceptance of additonal arguments - x and y, which the user is able to change.

../?page=profile_image&id=6&filename=uploads%2Fusers%2F
image_1_520x347.JPG&x=0&y=0

In this case the code must sanitize also the arguments to allow just integer values to be passed and bail on anything else.

Code review

Regex can get complicated so make sure you have a teammate or coworker review your code and if possible have a tester try their tricks and hacks on it too.

Keep user permissions separate

As a precaution it helps for the administrator of the server to keep the user accounts separate when possible. Most importantly the web application should have no more permissions than absolutely necessary. Especially don’t give it root permissions - that’s just an accident waiting to happen.

Krister Viirsaar