PHP Command Line Injection


Example

Problem

In a similar way that SQL injection allows an attacker to execute arbitrary queries on a database, command-line injection allows someone to run untrusted system commands on a web server. With an improperly secured server this would give an attacker complete control over a system.

Let's say, for example, a script allows a user to list directory contents on a web server.

<pre>
<?php system('ls ' . $_GET['path']); ?>
</pre>

(In a real-world application one would use PHP's built-in functions or objects to get path contents. This example is for a simple security demonstration.)

One would hope to get a path parameter similar to /tmp. But as any input is allowed, path could be ; rm -fr /. The web server would then execute the command

ls; rm -fr /

and attempt to delete all files from the root of the server.

Solution

All command arguments must be escaped using escapeshellarg() or escapeshellcmd(). This makes the arguments non-executable. For each parameter, the input value should also be validated.

In the simplest case, we can secure our example with

<pre>
<?php system('ls ' . escapeshellarg($_GET['path'])); ?>
</pre>

Following the previous example with the attempt to remove files, the executed command becomes

ls '; rm -fr /'

And the string is simply passed as a parameter to ls, rather than terminating the ls command and running rm.

It should be noted that the example above is now secure from command injection, but not from directory traversal. To fix this, it should be checked that the normalized path starts with the desired sub-directory.

PHP offers a variety of functions to execute system commands, including exec, passthru, proc_open, shell_exec, and system. All must have their inputs carefully validated and escaped.