#native_company# #native_desc#
#native_cta#

Mitigate the Security Risks of PHP System Command Execution Page 2

By W. Jason Gilmore
on January 28, 2010

Executing a System Command

PHP actually supports several functions capable of executing a system command, including exec(), passthru(), shellexec(), and system(). Each of these functions exhibits a slightly different behavior:
  • exec(): This function executes an external command, optionally saving the output to an array.
  • passthru(): This function executes an external command, displaying the raw output directly to the browser.
  • shellexec(): This function executes an external command, saving the output to a string.
  • system(): This function executes an external command, flushing the web server’s output buffer after each line of output is returned.
Let’s review a few examples involving these functions, beginning with the particularly simple task of executing the ls command in order to output a list of files residing in a particular directory:

<?php
  exec("ls -al /home/wjgilmore/pdfs", $lines);
  foreach($lines as $line) {
    echo "{$line}<br />";
  }
?>

Executing this script produces output identical to what you would see if executing ls -al /home/wjgilmore/pdfs from the command-line:

total 20
drwxr-xr-x 2 wjgilmore wjgilmore 4096 Jan 25 13:01 .
drwxr-xr-x 4 wjgilmore wjgilmore 4096 Jan 25 13:02 ..
-rw-r--r-- 1 wjgilmore wjgilmore 1366 Jan 25 13:00 01-2010.pdf
-rw-r--r-- 1 wjgilmore wjgilmore 1263 Jan 25 13:01 02-2010.pdf
-rw-r--r-- 1 wjgilmore wjgilmore 685 Jan 25 13:01 03-2010.pdf

Of course, there’s nothing particularly insecure about this example because the system command has been hardcoded as an exec() parameter. However, let’s return to the earlier scenario where a user was allowed to key in a Social Security Number, which was subsequently provided to a Ruby script. The user would presumably provide this input via a web form, with the SSN passed via the $_POST array. Coded in an insecure fashion, the script might look like this:

<?php

  $ssn = $_POST['ssn'];

  exec("/home/mainframe/fees.rb {$ssn}", $lines);

  foreach($lines AS $line) {
  
    echo "{$line}<br />";
  
  }

?>

To demonstrate the danger of this script, consider the following fees.rb script, which just outputs the SSN back to the user:

#!/usr/bin/ruby

ssn = ARGV[0]

puts "The SSN is #{ssn}n"

Now, suppose the user entered the following “social security number” into the form:

123-45-6789 ; cat /etc/passwd

Providing this input to the PHP script results in the following output:

The SSN is 123-45-6789
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/bin/sh
bin:x:2:2:bin:/bin:/bin/sh
sys:x:3:3:sys:/dev:/bin/sh
sync:x:4:65534:sync:/bin:/bin/sync
...

Surely this isn’t the output you intended!

Securing Your Scripts

Fortunately, several options are at your disposal for preventing such unintended consequences. The easiest solution involves using the escapeshellarg() and escapeshellcmd() functions to convert the user-supplied data into a safe format. The escapeshellarg() function will delimit the data with single quotes, as well as any single quotes already found in the data, thereby causing the data to be treated as a single argument. Therefore, the above malicious input would be converted to this:

'123-45-6789 ; cat /etc/passwd'

Once delimited, the PHP script will now produce the following output:

The SSN is 123-45-6789 ; cat /etc/passwd

The escapeshellcmd() function will escape any characters that could be used to trigger a system command. If applied to the above user-supplied data, the string will be converted to the following before being passed to the Ruby script:

123-45-6789 ; cat /etc/passwd

With the semi-colon escaped, the ensuing command can no longer be interpreted by the operating system.

Where to From Here?

Creating web-based applications that integrate tightly with the underlying operating system is pretty easy to do. However, you must be vigilant to avoid the serious security issues that can arise due to unchecked user input. Fortunately, PHP’s native functionality makes it easy to vet user input in a way that greatly reduces the likelihood of stolen or damaged server data.
W. Jason Gilmore is founder of EasyPHPWebsites.com. He is the author
of several popular books, including “Easy PHP
web sites with the Zend Framework
,” “Easy PayPal with PHP,” and “Beginning
PHP and MySQL, Third Edition
.” to e-mail him.